Compare commits
2 Commits
pacing-fix
...
e9834a5e35
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9834a5e35 | ||
|
|
fb99c9e472 |
19
.github/workflows/codeql-analysis.yml
vendored
19
.github/workflows/codeql-analysis.yml
vendored
@@ -18,21 +18,30 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
# Install dependencies
|
||||
- run: sudo apt update && sudo apt install 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
|
||||
- run: sudo apt update && sudo apt install libxext-dev libxcb1-dev libxcb-dpms0-dev libxcb-damage0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl1-mesa-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson ninja-build
|
||||
if: ${{ matrix.language == 'cpp' }}
|
||||
|
||||
# Autobuild
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
2
.github/workflows/coding-style-pr.yml
vendored
2
.github/workflows/coding-style-pr.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
- run: git fetch --depth=1 origin ${{ github.event.pull_request.base.sha }}
|
||||
- uses: yshui/git-clang-format-lint@v1.14
|
||||
with:
|
||||
|
||||
2
.github/workflows/coding-style.yml
vendored
2
.github/workflows/coding-style.yml
vendored
@@ -6,7 +6,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
fetch-depth: 2
|
||||
- uses: yshui/git-clang-format-lint@v1.14
|
||||
|
||||
0
.gitmodules
vendored
Normal file
0
.gitmodules
vendored
Normal file
164
CONTRIBUTORS
164
CONTRIBUTORS
@@ -1,95 +1,85 @@
|
||||
Sorted in alphabetical order. Feel free to open an issue or create a
|
||||
pull request if you want to change or remove your mention.
|
||||
Sorted in alphabetical order
|
||||
Open an issue or pull request if you don't want your name listed here.
|
||||
|
||||
Adam Jackson <ajax at nwnk.net>
|
||||
adelin-b <berard.adelin at gmail.com>
|
||||
Alexander Kapshuna <kapsh at kap.sh>
|
||||
Antonin Décimo <antonin.decimo at gmail.com>
|
||||
Antonio Vivace <dev at avivace.com>
|
||||
Avi ד <avi.the.coder at gmail.com>
|
||||
Ben Friesen <bfriesen95 at gmail.com>
|
||||
Bernd Busse <bernd at busse-net.de>
|
||||
Bert Gijsbers <gijsbers at science.uva.nl>
|
||||
bhagwan <bhagwan at disroot.org>
|
||||
Bodhi <craig.langman at gmail.com>
|
||||
Brottweiler <tibell.christoffer at gmail.com>
|
||||
Carl Worth <cworth at cworth.org>
|
||||
Christopher Jeffrey <chjjeffrey at gmail.com>
|
||||
Corax26 <cor.ax26 at gmail.com>
|
||||
Dan Elkouby <streetwalkermc at gmail.com>
|
||||
Dana Jansens <danakj at orodu.net>
|
||||
Daniel Kwan <daniel.w.t.kwan at outlook.com>
|
||||
Dave Airlie <airlied at linux.ie>
|
||||
Adam Jackson <ajax@nwnk.net>
|
||||
adelin-b <berard.adelin@gmail.com>
|
||||
Alexander Kapshuna <kapsh@kap.sh>
|
||||
Antonin Décimo <antonin.decimo@gmail.com>
|
||||
Antonio Vivace <dev@avivace.com>
|
||||
Avi-D-coder <avi.the.coder@gmail.com>
|
||||
Ben Friesen <bfriesen95@gmail.com>
|
||||
Bernd Busse <bernd@busse-net.de>
|
||||
Bert Gijsbers <gijsbers@science.uva.nl>
|
||||
bhagwan <bhagwan@disroot.org>
|
||||
Bodhi <craig.langman@gmail.com>
|
||||
Brottweiler <tibell.christoffer@gmail.com>
|
||||
Carl Worth <cworth@cworth.org>
|
||||
Christopher Jeffrey <chjjeffrey@gmail.com>
|
||||
Corax26 <cor.ax26@gmail.com>
|
||||
Dan Elkouby <streetwalkermc@gmail.com>
|
||||
Dana Jansens <danakj@orodu.net>
|
||||
Daniel Kwan <daniel.w.t.kwan@outlook.com>
|
||||
Dave Airlie <airlied@linux.ie>
|
||||
David Schlachter
|
||||
dolio
|
||||
Duncan <duncan.britton at outlook.com>
|
||||
Dylan Araps <dylan.araps at gmail.com>
|
||||
Einar Lielmanis <einars at gmail.com>
|
||||
Eric Anholt <anholt at freebsd.org> <eric at anholt.net>
|
||||
Evgeniy Baskov <j-basevgser at yandex.ru>
|
||||
Duncan <duncan.britton@outlook.com>
|
||||
Dylan Araps <dylan.araps@gmail.com>
|
||||
Einar Lielmanis <einars@gmail.com>
|
||||
Eric Anholt <anholt@freebsd.org> <eric@anholt.net>
|
||||
Greg Flynn
|
||||
h7x4 <h7x4 at nani.wtf>
|
||||
Harish Rajagopal <harish.rajagopals at gmail.com>
|
||||
hasufell <julian.ospald at googlemail.com>
|
||||
i-c-u-p
|
||||
Ignacio Taranto <ignacio.taranto at eclypsium.com>
|
||||
Harish Rajagopal <harish.rajagopals@gmail.com>
|
||||
hasufell <julian.ospald@googlemail.com>
|
||||
Ignacio Taranto <ignacio.taranto@eclypsium.com>
|
||||
Istvan Petres
|
||||
Jake <jakeroggenbuck2 at gmail.com>
|
||||
James Cloos <cloos at jhcloos.com>
|
||||
Jamey Sharp <jamey at minilop.net>
|
||||
Jan Beich <jbeich at freebsd.org>
|
||||
Jarrad <jarrad.whitaker at gmail.com>
|
||||
Javeed Shaikh <syscrash2k at gmail.com>
|
||||
Jerónimo Navarro <jnavarro at ancasrl.com.ar>
|
||||
jialeens <jialeadmin at 163.com>
|
||||
Johnny Pribyl <pribylsnbits at gmail.com>
|
||||
Keith Packard <keithp at keithp.com>
|
||||
Kevin Kelley <kelleyk at kelleyk.net>
|
||||
ktprograms <ktprograms at gmail.com>
|
||||
Kurenshe Nurdaulet
|
||||
Lukas Schmelzeisen <l.schmelzeisen at gmx.de>
|
||||
Mark Tiefenbruck <mark at fluxbox.org>
|
||||
Matthew Allum <breakfast at 10.am>
|
||||
Maxim Solovyov <msolovyov at protonmail.com>
|
||||
Michael Reed <supertron421 at gmail.com>
|
||||
Michele Lambertucci <michele.lambertucci at mail.polimi.it>
|
||||
mæp <m.aep at live.com>
|
||||
Namkhai Bourquin <namkhai.n3 at protonmail.com>
|
||||
Nate Hart <nejthan at gmail.com>
|
||||
nia <nia at netbsd.org>
|
||||
Nikolay Borodin <monsterovich at gmail.com>
|
||||
notfoss <static.vortex at gmx.com>
|
||||
Omar Polo <op at omarpolo.com>
|
||||
oofsauce <alanpanayotov at gmail.com>
|
||||
orbea <orbea at riseup.net>
|
||||
Paradigm0001
|
||||
James Cloos <cloos@jhcloos.com>
|
||||
Jamey Sharp <jamey@minilop.net>
|
||||
Jan Beich <jbeich@FreeBSD.org>
|
||||
Jarrad <jarrad.whitaker@gmail.com>
|
||||
Javeed Shaikh <syscrash2k@gmail.com>
|
||||
Jerónimo Navarro <jnavarro@ancasrl.com.ar>
|
||||
jialeens <jialeadmin@163.com>
|
||||
Johnny Pribyl <pribylsnbits@gmail.com>
|
||||
Keith Packard <keithp@keithp.com>
|
||||
Kevin Kelley <kelleyk@kelleyk.net>
|
||||
ktprograms <ktprograms@gmail.com>
|
||||
Lukas Schmelzeisen <l.schmelzeisen@gmx.de>
|
||||
mæp <m.aep@live.com>
|
||||
Mark Tiefenbruck <mark@fluxbox.org>
|
||||
Matthew Allum <breakfast@10.am>
|
||||
Maxim Solovyov <visleaf@protonmail.com>
|
||||
Michael Reed <supertron421@gmail.com>
|
||||
Michele Lambertucci <michele.lambertucci@mail.polimi.it>
|
||||
Namkhai Bourquin <namkhai.n3@protonmail.com>
|
||||
Nate Hart <nejthan@gmail.com>
|
||||
nia <nia@netbsd.org>
|
||||
notfoss <static.vortex@gmx.com>
|
||||
Omar Polo <op@omarpolo.com>
|
||||
orbea <orbea@riseup.net>
|
||||
@Paradigm0001
|
||||
Patrick Collins
|
||||
Peter Mattern <matternp at arcor.de>
|
||||
Phil Blundell <pb at reciva.com>
|
||||
Que Quotion <quequotion at bugmenot.com>
|
||||
Rafael Kitover <rkitover at gmail.com>
|
||||
Richard Grenville <pyxlcy at gmail.com>
|
||||
Rytis Karpuska <rytis.karpuska at gmail.com>
|
||||
Samuel Hand <samuel.d.hand at gmail.com>
|
||||
Scott Leggett <scott at sl.id.au>
|
||||
scrouthtv <lennivh24 at gmail.com>
|
||||
Sebastien Waegeneire <sebastien at waegeneire.com>
|
||||
Stefan Radziuk <stefan.radziuk19 at imperial.ac.uk>
|
||||
Subhaditya Nath <sn03.general at gmail.com>
|
||||
Tasos Sahanidis <tasos at tasossah.com>
|
||||
Thiago Kenji Okada <thiagokokada at gmail.com>
|
||||
Tilman Sauerbeck <tilman at code-monkey.de>
|
||||
Tim Siegel <siegeltr at gmail.com>
|
||||
Tim van Dalen <tim at timvdalen.nl>
|
||||
tokyoneon78 <mockcoder at protonmail.ch>
|
||||
Tom Dörr <tomdoerr96 at gmail.com>
|
||||
Tomas Janousek <tomi at nomi.cz>
|
||||
Peter Mattern <matternp@arcor.de>
|
||||
Phil Blundell <pb@reciva.com>
|
||||
Que Quotion <quequotion@bugmenot.com> <the_q123@hotmail.com>
|
||||
Rafael Kitover <rkitover@gmail.com>
|
||||
Richard Grenville <pyxlcy@gmail.com>
|
||||
Rytis Karpuska <rytis.karpuska@gmail.com>
|
||||
Samuel Hand <samuel.d.hand@gmail.com>
|
||||
Scott Leggett <scott@sl.id.au>
|
||||
scrouthtv <lennivh24@gmail.com>
|
||||
Sebastien Waegeneire <sebastien@waegeneire.com>
|
||||
Subhaditya Nath <sn03.general@gmail.com>
|
||||
Tasos Sahanidis <tasos@tasossah.com>
|
||||
Thiago Kenji Okada <thiagokokada@gmail.com>
|
||||
Tilman Sauerbeck <tilman@code-monkey.de>
|
||||
Tim van Dalen <Tim@timvdalen.nl>
|
||||
Tomas Janousek <tomi@nomi.cz>
|
||||
Tom Dörr <tomdoerr96@gmail.com>
|
||||
Toni Jarjour
|
||||
Tuomas Kinnunen <tuomas.kinnunen at aalto.fi>
|
||||
Uli Schlachter <psychon at znc.in>
|
||||
Walter Lapchynski <wxl at ubuntu.com>
|
||||
Will Dietz <w at wdtz.org>
|
||||
XeCycle <xecycle at gmail.com>
|
||||
Yuxuan Shui <yshuiv7 at gmail.com>
|
||||
Tuomas Kinnunen <tuomas.kinnunen@aalto.fi>
|
||||
Uli Schlachter <psychon@znc.in>
|
||||
Walter Lapchynski <wxl@ubuntu.com>
|
||||
Will Dietz <w@wdtz.org>
|
||||
XeCycle <XeCycle@Gmail.com>
|
||||
Yuxuan Shui <yshuiv7@gmail.com>
|
||||
zilrich
|
||||
ಠ_ಠ <easteregg at verfriemelt.org>
|
||||
ಠ_ಠ <easteregg@verfriemelt.org>
|
||||
|
||||
16
README.md
16
README.md
@@ -1,15 +1,11 @@
|
||||
picom
|
||||
=====
|
||||
|
||||
[](https://circleci.com/gh/yshui/picom)
|
||||
[](https://codecov.io/gh/yshui/picom)
|
||||
[](https://discord.gg/SY5JJzPgME)
|
||||
|
||||
__picom__ is a compositor for X, and a [fork of Compton](History.md).
|
||||
|
||||
**This is a development branch, bugs to be expected**
|
||||
|
||||
You can leave your feedback or thoughts in the [discussion tab](https://github.com/yshui/picom/discussions), or chat with other users on [discord](https://discord.gg/SY5JJzPgME)!
|
||||
You can leave your feedback or thoughts in the [discussion tab](https://github.com/yshui/picom/discussions).
|
||||
|
||||
## Change Log
|
||||
|
||||
@@ -26,7 +22,6 @@ Assuming you already have all the usual building tools installed (e.g. gcc, pyth
|
||||
* libXext
|
||||
* xproto
|
||||
* xcb
|
||||
* xcb-util
|
||||
* xcb-damage
|
||||
* xcb-dpms
|
||||
* xcb-xfixes
|
||||
@@ -49,7 +44,7 @@ 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
|
||||
|
||||
```
|
||||
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
|
||||
libxext-dev libxcb1-dev libxcb-damage0-dev libxcb-dpms0-dev libxcb-xfixes0-dev libxcb-shape0-dev libxcb-render-util0-dev libxcb-render0-dev libxcb-randr0-dev libxcb-composite0-dev libxcb-image0-dev libxcb-present-dev libxcb-glx0-dev libpixman-1-dev libdbus-1-dev libconfig-dev libgl-dev libegl-dev libpcre2-dev libevdev-dev uthash-dev libev-dev libx11-xcb-dev meson
|
||||
```
|
||||
|
||||
On Fedora, the needed packages are
|
||||
@@ -63,7 +58,8 @@ To build the documents, you need `asciidoc`
|
||||
### To build
|
||||
|
||||
```bash
|
||||
$ meson setup --buildtype=release build
|
||||
$ git submodule update --init --recursive
|
||||
$ meson setup --buildtype=release . build
|
||||
$ ninja -C build
|
||||
```
|
||||
|
||||
@@ -74,12 +70,12 @@ If you have libraries and/or headers installed at non-default location (e.g. und
|
||||
You can do that by setting the `CPPFLAGS` and `LDFLAGS` environment variables when running `meson`. Like this:
|
||||
|
||||
```bash
|
||||
$ LDFLAGS="-L/path/to/libraries" CPPFLAGS="-I/path/to/headers" meson setup --buildtype=release build
|
||||
$ LDFLAGS="-L/path/to/libraries" CPPFLAGS="-I/path/to/headers" meson setup --buildtype=release . build
|
||||
```
|
||||
|
||||
As an example, on FreeBSD, you might have to run meson with:
|
||||
```bash
|
||||
$ LDFLAGS="-L/usr/local/lib" CPPFLAGS="-I/usr/local/include" meson setup --buildtype=release build
|
||||
$ LDFLAGS="-L/usr/local/lib" CPPFLAGS="-I/usr/local/include" meson setup --buildtype=release . build
|
||||
$ ninja -C build
|
||||
```
|
||||
|
||||
|
||||
80
flake.lock
generated
80
flake.lock
generated
@@ -1,80 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1689068808,
|
||||
"narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-ignore-nix": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"ref": "master",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1691186842,
|
||||
"narHash": "sha256-wxBVCvZUwq+XS4N4t9NqsHV4E64cPVqQ2fdDISpjcw0=",
|
||||
"path": "/nix/store/d42v5grfq77vr10r336kks0qjp0wij8d-source",
|
||||
"rev": "18036c0be90f4e308ae3ebcab0e14aae0336fe42",
|
||||
"type": "path"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"git-ignore-nix": "git-ignore-nix",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
34
flake.nix
34
flake.nix
@@ -1,34 +0,0 @@
|
||||
{
|
||||
inputs = {
|
||||
flake-utils.url = github:numtide/flake-utils;
|
||||
git-ignore-nix = {
|
||||
url = github:hercules-ci/gitignore.nix/master;
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
};
|
||||
outputs = {
|
||||
self, flake-utils, nixpkgs, git-ignore-nix, ...
|
||||
}: flake-utils.lib.eachDefaultSystem (system: let
|
||||
overlay = self: super: {
|
||||
picom = super.picom.overrideAttrs (oldAttrs: rec {
|
||||
pname = "picom";
|
||||
buildInputs = [
|
||||
self.pcre2 self.xorg.xcbutil
|
||||
] ++ self.lib.remove self.xorg.libXinerama (
|
||||
self.lib.remove self.pcre oldAttrs.buildInputs
|
||||
);
|
||||
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 ++ [
|
||||
pkgs.clang-tools
|
||||
];
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -109,9 +109,6 @@ OPTIONS
|
||||
*--rounded-corners-exclude* 'CONDITION'::
|
||||
Exclude conditions for rounded corners.
|
||||
|
||||
*--no-frame-pacing*::
|
||||
Disable vsync-aware frame pacing. By default, the compositor tries to make sure it only renders once per vblank interval, and also the render happens as late as possible to minimize the latency from updates to the screen. However this can sometimes cause stuttering, or even lowered frame rate. This option can be used to disable frame pacing.
|
||||
|
||||
*--mark-wmwin-focused*::
|
||||
Try to detect WM windows (a non-override-redirect window with no child that has 'WM_STATE') and mark them as active.
|
||||
|
||||
|
||||
@@ -233,6 +233,9 @@ dithered-present = false;
|
||||
# Enable/disable VSync.
|
||||
# vsync = true
|
||||
|
||||
# Enable remote control via D-Bus. See the *D-BUS API* section below for more details.
|
||||
# dbus = false
|
||||
|
||||
# Try to detect WM windows (a non-override-redirect window with no
|
||||
# child that has 'WM_STATE') and mark them as active.
|
||||
#
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "cache.h"
|
||||
#include "meta.h"
|
||||
#include "cache.h"
|
||||
|
||||
// clang-format off
|
||||
// Splitted into 2 lists because of the limitation of our macros
|
||||
@@ -23,10 +23,8 @@
|
||||
WM_CLIENT_MACHINE, \
|
||||
_NET_ACTIVE_WINDOW, \
|
||||
_COMPTON_SHADOW, \
|
||||
_NET_WM_WINDOW_TYPE, \
|
||||
_XROOTPMAP_ID, \
|
||||
ESETROOT_PMAP_ID, \
|
||||
_XSETROOT_ID
|
||||
_NET_WM_DESKTOP, \
|
||||
_NET_CURRENT_DESKTOP
|
||||
|
||||
#define ATOM_LIST2 \
|
||||
_NET_WM_WINDOW_TYPE_DESKTOP, \
|
||||
@@ -46,6 +44,7 @@
|
||||
_NET_WM_STATE, \
|
||||
_NET_WM_STATE_FULLSCREEN, \
|
||||
_NET_WM_BYPASS_COMPOSITOR, \
|
||||
_NET_WM_WINDOW_TYPE, \
|
||||
UTF8_STRING, \
|
||||
C_STRING
|
||||
// clang-format on
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||
#include <inttypes.h>
|
||||
#include <xcb/sync.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
@@ -134,43 +133,35 @@ void handle_device_reset(session_t *ps) {
|
||||
}
|
||||
|
||||
/// paint all windows
|
||||
///
|
||||
/// Returns if any render command is issued. IOW if nothing on the screen has changed,
|
||||
/// this function will return false.
|
||||
bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
struct timespec now = get_time_timespec();
|
||||
auto paint_all_start_us =
|
||||
(uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
|
||||
void paint_all_new(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
if (ps->backend_data->ops->device_status &&
|
||||
ps->backend_data->ops->device_status(ps->backend_data) != DEVICE_STATUS_NORMAL) {
|
||||
handle_device_reset(ps);
|
||||
return false;
|
||||
return handle_device_reset(ps);
|
||||
}
|
||||
if (ps->o.xrender_sync_fence) {
|
||||
if (ps->xsync_exists && !x_fence_sync(&ps->c, ps->sync_fence)) {
|
||||
if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
|
||||
log_error("x_fence_sync failed, xrender-sync-fence will be "
|
||||
"disabled from now on.");
|
||||
xcb_sync_destroy_fence(ps->c.c, ps->sync_fence);
|
||||
xcb_sync_destroy_fence(ps->c, ps->sync_fence);
|
||||
ps->sync_fence = XCB_NONE;
|
||||
ps->o.xrender_sync_fence = false;
|
||||
ps->xsync_exists = false;
|
||||
}
|
||||
}
|
||||
|
||||
now = get_time_timespec();
|
||||
auto after_sync_fence_us =
|
||||
(uint64_t)now.tv_sec * 1000000UL + (uint64_t)now.tv_nsec / 1000;
|
||||
log_trace("Time spent on sync fence: %" PRIu64 " us",
|
||||
after_sync_fence_us - paint_all_start_us);
|
||||
// All painting will be limited to the damage, if _some_ of
|
||||
// the paints bleed out of the damage region, it will destroy
|
||||
// part of the image we want to reuse
|
||||
region_t reg_damage;
|
||||
reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage);
|
||||
if (!ignore_damage) {
|
||||
reg_damage = get_damage(ps, ps->o.monitor_repaint || !ps->o.use_damage);
|
||||
} else {
|
||||
pixman_region32_init(®_damage);
|
||||
pixman_region32_copy(®_damage, &ps->screen_reg);
|
||||
}
|
||||
|
||||
if (!pixman_region32_not_empty(®_damage)) {
|
||||
pixman_region32_fini(®_damage);
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef DEBUG_REPAINT
|
||||
@@ -210,7 +201,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
// TODO(yshui): maybe we don't need to resize reg_damage, only reg_paint?
|
||||
int resize_factor = 1;
|
||||
if (t) {
|
||||
resize_factor = t->stacking_rank + 1;
|
||||
resize_factor = t->stacking_rank;
|
||||
}
|
||||
resize_region_in_place(®_damage, blur_width * resize_factor,
|
||||
blur_height * resize_factor);
|
||||
@@ -242,20 +233,6 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
region_t reg_shadow_clip;
|
||||
pixman_region32_init(®_shadow_clip);
|
||||
|
||||
now = get_time_timespec();
|
||||
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);
|
||||
if (ps->next_render > 0) {
|
||||
log_verbose("Render schedule deviation: %ld us (%s) %" PRIu64 " %ld",
|
||||
labs((long)after_damage_us - (long)ps->next_render),
|
||||
after_damage_us < ps->next_render ? "early" : "late",
|
||||
after_damage_us, ps->next_render);
|
||||
ps->last_schedule_delay = 0;
|
||||
if (after_damage_us > ps->next_render) {
|
||||
ps->last_schedule_delay = after_damage_us - ps->next_render;
|
||||
}
|
||||
}
|
||||
|
||||
if (ps->backend_data->ops->prepare) {
|
||||
ps->backend_data->ops->prepare(ps->backend_data, ®_paint);
|
||||
}
|
||||
@@ -274,7 +251,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
// on top of that window. This is used to reduce the number of pixels painted.
|
||||
//
|
||||
// Whether this is beneficial is to be determined XXX
|
||||
for (struct managed_win *w = t; w; w = w->prev_trans) {
|
||||
for (auto w = t; w; w = w->prev_trans) {
|
||||
pixman_region32_subtract(®_visible, &ps->screen_reg, w->reg_ignore);
|
||||
assert(!(w->flags & WIN_FLAGS_IMAGE_ERROR));
|
||||
assert(!(w->flags & WIN_FLAGS_PIXMAP_STALE));
|
||||
@@ -404,7 +381,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
}
|
||||
|
||||
if (ps->o.crop_shadow_to_monitor && w->randr_monitor >= 0 &&
|
||||
w->randr_monitor < ps->monitors.count) {
|
||||
w->randr_monitor < ps->randr_nmonitors) {
|
||||
// There can be a window where number of monitors is
|
||||
// updated, but the monitor number attached to the window
|
||||
// have not.
|
||||
@@ -414,7 +391,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
// bounds.
|
||||
pixman_region32_intersect(
|
||||
®_shadow, ®_shadow,
|
||||
&ps->monitors.regions[w->randr_monitor]);
|
||||
&ps->randr_monitor_regs[w->randr_monitor]);
|
||||
}
|
||||
|
||||
if (ps->o.transparent_clipping) {
|
||||
@@ -520,7 +497,7 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
}
|
||||
|
||||
// Draw window on target
|
||||
bool is_animating = 0 <= w->animation_progress && w->animation_progress < 1.0;
|
||||
bool is_animating = 0 < w->animation_progress && w->animation_progress < 1.0;
|
||||
if (w->frame_opacity == 1 && !is_animating) {
|
||||
ps->backend_data->ops->compose(ps->backend_data, w->win_image,
|
||||
window_coord, NULL, dest_coord,
|
||||
@@ -598,7 +575,6 @@ bool paint_all_new(session_t *ps, struct managed_win *const t) {
|
||||
for (win *w = t; w; w = w->prev_trans)
|
||||
log_trace(" %#010lx", w->id);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
// vim: set noet sw=8 ts=8 :
|
||||
|
||||
@@ -23,7 +23,8 @@ struct backend_operations;
|
||||
|
||||
typedef struct backend_base {
|
||||
struct backend_operations *ops;
|
||||
struct x_connection *c;
|
||||
xcb_connection_t *c;
|
||||
xcb_window_t root;
|
||||
struct ev_loop *loop;
|
||||
|
||||
/// Whether the backend can accept new render request at the moment
|
||||
@@ -119,7 +120,11 @@ struct backend_operations {
|
||||
// =========== Initialization ===========
|
||||
|
||||
/// Initialize the backend, prepare for rendering to the target window.
|
||||
backend_t *(*init)(session_t *, xcb_window_t)attr_nonnull(1);
|
||||
/// Here is how you should choose target window:
|
||||
/// 1) if ps->overlay is not XCB_NONE, use that
|
||||
/// 2) use ps->root otherwise
|
||||
// TODO(yshui) make the target window a parameter
|
||||
backend_t *(*init)(session_t *)attr_nonnull(1);
|
||||
void (*deinit)(backend_t *backend_data) attr_nonnull(1);
|
||||
|
||||
/// Called when rendering will be stopped for an unknown amount of
|
||||
@@ -216,18 +221,18 @@ struct backend_operations {
|
||||
struct xvisual_info fmt, bool owned);
|
||||
|
||||
/// Create a shadow context for rendering shadows with radius `radius`.
|
||||
/// Default implementation: default_create_shadow_context
|
||||
/// Default implementation: default_backend_create_shadow_context
|
||||
struct backend_shadow_context *(*create_shadow_context)(backend_t *backend_data,
|
||||
double radius);
|
||||
/// Destroy a shadow context
|
||||
/// Default implementation: default_destroy_shadow_context
|
||||
/// Default implementation: default_backend_destroy_shadow_context
|
||||
void (*destroy_shadow_context)(backend_t *backend_data,
|
||||
struct backend_shadow_context *ctx);
|
||||
|
||||
/// 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
|
||||
/// shadow context is created.
|
||||
/// Default implementation: default_render_shadow
|
||||
/// Default implementation: default_backend_render_shadow
|
||||
///
|
||||
/// Required.
|
||||
void *(*render_shadow)(backend_t *backend_data, int width, int height,
|
||||
@@ -292,14 +297,6 @@ struct backend_operations {
|
||||
/// Optional
|
||||
int (*buffer_age)(backend_t *backend_data);
|
||||
|
||||
/// 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
|
||||
/// present() calls. i.e. after a present() call, last_render_time() should start
|
||||
/// reporting the time of the just presen1ted frame.
|
||||
///
|
||||
/// Optional, if not available, the most conservative estimation will be used.
|
||||
bool (*last_render_time)(backend_t *backend_data, struct timespec *ts);
|
||||
|
||||
/// The maximum number buffer_age might return.
|
||||
int max_buffer_age;
|
||||
|
||||
@@ -371,8 +368,5 @@ struct backend_operations {
|
||||
|
||||
extern struct backend_operations *backend_list[];
|
||||
|
||||
/// paint all windows
|
||||
///
|
||||
/// Returns if any render command is issued. IOW if nothing on the screen has changed,
|
||||
/// this function will return false.
|
||||
bool paint_all_new(session_t *ps, struct managed_win *t) attr_nonnull(1);
|
||||
void paint_all_new(session_t *ps, struct managed_win *const t, bool ignore_damage)
|
||||
attr_nonnull(1);
|
||||
|
||||
@@ -19,18 +19,17 @@
|
||||
/**
|
||||
* Generate a 1x1 <code>Picture</code> of a particular color.
|
||||
*/
|
||||
xcb_render_picture_t
|
||||
solid_picture(struct x_connection *c, bool argb, double a, double r, double g, double b) {
|
||||
xcb_render_picture_t solid_picture(xcb_connection_t *c, xcb_drawable_t d, bool argb,
|
||||
double a, double r, double g, double b) {
|
||||
xcb_pixmap_t pixmap;
|
||||
xcb_render_picture_t picture;
|
||||
xcb_render_create_picture_value_list_t pa;
|
||||
xcb_render_color_t col;
|
||||
xcb_rectangle_t rect;
|
||||
|
||||
pixmap = x_create_pixmap(c, argb ? 32 : 8, 1, 1);
|
||||
if (!pixmap) {
|
||||
pixmap = x_create_pixmap(c, argb ? 32 : 8, d, 1, 1);
|
||||
if (!pixmap)
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
pa.repeat = 1;
|
||||
picture = x_create_picture_with_standard_and_pixmap(
|
||||
@@ -38,7 +37,7 @@ solid_picture(struct x_connection *c, bool argb, double a, double r, double g, d
|
||||
XCB_RENDER_CP_REPEAT, &pa);
|
||||
|
||||
if (!picture) {
|
||||
xcb_free_pixmap(c->c, pixmap);
|
||||
xcb_free_pixmap(c, pixmap);
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
@@ -52,14 +51,14 @@ solid_picture(struct x_connection *c, bool argb, double a, double r, double g, d
|
||||
rect.width = 1;
|
||||
rect.height = 1;
|
||||
|
||||
xcb_render_fill_rectangles(c->c, XCB_RENDER_PICT_OP_SRC, picture, col, 1, &rect);
|
||||
xcb_free_pixmap(c->c, pixmap);
|
||||
xcb_render_fill_rectangles(c, XCB_RENDER_PICT_OP_SRC, picture, col, 1, &rect);
|
||||
xcb_free_pixmap(c, pixmap);
|
||||
|
||||
return picture;
|
||||
}
|
||||
|
||||
xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opacity,
|
||||
int width, int height) {
|
||||
xcb_image_t *
|
||||
make_shadow(xcb_connection_t *c, const conv *kernel, double opacity, int width, int height) {
|
||||
/*
|
||||
* We classify shadows into 4 kinds of regions
|
||||
* r = shadow radius
|
||||
@@ -85,9 +84,8 @@ xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opac
|
||||
assert(d % 2 == 1);
|
||||
assert(d > 0);
|
||||
|
||||
ximage =
|
||||
xcb_image_create_native(c->c, to_u16_checked(swidth), to_u16_checked(sheight),
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL);
|
||||
ximage = xcb_image_create_native(c, to_u16_checked(swidth), to_u16_checked(sheight),
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP, 8, 0, 0, NULL);
|
||||
if (!ximage) {
|
||||
log_error("failed to create an X image");
|
||||
return 0;
|
||||
@@ -195,7 +193,7 @@ xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opac
|
||||
/**
|
||||
* Generate shadow <code>Picture</code> for a window.
|
||||
*/
|
||||
bool build_shadow(struct x_connection *c, double opacity, const int width,
|
||||
bool build_shadow(xcb_connection_t *c, xcb_drawable_t d, double opacity, const int width,
|
||||
const int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
|
||||
xcb_pixmap_t *pixmap, xcb_render_picture_t *pict) {
|
||||
xcb_image_t *shadow_image = NULL;
|
||||
@@ -209,9 +207,9 @@ bool build_shadow(struct x_connection *c, double opacity, const int width,
|
||||
return false;
|
||||
}
|
||||
|
||||
shadow_pixmap = x_create_pixmap(c, 8, shadow_image->width, shadow_image->height);
|
||||
shadow_pixmap = x_create_pixmap(c, 8, d, shadow_image->width, shadow_image->height);
|
||||
shadow_pixmap_argb =
|
||||
x_create_pixmap(c, 32, shadow_image->width, shadow_image->height);
|
||||
x_create_pixmap(c, 32, d, shadow_image->width, shadow_image->height);
|
||||
|
||||
if (!shadow_pixmap || !shadow_pixmap_argb) {
|
||||
log_error("Failed to create shadow pixmaps");
|
||||
@@ -227,11 +225,11 @@ bool build_shadow(struct x_connection *c, double opacity, const int width,
|
||||
}
|
||||
|
||||
gc = x_new_id(c);
|
||||
xcb_create_gc(c->c, gc, shadow_pixmap, 0, NULL);
|
||||
xcb_create_gc(c, gc, shadow_pixmap, 0, NULL);
|
||||
|
||||
// We need to make room for protocol metadata in the request. The metadata should
|
||||
// be 24 bytes plus padding, let's be generous and give it 1kb
|
||||
auto maximum_image_size = xcb_get_maximum_request_length(c->c) * 4 - 1024;
|
||||
auto maximum_image_size = xcb_get_maximum_request_length(c) * 4 - 1024;
|
||||
auto maximum_row =
|
||||
to_u16_checked(clamp(maximum_image_size / shadow_image->stride, 0, UINT16_MAX));
|
||||
if (maximum_row <= 0) {
|
||||
@@ -250,23 +248,23 @@ bool build_shadow(struct x_connection *c, double opacity, const int width,
|
||||
}
|
||||
|
||||
uint32_t offset = row * shadow_image->stride / sizeof(*shadow_image->data);
|
||||
xcb_put_image(c->c, (uint8_t)shadow_image->format, shadow_pixmap, gc,
|
||||
xcb_put_image(c, (uint8_t)shadow_image->format, shadow_pixmap, gc,
|
||||
shadow_image->width, batch_height, 0, to_i16_checked(row),
|
||||
0, shadow_image->depth, shadow_image->stride * batch_height,
|
||||
shadow_image->data + offset);
|
||||
}
|
||||
|
||||
xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, shadow_pixel, shadow_picture,
|
||||
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, shadow_pixel, shadow_picture,
|
||||
shadow_picture_argb, 0, 0, 0, 0, 0, 0, shadow_image->width,
|
||||
shadow_image->height);
|
||||
|
||||
*pixmap = shadow_pixmap_argb;
|
||||
*pict = shadow_picture_argb;
|
||||
|
||||
xcb_free_gc(c->c, gc);
|
||||
xcb_free_gc(c, gc);
|
||||
xcb_image_destroy(shadow_image);
|
||||
xcb_free_pixmap(c->c, shadow_pixmap);
|
||||
x_free_picture(c, shadow_picture);
|
||||
xcb_free_pixmap(c, shadow_pixmap);
|
||||
xcb_render_free_picture(c, shadow_picture);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -275,43 +273,43 @@ shadow_picture_err:
|
||||
xcb_image_destroy(shadow_image);
|
||||
}
|
||||
if (shadow_pixmap) {
|
||||
xcb_free_pixmap(c->c, shadow_pixmap);
|
||||
xcb_free_pixmap(c, shadow_pixmap);
|
||||
}
|
||||
if (shadow_pixmap_argb) {
|
||||
xcb_free_pixmap(c->c, shadow_pixmap_argb);
|
||||
xcb_free_pixmap(c, shadow_pixmap_argb);
|
||||
}
|
||||
if (shadow_picture) {
|
||||
x_free_picture(c, shadow_picture);
|
||||
xcb_render_free_picture(c, shadow_picture);
|
||||
}
|
||||
if (shadow_picture_argb) {
|
||||
x_free_picture(c, shadow_picture_argb);
|
||||
xcb_render_free_picture(c, shadow_picture_argb);
|
||||
}
|
||||
if (gc) {
|
||||
xcb_free_gc(c->c, gc);
|
||||
xcb_free_gc(c, gc);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void *default_render_shadow(backend_t *backend_data, int width, int height,
|
||||
struct backend_shadow_context *sctx, struct color color) {
|
||||
void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
struct backend_shadow_context *sctx, struct color color) {
|
||||
const conv *kernel = (void *)sctx;
|
||||
xcb_render_picture_t shadow_pixel =
|
||||
solid_picture(backend_data->c, true, 1, color.red, color.green, color.blue);
|
||||
xcb_render_picture_t shadow_pixel = solid_picture(
|
||||
backend_data->c, backend_data->root, true, 1, color.red, color.green, color.blue);
|
||||
xcb_pixmap_t shadow = XCB_NONE;
|
||||
xcb_render_picture_t pict = XCB_NONE;
|
||||
|
||||
if (!build_shadow(backend_data->c, color.alpha, width, height, kernel,
|
||||
shadow_pixel, &shadow, &pict)) {
|
||||
x_free_picture(backend_data->c, shadow_pixel);
|
||||
if (!build_shadow(backend_data->c, backend_data->root, color.alpha, width, height,
|
||||
kernel, shadow_pixel, &shadow, &pict)) {
|
||||
xcb_render_free_picture(backend_data->c, shadow_pixel);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto visual = x_get_visual_for_standard(backend_data->c, XCB_PICT_STANDARD_ARGB_32);
|
||||
void *ret = backend_data->ops->bind_pixmap(
|
||||
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, shadow_pixel);
|
||||
xcb_render_free_picture(backend_data->c, pict);
|
||||
xcb_render_free_picture(backend_data->c, shadow_pixel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -405,31 +403,31 @@ struct dual_kawase_params *generate_dual_kawase_params(void *args) {
|
||||
int min_radius; /// Approximate gauss-blur with at least this
|
||||
/// radius and std-deviation
|
||||
} strength_levels[20] = {
|
||||
{.iterations = 1, .offset = 1.25F, .min_radius = 1}, // LVL 1
|
||||
{.iterations = 1, .offset = 2.25F, .min_radius = 6}, // LVL 2
|
||||
{.iterations = 2, .offset = 2.00F, .min_radius = 11}, // LVL 3
|
||||
{.iterations = 2, .offset = 3.00F, .min_radius = 17}, // LVL 4
|
||||
{.iterations = 2, .offset = 4.25F, .min_radius = 24}, // LVL 5
|
||||
{.iterations = 3, .offset = 2.50F, .min_radius = 32}, // LVL 6
|
||||
{.iterations = 3, .offset = 3.25F, .min_radius = 40}, // LVL 7
|
||||
{.iterations = 3, .offset = 4.25F, .min_radius = 51}, // LVL 8
|
||||
{.iterations = 3, .offset = 5.50F, .min_radius = 67}, // LVL 9
|
||||
{.iterations = 4, .offset = 3.25F, .min_radius = 83}, // LVL 10
|
||||
{.iterations = 4, .offset = 4.00F, .min_radius = 101}, // LVL 11
|
||||
{.iterations = 4, .offset = 5.00F, .min_radius = 123}, // LVL 12
|
||||
{.iterations = 4, .offset = 6.00F, .min_radius = 148}, // LVL 13
|
||||
{.iterations = 4, .offset = 7.25F, .min_radius = 178}, // LVL 14
|
||||
{.iterations = 4, .offset = 8.25F, .min_radius = 208}, // LVL 15
|
||||
{.iterations = 5, .offset = 4.50F, .min_radius = 236}, // LVL 16
|
||||
{.iterations = 5, .offset = 5.25F, .min_radius = 269}, // LVL 17
|
||||
{.iterations = 5, .offset = 6.25F, .min_radius = 309}, // LVL 18
|
||||
{.iterations = 5, .offset = 7.25F, .min_radius = 357}, // LVL 19
|
||||
{.iterations = 5, .offset = 8.50F, .min_radius = 417}, // LVL 20
|
||||
{.iterations = 1, .offset = 1.25f, .min_radius = 1}, // LVL 1
|
||||
{.iterations = 1, .offset = 2.25f, .min_radius = 6}, // LVL 2
|
||||
{.iterations = 2, .offset = 2.00f, .min_radius = 11}, // LVL 3
|
||||
{.iterations = 2, .offset = 3.00f, .min_radius = 17}, // LVL 4
|
||||
{.iterations = 2, .offset = 4.25f, .min_radius = 24}, // LVL 5
|
||||
{.iterations = 3, .offset = 2.50f, .min_radius = 32}, // LVL 6
|
||||
{.iterations = 3, .offset = 3.25f, .min_radius = 40}, // LVL 7
|
||||
{.iterations = 3, .offset = 4.25f, .min_radius = 51}, // LVL 8
|
||||
{.iterations = 3, .offset = 5.50f, .min_radius = 67}, // LVL 9
|
||||
{.iterations = 4, .offset = 3.25f, .min_radius = 83}, // LVL 10
|
||||
{.iterations = 4, .offset = 4.00f, .min_radius = 101}, // LVL 11
|
||||
{.iterations = 4, .offset = 5.00f, .min_radius = 123}, // LVL 12
|
||||
{.iterations = 4, .offset = 6.00f, .min_radius = 148}, // LVL 13
|
||||
{.iterations = 4, .offset = 7.25f, .min_radius = 178}, // LVL 14
|
||||
{.iterations = 4, .offset = 8.25f, .min_radius = 208}, // LVL 15
|
||||
{.iterations = 5, .offset = 4.50f, .min_radius = 236}, // LVL 16
|
||||
{.iterations = 5, .offset = 5.25f, .min_radius = 269}, // LVL 17
|
||||
{.iterations = 5, .offset = 6.25f, .min_radius = 309}, // LVL 18
|
||||
{.iterations = 5, .offset = 7.25f, .min_radius = 357}, // LVL 19
|
||||
{.iterations = 5, .offset = 8.50f, .min_radius = 417}, // LVL 20
|
||||
};
|
||||
|
||||
auto params = ccalloc(1, struct dual_kawase_params);
|
||||
params->iterations = 0;
|
||||
params->offset = 1.0F;
|
||||
params->offset = 1.0f;
|
||||
|
||||
if (blur_args->strength <= 0 && blur_args->size) {
|
||||
// find highest level that approximates blur-strength with the selected
|
||||
@@ -453,7 +451,7 @@ struct dual_kawase_params *generate_dual_kawase_params(void *args) {
|
||||
// - Smallest texture dimensions are halved `iterations`-times
|
||||
// - Upsample needs pixels two-times `offset` away from the border
|
||||
// - Plus one for interpolation differences
|
||||
params->expand = (1 << params->iterations) * 2 * (int)ceilf(params->offset) + 1;
|
||||
params->expand = (1 << params->iterations) * 2 * (int)ceil(params->offset) + 1;
|
||||
|
||||
return params;
|
||||
}
|
||||
@@ -508,8 +506,9 @@ struct backend_image *default_new_backend_image(int w, int h) {
|
||||
}
|
||||
|
||||
void init_backend_base(struct backend_base *base, session_t *ps) {
|
||||
base->c = &ps->c;
|
||||
base->c = ps->c;
|
||||
base->loop = ps->loop;
|
||||
base->root = ps->root;
|
||||
base->busy = false;
|
||||
base->ops = NULL;
|
||||
}
|
||||
|
||||
@@ -44,18 +44,26 @@ struct backend_image {
|
||||
int border_width;
|
||||
};
|
||||
|
||||
bool build_shadow(struct x_connection *, double opacity, int width, int height,
|
||||
const conv *kernel, xcb_render_picture_t shadow_pixel,
|
||||
bool build_shadow(xcb_connection_t *, xcb_drawable_t, double opacity, int width,
|
||||
int height, const conv *kernel, xcb_render_picture_t shadow_pixel,
|
||||
xcb_pixmap_t *pixmap, xcb_render_picture_t *pict);
|
||||
|
||||
xcb_render_picture_t
|
||||
solid_picture(struct x_connection *, bool argb, double a, double r, double g, double b);
|
||||
xcb_render_picture_t solid_picture(xcb_connection_t *, xcb_drawable_t, bool argb,
|
||||
double a, double r, double g, double b);
|
||||
|
||||
xcb_image_t *make_shadow(struct x_connection *c, const conv *kernel, double opacity,
|
||||
int width, int height);
|
||||
xcb_image_t *
|
||||
make_shadow(xcb_connection_t *c, const conv *kernel, double opacity, int width, int height);
|
||||
|
||||
void *default_render_shadow(backend_t *backend_data, int width, int height,
|
||||
struct backend_shadow_context *sctx, struct color color);
|
||||
/// The default implementation of `is_win_transparent`, it simply looks at win::mode. So
|
||||
/// this is not suitable for backends that alter the content of windows
|
||||
bool default_is_win_transparent(void *, win *, void *);
|
||||
|
||||
/// The default implementation of `is_frame_transparent`, it uses win::frame_opacity. Same
|
||||
/// caveat as `default_is_win_transparent` applies.
|
||||
bool default_is_frame_transparent(void *, win *, void *);
|
||||
|
||||
void *default_backend_render_shadow(backend_t *backend_data, int width, int height,
|
||||
struct backend_shadow_context *sctx, struct color color);
|
||||
|
||||
/// Implement `render_shadow` with `shadow_from_mask`.
|
||||
void *
|
||||
|
||||
@@ -15,17 +15,12 @@
|
||||
/// Apply driver specified global workarounds. It's safe to call this multiple times.
|
||||
void apply_driver_workarounds(struct session *ps, enum driver driver) {
|
||||
if (driver & DRIVER_NVIDIA) {
|
||||
// setenv("__GL_YIELD", "usleep", true);
|
||||
setenv("__GL_MaxFramesAllowed", "1", true);
|
||||
ps->o.xrender_sync_fence = true;
|
||||
}
|
||||
}
|
||||
|
||||
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver) {
|
||||
if (driver & DRIVER_NVIDIA) {
|
||||
return VBLANK_SCHEDULER_SGI_VIDEO_SYNC;
|
||||
}
|
||||
return VBLANK_SCHEDULER_PRESENT;
|
||||
}
|
||||
|
||||
enum driver detect_driver(xcb_connection_t *c, backend_t *backend_data, xcb_window_t window) {
|
||||
enum driver ret = 0;
|
||||
// First we try doing backend agnostic detection using RANDR
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
#include <stdio.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "utils.h"
|
||||
|
||||
struct session;
|
||||
@@ -42,15 +41,13 @@ enum driver detect_driver(xcb_connection_t *, struct backend_base *, xcb_window_
|
||||
|
||||
/// Apply driver specified global workarounds. It's safe to call this multiple times.
|
||||
void apply_driver_workarounds(struct session *ps, enum driver);
|
||||
/// Choose a vblank scheduler based on the driver.
|
||||
enum vblank_scheduler_type choose_vblank_scheduler(enum driver driver);
|
||||
|
||||
// Print driver names to stdout, for diagnostics
|
||||
static inline void print_drivers(enum driver drivers) {
|
||||
const char *seen_drivers[ARR_SIZE(driver_names)];
|
||||
int driver_count = 0;
|
||||
for (size_t i = 0; i < ARR_SIZE(driver_names); i++) {
|
||||
if (drivers & (1UL << i)) {
|
||||
if (drivers & (1ul << i)) {
|
||||
seen_drivers[driver_count++] = driver_names[i];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ struct dummy_image {
|
||||
xcb_pixmap_t pixmap;
|
||||
bool transparent;
|
||||
int *refcount;
|
||||
bool owned;
|
||||
UT_hash_handle hh;
|
||||
};
|
||||
|
||||
@@ -28,10 +27,11 @@ struct dummy_data {
|
||||
struct backend_image mask;
|
||||
};
|
||||
|
||||
struct backend_base *dummy_init(session_t *ps attr_unused, xcb_window_t target attr_unused) {
|
||||
struct backend_base *dummy_init(struct session *ps attr_unused) {
|
||||
auto ret = (struct backend_base *)ccalloc(1, struct dummy_data);
|
||||
ret->c = &ps->c;
|
||||
ret->c = ps->c;
|
||||
ret->loop = ps->loop;
|
||||
ret->root = ps->root;
|
||||
ret->busy = false;
|
||||
return ret;
|
||||
}
|
||||
@@ -42,9 +42,6 @@ void dummy_deinit(struct backend_base *data) {
|
||||
log_warn("Backend image for pixmap %#010x is not freed", img->pixmap);
|
||||
HASH_DEL(dummy->images, img);
|
||||
free(img->refcount);
|
||||
if (img->owned) {
|
||||
xcb_free_pixmap(data->c->c, img->pixmap);
|
||||
}
|
||||
free(img);
|
||||
}
|
||||
free(dummy);
|
||||
@@ -85,7 +82,7 @@ bool dummy_blur(struct backend_base *backend_data attr_unused, double opacity at
|
||||
}
|
||||
|
||||
void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
|
||||
struct xvisual_info fmt, bool owned) {
|
||||
struct xvisual_info fmt, bool owned attr_unused) {
|
||||
auto dummy = (struct dummy_data *)base;
|
||||
struct dummy_image *img = NULL;
|
||||
HASH_FIND_INT(dummy->images, &pixmap, img);
|
||||
@@ -99,7 +96,6 @@ void *dummy_bind_pixmap(struct backend_base *base, xcb_pixmap_t pixmap,
|
||||
img->transparent = fmt.alpha_size != 0;
|
||||
img->refcount = ccalloc(1, int);
|
||||
*img->refcount = 1;
|
||||
img->owned = owned;
|
||||
|
||||
HASH_ADD_INT(dummy->images, pixmap, img);
|
||||
return (void *)img;
|
||||
@@ -116,9 +112,6 @@ void dummy_release_image(backend_t *base, void *image) {
|
||||
if (*img->refcount == 0) {
|
||||
HASH_DEL(dummy->images, img);
|
||||
free(img->refcount);
|
||||
if (img->owned) {
|
||||
xcb_free_pixmap(base->c->c, img->pixmap);
|
||||
}
|
||||
free(img);
|
||||
}
|
||||
}
|
||||
@@ -169,7 +162,7 @@ void dummy_destroy_blur_context(struct backend_base *base attr_unused, void *ctx
|
||||
}
|
||||
|
||||
void dummy_get_blur_size(void *ctx attr_unused, int *width, int *height) {
|
||||
// These numbers are arbitrary, to make sure the resize_region code path is
|
||||
// These numbers are arbitrary, to make sure the reisze_region code path is
|
||||
// covered.
|
||||
*width = 5;
|
||||
*height = 5;
|
||||
@@ -184,7 +177,7 @@ struct backend_operations dummy_ops = {
|
||||
.bind_pixmap = dummy_bind_pixmap,
|
||||
.create_shadow_context = default_create_shadow_context,
|
||||
.destroy_shadow_context = default_destroy_shadow_context,
|
||||
.render_shadow = default_render_shadow,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.make_mask = dummy_make_mask,
|
||||
.release_image = dummy_release_image,
|
||||
.is_image_transparent = dummy_is_image_transparent,
|
||||
|
||||
@@ -23,7 +23,7 @@ struct gl_blur_context {
|
||||
struct texture_size {
|
||||
int width;
|
||||
int height;
|
||||
} *texture_sizes;
|
||||
} * texture_sizes;
|
||||
|
||||
/// Cached dimensions of the offscreen framebuffer. It's the same size as the
|
||||
/// target but is expanded in either direction by resize_width / resize_height.
|
||||
@@ -150,9 +150,6 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
|
||||
|
||||
glUniform2f(down_pass->texorig_loc, (GLfloat)extent->x1, (GLfloat)dst_y_fb_coord);
|
||||
|
||||
glBindVertexArray(vao[1]);
|
||||
int nelems = vao_nelems[1];
|
||||
|
||||
for (int i = 0; i < iterations; ++i) {
|
||||
// Scale output width / height by half in each iteration
|
||||
scale_factor <<= 1;
|
||||
@@ -177,6 +174,8 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
|
||||
assert(bctx->blur_fbos[i]);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
glBindVertexArray(vao[1]);
|
||||
auto nelems = vao_nelems[1];
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i]);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
@@ -195,15 +194,6 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
|
||||
|
||||
glUniform2f(up_pass->texorig_loc, (GLfloat)extent->x1, (GLfloat)dst_y_fb_coord);
|
||||
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, default_mask);
|
||||
|
||||
glUniform1i(up_pass->uniform_mask_tex, 1);
|
||||
glUniform2f(up_pass->uniform_mask_offset, 0.0F, 0.0F);
|
||||
glUniform1i(up_pass->uniform_mask_inverted, 0);
|
||||
glUniform1f(up_pass->uniform_mask_corner_radius, 0.0F);
|
||||
glUniform1f(up_pass->uniform_opacity, 1.0F);
|
||||
|
||||
for (int i = iterations - 1; i >= 0; --i) {
|
||||
// Scale output width / height back by two in each iteration
|
||||
scale_factor >>= 1;
|
||||
@@ -216,15 +206,28 @@ bool gl_dual_kawase_blur(double opacity, struct gl_blur_context *bctx, const rec
|
||||
int tex_width = src_size.width;
|
||||
int tex_height = src_size.height;
|
||||
|
||||
// The number of indices in the selected vertex array
|
||||
GLsizei nelems;
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, src_texture);
|
||||
glActiveTexture(GL_TEXTURE1);
|
||||
glBindTexture(GL_TEXTURE_2D, default_mask);
|
||||
|
||||
glUniform1i(up_pass->uniform_mask_tex, 1);
|
||||
glUniform2f(up_pass->uniform_mask_offset, 0.0F, 0.0F);
|
||||
glUniform1i(up_pass->uniform_mask_inverted, 0);
|
||||
glUniform1f(up_pass->uniform_mask_corner_radius, 0.0F);
|
||||
if (i > 0) {
|
||||
assert(bctx->blur_fbos[i - 1]);
|
||||
|
||||
// not last pass, draw into next framebuffer
|
||||
glBindVertexArray(vao[1]);
|
||||
nelems = vao_nelems[1];
|
||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, bctx->blur_fbos[i - 1]);
|
||||
glDrawBuffer(GL_COLOR_ATTACHMENT0);
|
||||
|
||||
glUniform1f(up_pass->uniform_opacity, (GLfloat)1);
|
||||
} else {
|
||||
// last pass, draw directly into the back buffer
|
||||
if (mask) {
|
||||
@@ -344,9 +347,9 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coor
|
||||
glBindVertexArray(vao[0]);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
|
||||
indices, GL_STREAM_DRAW);
|
||||
indices, GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(vert_coord_loc);
|
||||
glEnableVertexAttribArray(vert_in_texcoord_loc);
|
||||
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
|
||||
@@ -357,10 +360,10 @@ bool gl_blur_impl(double opacity, struct gl_blur_context *bctx, void *mask, coor
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bo[2]);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[3]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord_resized) * nrects_resized * 16,
|
||||
coord_resized, GL_STREAM_DRAW);
|
||||
coord_resized, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
|
||||
(long)sizeof(*indices_resized) * nrects_resized * 6, indices_resized,
|
||||
GL_STREAM_DRAW);
|
||||
GL_STATIC_DRAW);
|
||||
glEnableVertexAttribArray(vert_coord_loc);
|
||||
glEnableVertexAttribArray(vert_in_texcoord_loc);
|
||||
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 4, NULL);
|
||||
|
||||
@@ -42,32 +42,8 @@ static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageProc = NULL;
|
||||
static PFNEGLGETPLATFORMDISPLAYPROC eglGetPlatformDisplayProc = NULL;
|
||||
static PFNEGLCREATEPLATFORMWINDOWSURFACEPROC eglCreatePlatformWindowSurfaceProc = NULL;
|
||||
|
||||
const char *eglGetErrorString(EGLint error) {
|
||||
#define CASE_STR(value) \
|
||||
case value: return #value;
|
||||
switch (error) {
|
||||
CASE_STR(EGL_SUCCESS)
|
||||
CASE_STR(EGL_NOT_INITIALIZED)
|
||||
CASE_STR(EGL_BAD_ACCESS)
|
||||
CASE_STR(EGL_BAD_ALLOC)
|
||||
CASE_STR(EGL_BAD_ATTRIBUTE)
|
||||
CASE_STR(EGL_BAD_CONTEXT)
|
||||
CASE_STR(EGL_BAD_CONFIG)
|
||||
CASE_STR(EGL_BAD_CURRENT_SURFACE)
|
||||
CASE_STR(EGL_BAD_DISPLAY)
|
||||
CASE_STR(EGL_BAD_SURFACE)
|
||||
CASE_STR(EGL_BAD_MATCH)
|
||||
CASE_STR(EGL_BAD_PARAMETER)
|
||||
CASE_STR(EGL_BAD_NATIVE_PIXMAP)
|
||||
CASE_STR(EGL_BAD_NATIVE_WINDOW)
|
||||
CASE_STR(EGL_CONTEXT_LOST)
|
||||
default: return "Unknown";
|
||||
}
|
||||
#undef CASE_STR
|
||||
}
|
||||
|
||||
/**
|
||||
* Free a gl_texture_t.
|
||||
* Free a glx_texture_t.
|
||||
*/
|
||||
static void egl_release_image(backend_t *base, struct gl_texture *tex) {
|
||||
struct egl_data *gd = (void *)base;
|
||||
@@ -79,7 +55,7 @@ static void egl_release_image(backend_t *base, struct gl_texture *tex) {
|
||||
}
|
||||
|
||||
if (p->owned) {
|
||||
xcb_free_pixmap(base->c->c, p->pixmap);
|
||||
xcb_free_pixmap(base->c, p->pixmap);
|
||||
p->pixmap = XCB_NONE;
|
||||
}
|
||||
|
||||
@@ -88,14 +64,14 @@ static void egl_release_image(backend_t *base, struct gl_texture *tex) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy EGL related resources.
|
||||
* Destroy GLX related resources.
|
||||
*/
|
||||
void egl_deinit(backend_t *base) {
|
||||
struct egl_data *gd = (void *)base;
|
||||
|
||||
gl_deinit(&gd->gl);
|
||||
|
||||
// Destroy EGL context
|
||||
// Destroy GLX context
|
||||
if (gd->ctx != EGL_NO_CONTEXT) {
|
||||
eglMakeCurrent(gd->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
|
||||
eglDestroyContext(gd->display, gd->ctx);
|
||||
@@ -130,7 +106,7 @@ static bool egl_set_swap_interval(int interval, EGLDisplay dpy) {
|
||||
/**
|
||||
* Initialize OpenGL.
|
||||
*/
|
||||
static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
||||
static backend_t *egl_init(session_t *ps) {
|
||||
bool success = false;
|
||||
struct egl_data *gd = NULL;
|
||||
|
||||
@@ -154,10 +130,10 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
||||
}
|
||||
|
||||
gd = ccalloc(1, struct egl_data);
|
||||
gd->display = eglGetPlatformDisplayProc(EGL_PLATFORM_X11_EXT, ps->c.dpy,
|
||||
gd->display = eglGetPlatformDisplayProc(EGL_PLATFORM_X11_EXT, ps->dpy,
|
||||
(EGLAttrib[]){
|
||||
EGL_PLATFORM_X11_SCREEN_EXT,
|
||||
ps->c.screen,
|
||||
ps->scr,
|
||||
EGL_NONE,
|
||||
});
|
||||
if (gd->display == EGL_NO_DISPLAY) {
|
||||
@@ -190,7 +166,7 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
auto visual_info = x_get_visual_info(&ps->c, ps->c.screen_info->root_visual);
|
||||
auto visual_info = x_get_visual_info(ps->c, ps->vis);
|
||||
EGLConfig config = NULL;
|
||||
int nconfigs = 1;
|
||||
// clang-format off
|
||||
@@ -211,8 +187,8 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
gd->target_win =
|
||||
eglCreatePlatformWindowSurfaceProc(gd->display, config, &target, NULL);
|
||||
gd->target_win = eglCreatePlatformWindowSurfaceProc(
|
||||
gd->display, config, (xcb_window_t[]){session_get_target_window(ps)}, NULL);
|
||||
if (gd->target_win == EGL_NO_SURFACE) {
|
||||
log_error("Failed to create EGL surface.");
|
||||
goto end;
|
||||
@@ -225,12 +201,12 @@ static backend_t *egl_init(session_t *ps, xcb_window_t target) {
|
||||
|
||||
gd->ctx = eglCreateContext(gd->display, config, NULL, NULL);
|
||||
if (gd->ctx == EGL_NO_CONTEXT) {
|
||||
log_error("Failed to get EGL context.");
|
||||
log_error("Failed to get GLX context.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (!eglMakeCurrent(gd->display, gd->target_win, gd->target_win, gd->ctx)) {
|
||||
log_error("Failed to attach EGL context.");
|
||||
log_error("Failed to attach GLX context.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
@@ -280,8 +256,7 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
struct egl_data *gd = (void *)base;
|
||||
struct egl_pixmap *eglpixmap = NULL;
|
||||
|
||||
auto r =
|
||||
xcb_get_geometry_reply(base->c->c, xcb_get_geometry(base->c->c, pixmap), NULL);
|
||||
auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), NULL);
|
||||
if (!r) {
|
||||
log_error("Invalid pixmap %#010x", pixmap);
|
||||
return NULL;
|
||||
@@ -308,8 +283,7 @@ egl_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
eglpixmap->owned = owned;
|
||||
|
||||
if (eglpixmap->image == EGL_NO_IMAGE) {
|
||||
log_error("Failed to create eglpixmap for pixmap %#010x: %s", pixmap,
|
||||
eglGetErrorString(eglGetError()));
|
||||
log_error("Failed to create eglpixmap for pixmap %#010x", pixmap);
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -336,7 +310,7 @@ err:
|
||||
free(eglpixmap);
|
||||
|
||||
if (owned) {
|
||||
xcb_free_pixmap(base->c->c, pixmap);
|
||||
xcb_free_pixmap(base->c, pixmap);
|
||||
}
|
||||
free(wd);
|
||||
return NULL;
|
||||
@@ -346,6 +320,9 @@ static void egl_present(backend_t *base, const region_t *region attr_unused) {
|
||||
struct egl_data *gd = (void *)base;
|
||||
gl_present(base, region);
|
||||
eglSwapBuffers(gd->display, gd->target_win);
|
||||
if (!gd->gl.is_nvidia) {
|
||||
glFinish();
|
||||
}
|
||||
}
|
||||
|
||||
static int egl_buffer_age(backend_t *base) {
|
||||
@@ -395,7 +372,6 @@ struct backend_operations egl_ops = {
|
||||
.deinit = egl_deinit,
|
||||
.bind_pixmap = egl_bind_pixmap,
|
||||
.release_image = gl_release_image,
|
||||
.prepare = gl_prepare,
|
||||
.compose = gl_compose,
|
||||
.image_op = gl_image_op,
|
||||
.set_image_property = gl_set_image_property,
|
||||
@@ -404,7 +380,6 @@ struct backend_operations egl_ops = {
|
||||
.is_image_transparent = default_is_image_transparent,
|
||||
.present = egl_present,
|
||||
.buffer_age = egl_buffer_age,
|
||||
.last_render_time = gl_last_render_time,
|
||||
.create_shadow_context = gl_create_shadow_context,
|
||||
.destroy_shadow_context = gl_destroy_shadow_context,
|
||||
.render_shadow = backend_render_shadow_from_mask,
|
||||
@@ -424,7 +399,7 @@ struct backend_operations egl_ops = {
|
||||
|
||||
PFNEGLGETDISPLAYDRIVERNAMEPROC eglGetDisplayDriverName;
|
||||
/**
|
||||
* Check if a EGL extension exists.
|
||||
* Check if a GLX extension exists.
|
||||
*/
|
||||
static inline bool egl_has_extension(EGLDisplay dpy, const char *ext) {
|
||||
const char *egl_exts = eglQueryString(dpy, EGL_EXTENSIONS);
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) Yuxuan Shui <yshuiv7@gmail.com>
|
||||
#pragma once
|
||||
#include <stdbool.h>
|
||||
// Older version of glx.h defines function prototypes for these extensions...
|
||||
// Rename them to avoid conflicts
|
||||
#include <EGL/egl.h>
|
||||
#include <EGL/eglext.h>
|
||||
#include <GL/gl.h>
|
||||
#include <GL/glext.h>
|
||||
#include <stdbool.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
|
||||
@@ -22,11 +22,6 @@
|
||||
#include "backend/backend_common.h"
|
||||
#include "backend/gl/gl_common.h"
|
||||
|
||||
void gl_prepare(backend_t *base, const region_t *reg attr_unused) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
glBeginQuery(GL_TIME_ELAPSED, gd->frame_timing[gd->current_frame_timing]);
|
||||
}
|
||||
|
||||
GLuint gl_create_shader(GLenum shader_type, const char *shader_str) {
|
||||
log_trace("===\n%s\n===", shader_str);
|
||||
|
||||
@@ -309,9 +304,9 @@ static GLuint gl_average_texture_color(backend_t *base, struct backend_image *im
|
||||
// Allocate buffers for render input
|
||||
GLint coord[16] = {0};
|
||||
GLuint indices[] = {0, 1, 2, 2, 3, 0};
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_DYNAMIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
||||
GL_STREAM_DRAW);
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
// Do actual recursive render to 1x1 texture
|
||||
GLuint result_texture = _gl_average_texture_color(
|
||||
@@ -448,9 +443,9 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
|
||||
glGenBuffers(2, bo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * nrects * 16, coord, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * nrects * 6,
|
||||
indices, GL_STREAM_DRAW);
|
||||
indices, GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(vert_coord_loc);
|
||||
glEnableVertexAttribArray(vert_in_texcoord_loc);
|
||||
@@ -482,6 +477,8 @@ static void _gl_compose(backend_t *base, struct backend_image *img, GLuint targe
|
||||
glUseProgram(0);
|
||||
|
||||
gl_check_err();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/// Convert rectangles in X coordinates to OpenGL vertex and texture coordinates
|
||||
@@ -497,7 +494,7 @@ void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
||||
int extent_height, int texture_height, int root_height,
|
||||
bool y_inverted, GLint *coord, GLuint *indices) {
|
||||
image_dst.y = root_height - image_dst.y;
|
||||
image_dst.y -= extent_height;
|
||||
image_dst.y -= extent_height;
|
||||
|
||||
|
||||
for (int i = 0; i < nrects; i++) {
|
||||
@@ -812,10 +809,7 @@ uint64_t gl_get_shader_attributes(backend_t *backend_data attr_unused, void *sha
|
||||
}
|
||||
|
||||
bool gl_init(struct gl_data *gd, session_t *ps) {
|
||||
glGenQueries(2, gd->frame_timing);
|
||||
gd->current_frame_timing = 0;
|
||||
|
||||
// Initialize GL data structure
|
||||
// Initialize GLX data structure
|
||||
glDisable(GL_DEPTH_TEST);
|
||||
glDepthMask(GL_FALSE);
|
||||
|
||||
@@ -960,7 +954,7 @@ bool gl_init(struct gl_data *gd, session_t *ps) {
|
||||
const char *vendor = (const char *)glGetString(GL_VENDOR);
|
||||
log_debug("GL_VENDOR = %s", vendor);
|
||||
if (strcmp(vendor, "NVIDIA Corporation") == 0) {
|
||||
log_info("GL vendor is NVIDIA, enable xrender sync fence.");
|
||||
log_info("GL vendor is NVIDIA, don't use glFinish");
|
||||
gd->is_nvidia = true;
|
||||
} else {
|
||||
gd->is_nvidia = false;
|
||||
@@ -983,9 +977,6 @@ void gl_deinit(struct gl_data *gd) {
|
||||
gd->default_shader = NULL;
|
||||
}
|
||||
|
||||
glDeleteTextures(1, &gd->default_mask_texture);
|
||||
glDeleteTextures(1, &gd->back_texture);
|
||||
|
||||
gl_check_err();
|
||||
}
|
||||
|
||||
@@ -1072,9 +1063,9 @@ static inline void gl_image_decouple(backend_t *base, struct backend_image *img)
|
||||
glGenBuffers(2, bo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 16, coord, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
||||
GL_STREAM_DRAW);
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(vert_coord_loc);
|
||||
glEnableVertexAttribArray(vert_in_texcoord_loc);
|
||||
@@ -1172,33 +1163,10 @@ void gl_present(backend_t *base, const region_t *region) {
|
||||
glDeleteBuffers(2, bo);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
|
||||
glEndQuery(GL_TIME_ELAPSED);
|
||||
gd->current_frame_timing ^= 1;
|
||||
|
||||
gl_check_err();
|
||||
|
||||
free(coord);
|
||||
free(indices);
|
||||
}
|
||||
|
||||
bool gl_last_render_time(backend_t *base, struct timespec *ts) {
|
||||
auto gd = (struct gl_data *)base;
|
||||
GLint available = 0;
|
||||
glGetQueryObjectiv(gd->frame_timing[gd->current_frame_timing ^ 1],
|
||||
GL_QUERY_RESULT_AVAILABLE, &available);
|
||||
if (!available) {
|
||||
return false;
|
||||
}
|
||||
|
||||
GLuint64 time;
|
||||
glGetQueryObjectui64v(gd->frame_timing[gd->current_frame_timing ^ 1],
|
||||
GL_QUERY_RESULT, &time);
|
||||
ts->tv_sec = (long)(time / 1000000000);
|
||||
ts->tv_nsec = (long)(time % 1000000000);
|
||||
gl_check_err();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool gl_image_op(backend_t *base, enum image_operations op, void *image_data,
|
||||
const region_t *reg_op, const region_t *reg_visible attr_unused, void *arg) {
|
||||
struct backend_image *tex = image_data;
|
||||
@@ -1356,11 +1324,8 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, tmp_texture);
|
||||
glUseProgram(gd->shadow_shader.prog);
|
||||
// The shadow color is converted to the premultiplied format to respect the
|
||||
// globally set glBlendFunc and thus get the correct and expected result.
|
||||
glUniform4f(gd->shadow_shader.uniform_color, (GLfloat)(color.red * color.alpha),
|
||||
(GLfloat)(color.green * color.alpha),
|
||||
(GLfloat)(color.blue * color.alpha), (GLfloat)color.alpha);
|
||||
glUniform4f(gd->shadow_shader.uniform_color, (GLfloat)color.red,
|
||||
(GLfloat)color.green, (GLfloat)color.blue, (GLfloat)color.alpha);
|
||||
|
||||
// clang-format off
|
||||
GLuint indices[] = {0, 1, 2, 2, 3, 0};
|
||||
@@ -1378,9 +1343,9 @@ void *gl_shadow_from_mask(backend_t *base, void *mask,
|
||||
glGenBuffers(2, bo);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, bo[0]);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bo[1]);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 8, coord, GL_STREAM_DRAW);
|
||||
glBufferData(GL_ARRAY_BUFFER, (long)sizeof(*coord) * 8, coord, GL_STATIC_DRAW);
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (long)sizeof(*indices) * 6, indices,
|
||||
GL_STREAM_DRAW);
|
||||
GL_STATIC_DRAW);
|
||||
|
||||
glEnableVertexAttribArray(vert_coord_loc);
|
||||
glVertexAttribPointer(vert_coord_loc, 2, GL_INT, GL_FALSE, sizeof(GLint) * 2, NULL);
|
||||
|
||||
@@ -77,7 +77,7 @@ typedef struct {
|
||||
GLint color_loc;
|
||||
} gl_fill_shader_t;
|
||||
|
||||
/// @brief Wrapper of a binded GL texture.
|
||||
/// @brief Wrapper of a binded GLX texture.
|
||||
struct gl_texture {
|
||||
int refcount;
|
||||
bool has_alpha;
|
||||
@@ -108,8 +108,6 @@ struct gl_data {
|
||||
gl_shadow_shader_t shadow_shader;
|
||||
GLuint back_texture, back_fbo;
|
||||
GLint back_format;
|
||||
GLuint frame_timing[2];
|
||||
int current_frame_timing;
|
||||
GLuint present_prog;
|
||||
|
||||
bool dithered_present;
|
||||
@@ -131,7 +129,6 @@ typedef struct session session_t;
|
||||
#define GL_PROG_MAIN_INIT \
|
||||
{ .prog = 0, .unifm_opacity = -1, .unifm_invert_color = -1, .unifm_tex = -1, }
|
||||
|
||||
void gl_prepare(backend_t *base, const region_t *reg);
|
||||
void x_rect_to_coords(int nrects, const rect_t *rects, coord_t image_dst,
|
||||
int extent_height, int texture_height, int root_height,
|
||||
bool y_inverted, GLint *coord, GLuint *indices);
|
||||
@@ -145,7 +142,6 @@ void gl_destroy_window_shader(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,
|
||||
void *image_data, void *args);
|
||||
bool gl_last_render_time(backend_t *backend_data, struct timespec *time);
|
||||
|
||||
/**
|
||||
* @brief Render a region with texture data.
|
||||
@@ -188,6 +184,10 @@ 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);
|
||||
|
||||
static inline void gl_delete_texture(GLuint texture) {
|
||||
glDeleteTextures(1, &texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a textual representation of an OpenGL error.
|
||||
*/
|
||||
@@ -214,7 +214,7 @@ static inline const char *gl_get_err_str(GLenum err) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for GL error.
|
||||
* Check for GLX error.
|
||||
*
|
||||
* http://blog.nobel-joergensen.com/2013/01/29/debugging-opengl-using-glgeterror/
|
||||
*/
|
||||
@@ -225,10 +225,10 @@ static inline void gl_check_err_(const char *func, int line) {
|
||||
const char *errtext = gl_get_err_str(err);
|
||||
if (errtext) {
|
||||
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
||||
"GL error at line %d: %s", line, errtext);
|
||||
"GLX error at line %d: %s", line, errtext);
|
||||
} else {
|
||||
log_printf(tls_logger, LOG_LEVEL_ERROR, func,
|
||||
"GL error at line %d: %d", line, err);
|
||||
"GLX error at line %d: %d", line, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ 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))
|
||||
|
||||
/**
|
||||
* Check if a GL extension exists.
|
||||
* Check if a GLX extension exists.
|
||||
*/
|
||||
static inline bool gl_has_extension(const char *ext) {
|
||||
int nexts = 0;
|
||||
|
||||
@@ -42,6 +42,8 @@ struct _glx_pixmap {
|
||||
|
||||
struct _glx_data {
|
||||
struct gl_data gl;
|
||||
Display *display;
|
||||
int screen;
|
||||
xcb_window_t target_win;
|
||||
GLXContext ctx;
|
||||
};
|
||||
@@ -50,18 +52,18 @@ struct _glx_data {
|
||||
do { \
|
||||
if (glXGetFBConfigAttrib(a, b, attr, c)) { \
|
||||
log_info("Cannot get FBConfig attribute " #attr); \
|
||||
break; \
|
||||
continue; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisual_info m) {
|
||||
struct glx_fbconfig_info *glx_find_fbconfig(Display *dpy, int screen, struct xvisual_info m) {
|
||||
log_debug("Looking for FBConfig for RGBA%d%d%d%d, depth %d", m.red_size,
|
||||
m.blue_size, m.green_size, m.alpha_size, m.visual_depth);
|
||||
|
||||
int ncfg;
|
||||
// clang-format off
|
||||
GLXFBConfig *cfg =
|
||||
glXChooseFBConfig(c->dpy, c->screen, (int[]){
|
||||
glXChooseFBConfig(dpy, screen, (int[]){
|
||||
GLX_RENDER_TYPE, GLX_RGBA_BIT,
|
||||
GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
|
||||
GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
|
||||
@@ -85,26 +87,25 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
|
||||
GLXFBConfig ret;
|
||||
for (int i = 0; i < ncfg; i++) {
|
||||
int depthbuf, stencil, doublebuf, bufsize;
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_BUFFER_SIZE, &bufsize);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_DEPTH_SIZE, &depthbuf);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_STENCIL_SIZE, &stencil);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_DOUBLEBUFFER, &doublebuf);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BUFFER_SIZE, &bufsize);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DEPTH_SIZE, &depthbuf);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_STENCIL_SIZE, &stencil);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_DOUBLEBUFFER, &doublebuf);
|
||||
if (depthbuf + stencil + bufsize * (doublebuf + 1) >= min_cost) {
|
||||
continue;
|
||||
}
|
||||
int red, green, blue;
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_RED_SIZE, &red);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_BLUE_SIZE, &blue);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_GREEN_SIZE, &green);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_RED_SIZE, &red);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BLUE_SIZE, &blue);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_GREEN_SIZE, &green);
|
||||
if (red != m.red_size || green != m.green_size || blue != m.blue_size) {
|
||||
// Color size doesn't match, this cannot work
|
||||
continue;
|
||||
}
|
||||
|
||||
int rgb, rgba;
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &rgb);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGBA_EXT,
|
||||
&rgba);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &rgb);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &rgba);
|
||||
if (!rgb && !rgba) {
|
||||
log_info("FBConfig is neither RGBA nor RGB, we cannot "
|
||||
"handle this setup.");
|
||||
@@ -112,9 +113,10 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
|
||||
}
|
||||
|
||||
int visual;
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_VISUAL_ID, &visual);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_VISUAL_ID, &visual);
|
||||
if (m.visual_depth != -1 &&
|
||||
x_get_visual_depth(c, (xcb_visualid_t)visual) != m.visual_depth) {
|
||||
x_get_visual_depth(XGetXCBConnection(dpy), (xcb_visualid_t)visual) !=
|
||||
m.visual_depth) {
|
||||
// FBConfig and the correspondent X Visual might not have the same
|
||||
// depth. (e.g. 32 bit FBConfig with a 24 bit Visual). This is
|
||||
// quite common, seen in both open source and proprietary drivers.
|
||||
@@ -127,9 +129,9 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
|
||||
// All check passed, we are using this one.
|
||||
found = true;
|
||||
ret = cfg[i];
|
||||
glXGetFBConfigAttribChecked(
|
||||
c->dpy, cfg[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT, &texture_tgts);
|
||||
glXGetFBConfigAttribChecked(c->dpy, cfg[i], GLX_Y_INVERTED_EXT, &y_inverted);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_BIND_TO_TEXTURE_TARGETS_EXT,
|
||||
&texture_tgts);
|
||||
glXGetFBConfigAttribChecked(dpy, cfg[i], GLX_Y_INVERTED_EXT, &y_inverted);
|
||||
|
||||
// Prefer the texture format with matching alpha, with the other one as
|
||||
// fallback
|
||||
@@ -159,22 +161,24 @@ struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *c, struct xvisu
|
||||
* Free a glx_texture_t.
|
||||
*/
|
||||
static void glx_release_image(backend_t *base, struct gl_texture *tex) {
|
||||
struct _glx_data *gd = (void *)base;
|
||||
|
||||
struct _glx_pixmap *p = tex->user_data;
|
||||
// Release binding
|
||||
if (p->glpixmap && tex->texture) {
|
||||
glBindTexture(GL_TEXTURE_2D, tex->texture);
|
||||
glXReleaseTexImageEXT(base->c->dpy, p->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
glXReleaseTexImageEXT(gd->display, p->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
}
|
||||
|
||||
// Free GLX Pixmap
|
||||
if (p->glpixmap) {
|
||||
glXDestroyPixmap(base->c->dpy, p->glpixmap);
|
||||
glXDestroyPixmap(gd->display, p->glpixmap);
|
||||
p->glpixmap = 0;
|
||||
}
|
||||
|
||||
if (p->owned) {
|
||||
xcb_free_pixmap(base->c->c, p->pixmap);
|
||||
xcb_free_pixmap(base->c, p->pixmap);
|
||||
p->pixmap = XCB_NONE;
|
||||
}
|
||||
|
||||
@@ -192,8 +196,8 @@ void glx_deinit(backend_t *base) {
|
||||
|
||||
// Destroy GLX context
|
||||
if (gd->ctx) {
|
||||
glXMakeCurrent(base->c->dpy, None, NULL);
|
||||
glXDestroyContext(base->c->dpy, gd->ctx);
|
||||
glXMakeCurrent(gd->display, None, NULL);
|
||||
glXDestroyContext(gd->display, gd->ctx);
|
||||
gd->ctx = 0;
|
||||
}
|
||||
|
||||
@@ -227,13 +231,15 @@ static bool glx_set_swap_interval(int interval, Display *dpy, GLXDrawable drawab
|
||||
/**
|
||||
* Initialize OpenGL.
|
||||
*/
|
||||
static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
static backend_t *glx_init(session_t *ps) {
|
||||
bool success = false;
|
||||
glxext_init(ps->c.dpy, ps->c.screen);
|
||||
glxext_init(ps->dpy, ps->scr);
|
||||
auto gd = ccalloc(1, struct _glx_data);
|
||||
init_backend_base(&gd->gl.base, ps);
|
||||
|
||||
gd->target_win = target;
|
||||
gd->display = ps->dpy;
|
||||
gd->screen = ps->scr;
|
||||
gd->target_win = session_get_target_window(ps);
|
||||
|
||||
XVisualInfo *pvis = NULL;
|
||||
|
||||
@@ -245,8 +251,8 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
|
||||
// Get XVisualInfo
|
||||
int nitems = 0;
|
||||
XVisualInfo vreq = {.visualid = ps->c.screen_info->root_visual};
|
||||
pvis = XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems);
|
||||
XVisualInfo vreq = {.visualid = ps->vis};
|
||||
pvis = XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
|
||||
if (!pvis) {
|
||||
log_error("Failed to acquire XVisualInfo for current visual.");
|
||||
goto end;
|
||||
@@ -254,22 +260,22 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
|
||||
// Ensure the visual is double-buffered
|
||||
int value = 0;
|
||||
if (glXGetConfig(ps->c.dpy, pvis, GLX_USE_GL, &value) || !value) {
|
||||
if (glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
|
||||
log_error("Root visual is not a GL visual.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (glXGetConfig(ps->c.dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
|
||||
if (glXGetConfig(ps->dpy, pvis, GLX_STENCIL_SIZE, &value) || !value) {
|
||||
log_error("Root visual lacks stencil buffer.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
|
||||
if (glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
|
||||
log_error("Root visual is not a double buffered GL visual.");
|
||||
goto end;
|
||||
}
|
||||
|
||||
if (glXGetConfig(ps->c.dpy, pvis, GLX_RGBA, &value) || !value) {
|
||||
if (glXGetConfig(ps->dpy, pvis, GLX_RGBA, &value) || !value) {
|
||||
log_error("Root visual is a color index visual, not supported");
|
||||
goto end;
|
||||
}
|
||||
@@ -287,11 +293,11 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
// Find a fbconfig with visualid matching the one from the target win, so we can
|
||||
// be sure that the fbconfig is compatible with our target window.
|
||||
int ncfgs;
|
||||
GLXFBConfig *cfg = glXGetFBConfigs(ps->c.dpy, ps->c.screen, &ncfgs);
|
||||
GLXFBConfig *cfg = glXGetFBConfigs(gd->display, gd->screen, &ncfgs);
|
||||
bool found = false;
|
||||
for (int i = 0; i < ncfgs; i++) {
|
||||
int visualid;
|
||||
glXGetFBConfigAttribChecked(ps->c.dpy, cfg[i], GLX_VISUAL_ID, &visualid);
|
||||
glXGetFBConfigAttribChecked(gd->display, cfg[i], GLX_VISUAL_ID, &visualid);
|
||||
if ((VisualID)visualid != pvis->visualid) {
|
||||
continue;
|
||||
}
|
||||
@@ -310,7 +316,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
attributes[7] = GLX_LOSE_CONTEXT_ON_RESET_ARB;
|
||||
}
|
||||
|
||||
gd->ctx = glXCreateContextAttribsARB(ps->c.dpy, cfg[i], 0, true, attributes);
|
||||
gd->ctx = glXCreateContextAttribsARB(ps->dpy, cfg[i], 0, true, attributes);
|
||||
free(cfg);
|
||||
|
||||
if (!gd->ctx) {
|
||||
@@ -328,7 +334,7 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
|
||||
// Attach GLX context
|
||||
GLXDrawable tgt = gd->target_win;
|
||||
if (!glXMakeCurrent(ps->c.dpy, tgt, gd->ctx)) {
|
||||
if (!glXMakeCurrent(ps->dpy, tgt, gd->ctx)) {
|
||||
log_error("Failed to attach GLX context.");
|
||||
goto end;
|
||||
}
|
||||
@@ -342,11 +348,11 @@ static backend_t *glx_init(session_t *ps, xcb_window_t target) {
|
||||
gd->gl.release_user_data = glx_release_image;
|
||||
|
||||
if (ps->o.vsync) {
|
||||
if (!glx_set_swap_interval(1, ps->c.dpy, tgt)) {
|
||||
if (!glx_set_swap_interval(1, ps->dpy, tgt)) {
|
||||
log_error("Failed to enable vsync.");
|
||||
}
|
||||
} else {
|
||||
glx_set_swap_interval(0, ps->c.dpy, tgt);
|
||||
glx_set_swap_interval(0, ps->dpy, tgt);
|
||||
}
|
||||
|
||||
success = true;
|
||||
@@ -366,21 +372,21 @@ end:
|
||||
|
||||
static void *
|
||||
glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
||||
struct _glx_data *gd = (void *)base;
|
||||
struct _glx_pixmap *glxpixmap = NULL;
|
||||
// Retrieve pixmap parameters, if they aren't provided
|
||||
if (fmt.visual_depth > OPENGL_MAX_DEPTH) {
|
||||
log_error("Requested depth %d higher than max possible depth %d.",
|
||||
fmt.visual_depth, OPENGL_MAX_DEPTH);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fmt.visual_depth < 0) {
|
||||
log_error("Pixmap %#010x with invalid depth %d", pixmap, fmt.visual_depth);
|
||||
return NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto r =
|
||||
xcb_get_geometry_reply(base->c->c, xcb_get_geometry(base->c->c, pixmap), NULL);
|
||||
auto r = xcb_get_geometry_reply(base->c, xcb_get_geometry(base->c, pixmap), NULL);
|
||||
if (!r) {
|
||||
log_error("Invalid pixmap %#010x", pixmap);
|
||||
return NULL;
|
||||
@@ -394,7 +400,7 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
wd->inner = (struct backend_image_inner_base *)inner;
|
||||
free(r);
|
||||
|
||||
auto fbcfg = glx_find_fbconfig(base->c, fmt);
|
||||
auto fbcfg = glx_find_fbconfig(gd->display, gd->screen, fmt);
|
||||
if (!fbcfg) {
|
||||
log_error("Couldn't find FBConfig with requested visual %x", fmt.visual);
|
||||
goto err;
|
||||
@@ -423,7 +429,7 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
|
||||
glxpixmap = cmalloc(struct _glx_pixmap);
|
||||
glxpixmap->pixmap = pixmap;
|
||||
glxpixmap->glpixmap = glXCreatePixmap(base->c->dpy, fbcfg->cfg, pixmap, attrs);
|
||||
glxpixmap->glpixmap = glXCreatePixmap(gd->display, fbcfg->cfg, pixmap, attrs);
|
||||
glxpixmap->owned = owned;
|
||||
free(fbcfg);
|
||||
|
||||
@@ -440,19 +446,19 @@ glx_bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, b
|
||||
inner->has_alpha = fmt.alpha_size != 0;
|
||||
wd->inner->refcount = 1;
|
||||
glBindTexture(GL_TEXTURE_2D, inner->texture);
|
||||
glXBindTexImageEXT(base->c->dpy, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
glXBindTexImageEXT(gd->display, glxpixmap->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
gl_check_err();
|
||||
return wd;
|
||||
err:
|
||||
if (glxpixmap && glxpixmap->glpixmap) {
|
||||
glXDestroyPixmap(base->c->dpy, glxpixmap->glpixmap);
|
||||
glXDestroyPixmap(gd->display, glxpixmap->glpixmap);
|
||||
}
|
||||
free(glxpixmap);
|
||||
|
||||
if (owned) {
|
||||
xcb_free_pixmap(base->c->c, pixmap);
|
||||
xcb_free_pixmap(base->c, pixmap);
|
||||
}
|
||||
free(wd);
|
||||
return NULL;
|
||||
@@ -461,7 +467,10 @@ err:
|
||||
static void glx_present(backend_t *base, const region_t *region attr_unused) {
|
||||
struct _glx_data *gd = (void *)base;
|
||||
gl_present(base, region);
|
||||
glXSwapBuffers(base->c->dpy, gd->target_win);
|
||||
glXSwapBuffers(gd->display, gd->target_win);
|
||||
if (!gd->gl.is_nvidia) {
|
||||
glFinish();
|
||||
}
|
||||
}
|
||||
|
||||
static int glx_buffer_age(backend_t *base) {
|
||||
@@ -471,14 +480,15 @@ static int glx_buffer_age(backend_t *base) {
|
||||
|
||||
struct _glx_data *gd = (void *)base;
|
||||
unsigned int val;
|
||||
glXQueryDrawable(base->c->dpy, gd->target_win, GLX_BACK_BUFFER_AGE_EXT, &val);
|
||||
glXQueryDrawable(gd->display, gd->target_win, GLX_BACK_BUFFER_AGE_EXT, &val);
|
||||
return (int)val ?: -1;
|
||||
}
|
||||
|
||||
static void glx_diagnostics(backend_t *base) {
|
||||
struct _glx_data *gd = (void *)base;
|
||||
bool warn_software_rendering = false;
|
||||
const char *software_renderer_names[] = {"llvmpipe", "SWR", "softpipe"};
|
||||
auto glx_vendor = glXGetClientString(base->c->dpy, GLX_VENDOR);
|
||||
auto glx_vendor = glXGetClientString(gd->display, GLX_VENDOR);
|
||||
printf("* Driver vendors:\n");
|
||||
printf(" * GLX: %s\n", glx_vendor);
|
||||
printf(" * GL: %s\n", glGetString(GL_VENDOR));
|
||||
@@ -518,7 +528,6 @@ struct backend_operations glx_ops = {
|
||||
.deinit = glx_deinit,
|
||||
.bind_pixmap = glx_bind_pixmap,
|
||||
.release_image = gl_release_image,
|
||||
.prepare = gl_prepare,
|
||||
.compose = gl_compose,
|
||||
.image_op = gl_image_op,
|
||||
.set_image_property = gl_set_image_property,
|
||||
@@ -527,7 +536,6 @@ struct backend_operations glx_ops = {
|
||||
.is_image_transparent = default_is_image_transparent,
|
||||
.present = glx_present,
|
||||
.buffer_age = glx_buffer_age,
|
||||
.last_render_time = gl_last_render_time,
|
||||
.create_shadow_context = gl_create_shadow_context,
|
||||
.destroy_shadow_context = gl_destroy_shadow_context,
|
||||
.render_shadow = backend_render_shadow_from_mask,
|
||||
@@ -615,7 +623,7 @@ void glxext_init(Display *dpy, int screen) {
|
||||
#endif
|
||||
#undef check_ext
|
||||
|
||||
#define lookup(name) ((name) = (__typeof__(name))glXGetProcAddress((GLubyte *)#name))
|
||||
#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.
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
#undef glXBindTexImageEXT
|
||||
#undef glXReleaseTexImageEXT
|
||||
#include <X11/Xlib.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/render.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "log.h"
|
||||
#include "compiler.h"
|
||||
#include "utils.h"
|
||||
#include "x.h"
|
||||
|
||||
@@ -41,7 +41,8 @@ struct glx_fbconfig_criteria {
|
||||
int visual_depth;
|
||||
};
|
||||
|
||||
struct glx_fbconfig_info *glx_find_fbconfig(struct x_connection *, struct xvisual_info);
|
||||
struct glx_fbconfig_info *glx_find_fbconfig(Display *, int screen, struct xvisual_info);
|
||||
|
||||
|
||||
struct glxext_info {
|
||||
bool initialized;
|
||||
|
||||
@@ -202,8 +202,7 @@ const char dither_glsl[] = GLSL(330,
|
||||
}
|
||||
vec4 dither(vec4 c, vec2 coord) {
|
||||
vec4 residual = mod(c, 1.0 / 255.0);
|
||||
residual = min(residual, vec4(1.0 / 255.0) - residual);
|
||||
vec4 dithered = vec4(greaterThan(residual, vec4(1.0 / 65535.0)));
|
||||
vec4 dithered = vec4(greaterThan(residual, vec4(1e-4)));
|
||||
return vec4(c + dithered * bayer(coord) / 255.0);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -28,6 +28,7 @@ typedef struct _xrender_data {
|
||||
backend_t base;
|
||||
/// If vsync is enabled and supported by the current system
|
||||
bool vsync;
|
||||
xcb_visualid_t default_visual;
|
||||
/// Target window
|
||||
xcb_window_t target_win;
|
||||
/// Painting target, it is either the root or the overlay
|
||||
@@ -103,9 +104,9 @@ struct xrender_image {
|
||||
/// Make a picture of size width x height, which has a rounded rectangle of corner_radius
|
||||
/// rendered in it.
|
||||
struct xrender_rounded_rectangle_cache *
|
||||
make_rounded_corner_cache(struct x_connection *c, xcb_render_picture_t src, int width,
|
||||
int height, int corner_radius) {
|
||||
auto picture = x_create_picture_with_standard(c, width, height,
|
||||
make_rounded_corner_cache(xcb_connection_t *c, xcb_render_picture_t src,
|
||||
xcb_drawable_t root, int width, int height, int corner_radius) {
|
||||
auto picture = x_create_picture_with_standard(c, root, width, height,
|
||||
XCB_PICT_STANDARD_ARGB_32, 0, NULL);
|
||||
if (picture == XCB_NONE) {
|
||||
return NULL;
|
||||
@@ -159,7 +160,7 @@ make_rounded_corner_cache(struct x_connection *c, xcb_render_picture_t src, int
|
||||
}
|
||||
#undef ADD_POINT
|
||||
|
||||
XCB_AWAIT_VOID(xcb_render_tri_strip, c->c, XCB_RENDER_PICT_OP_SRC, src, picture,
|
||||
XCB_AWAIT_VOID(xcb_render_tri_strip, c, XCB_RENDER_PICT_OP_SRC, src, picture,
|
||||
x_get_pictfmt_for_standard(c, XCB_PICT_STANDARD_A_8), 0, 0,
|
||||
(uint32_t)point_count, points);
|
||||
free(points);
|
||||
@@ -176,34 +177,35 @@ static xcb_render_picture_t process_mask(struct _xrender_data *xd, struct xrende
|
||||
*allocated = false;
|
||||
return inner->pict;
|
||||
}
|
||||
auto const tmpw = to_u16_checked(inner->width);
|
||||
auto const tmph = to_u16_checked(inner->height);
|
||||
const auto tmpw = to_u16_checked(inner->width);
|
||||
const auto tmph = to_u16_checked(inner->height);
|
||||
*allocated = true;
|
||||
x_clear_picture_clip_region(xd->base.c, inner->pict);
|
||||
auto ret = x_create_picture_with_visual(
|
||||
xd->base.c, inner->width, inner->height, inner->visual, XCB_RENDER_CP_REPEAT,
|
||||
xd->base.c, xd->base.root, inner->width, inner->height, inner->visual,
|
||||
XCB_RENDER_CP_REPEAT,
|
||||
(xcb_render_create_picture_value_list_t[]){XCB_RENDER_REPEAT_PAD});
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
|
||||
ret, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
// Remember: the mask has a 1-pixel border
|
||||
if (mask->base.corner_radius != 0) {
|
||||
if (mask->rounded_rectangle == NULL) {
|
||||
mask->rounded_rectangle = make_rounded_corner_cache(
|
||||
xd->base.c, xd->white_pixel, inner->width - 2,
|
||||
xd->base.c, xd->white_pixel, xd->base.root, inner->width - 2,
|
||||
inner->height - 2, (int)mask->base.corner_radius);
|
||||
}
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
mask->rounded_rectangle->p, XCB_NONE, ret, 0, 0, 0,
|
||||
0, 1, 1, (uint16_t)(tmpw - 2), (uint16_t)(tmph - 2));
|
||||
}
|
||||
|
||||
if (mask->base.color_inverted) {
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_XOR, xd->white_pixel,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_XOR, xd->white_pixel,
|
||||
XCB_NONE, ret, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
|
||||
if (alpha_pict != XCB_NONE) {
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_SRC, ret, alpha_pict,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, ret, alpha_pict,
|
||||
ret, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width),
|
||||
to_u16_checked(inner->height));
|
||||
}
|
||||
@@ -226,13 +228,13 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
||||
region_t reg;
|
||||
|
||||
bool has_alpha = inner->has_alpha || img->opacity != 1;
|
||||
auto const tmpw = to_u16_checked(inner->width);
|
||||
auto const tmph = to_u16_checked(inner->height);
|
||||
auto const tmpew = to_u16_checked(img->ewidth);
|
||||
auto const tmpeh = to_u16_checked(img->eheight);
|
||||
const auto tmpw = to_u16_checked(inner->width);
|
||||
const auto tmph = to_u16_checked(inner->height);
|
||||
const auto tmpew = to_u16_checked(img->ewidth);
|
||||
const auto tmpeh = to_u16_checked(img->eheight);
|
||||
// Remember: the mask has a 1-pixel border
|
||||
auto const mask_dst_x = to_i16_checked(dst.x - mask_dst.x + 1);
|
||||
auto const mask_dst_y = to_i16_checked(dst.y - mask_dst.y + 1);
|
||||
const auto mask_dst_x = to_i16_checked(dst.x - mask_dst.x + 1);
|
||||
const auto mask_dst_y = to_i16_checked(dst.y - mask_dst.y + 1);
|
||||
const xcb_render_color_t dim_color = {
|
||||
.red = 0, .green = 0, .blue = 0, .alpha = (uint16_t)(0xffff * img->dim)};
|
||||
|
||||
@@ -244,49 +246,43 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
||||
pixman_region32_intersect(®, (region_t *)reg_paint, (region_t *)reg_visible);
|
||||
x_set_picture_clip_region(xd->base.c, result, 0, 0, ®);
|
||||
if (img->corner_radius != 0 && xrimg->rounded_rectangle == NULL) {
|
||||
xrimg->rounded_rectangle =
|
||||
make_rounded_corner_cache(xd->base.c, xd->white_pixel, inner->width,
|
||||
inner->height, (int)img->corner_radius);
|
||||
xrimg->rounded_rectangle = make_rounded_corner_cache(
|
||||
xd->base.c, xd->white_pixel, xd->base.root, inner->width,
|
||||
inner->height, (int)img->corner_radius);
|
||||
}
|
||||
if (((img->color_inverted || img->dim != 0) && has_alpha) || img->corner_radius != 0) {
|
||||
// Apply image properties using a temporary image, because the source
|
||||
// image is transparent or will get transparent corners. Otherwise the
|
||||
// properties can be applied directly on the target image.
|
||||
// Also force a 32-bit ARGB visual for transparent corners, otherwise the
|
||||
// corners become black.
|
||||
auto visual =
|
||||
(img->corner_radius != 0 && inner->depth != 32)
|
||||
? x_get_visual_for_standard(xd->base.c, XCB_PICT_STANDARD_ARGB_32)
|
||||
: inner->visual;
|
||||
auto tmp_pict = x_create_picture_with_visual(
|
||||
xd->base.c, inner->width, inner->height, visual, 0, NULL);
|
||||
// image is transparent. Otherwise the properties can be applied directly
|
||||
// on the target image.
|
||||
auto tmp_pict =
|
||||
x_create_picture_with_visual(xd->base.c, xd->base.root, inner->width,
|
||||
inner->height, inner->visual, 0, NULL);
|
||||
|
||||
// Set clip region translated to source coordinate
|
||||
x_set_picture_clip_region(xd->base.c, tmp_pict, to_i16_checked(-dst.x),
|
||||
to_i16_checked(-dst.y), ®);
|
||||
// Copy source -> tmp
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
|
||||
if (img->color_inverted) {
|
||||
if (inner->has_alpha) {
|
||||
auto tmp_pict2 = x_create_picture_with_visual(
|
||||
xd->base.c, tmpw, tmph, inner->visual, 0, NULL);
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_SRC,
|
||||
xd->base.c, xd->base.root, tmpw, tmph, inner->visual,
|
||||
0, NULL);
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_SRC,
|
||||
tmp_pict, XCB_NONE, tmp_pict2, 0, 0,
|
||||
0, 0, 0, 0, tmpw, tmph);
|
||||
|
||||
xcb_render_composite(xd->base.c->c,
|
||||
XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, tmp_pict,
|
||||
0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
xcb_render_composite(
|
||||
xd->base.c->c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict2,
|
||||
xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE, tmp_pict2,
|
||||
XCB_NONE, tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
x_free_picture(xd->base.c, tmp_pict2);
|
||||
xcb_render_free_picture(xd->base.c, tmp_pict2);
|
||||
} else {
|
||||
xcb_render_composite(xd->base.c->c,
|
||||
XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, tmp_pict,
|
||||
0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
@@ -301,34 +297,33 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
||||
.height = tmph,
|
||||
};
|
||||
|
||||
xcb_render_fill_rectangles(xd->base.c->c, XCB_RENDER_PICT_OP_OVER,
|
||||
xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER,
|
||||
tmp_pict, dim_color, 1, &rect);
|
||||
}
|
||||
|
||||
if (img->corner_radius != 0 && xrimg->rounded_rectangle != NULL) {
|
||||
// Clip tmp_pict with a rounded rectangle
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
xrimg->rounded_rectangle->p, XCB_NONE,
|
||||
tmp_pict, 0, 0, 0, 0, 0, 0, tmpw, tmph);
|
||||
}
|
||||
|
||||
xcb_render_composite(xd->base.c->c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_OVER, tmp_pict,
|
||||
mask_pict, result, 0, 0, mask_dst_x, mask_dst_y,
|
||||
to_i16_checked(dst.x), to_i16_checked(dst.y), tmpew,
|
||||
tmpeh);
|
||||
xcb_render_free_picture(xd->base.c->c, tmp_pict);
|
||||
xcb_render_free_picture(xd->base.c, tmp_pict);
|
||||
} else {
|
||||
uint8_t op = (has_alpha ? XCB_RENDER_PICT_OP_OVER : XCB_RENDER_PICT_OP_SRC);
|
||||
|
||||
xcb_render_composite(xd->base.c->c, op, inner->pict, mask_pict, result, 0,
|
||||
0, mask_dst_x, mask_dst_y, to_i16_checked(dst.x),
|
||||
xcb_render_composite(xd->base.c, op, inner->pict, mask_pict, result, 0, 0,
|
||||
mask_dst_x, mask_dst_y, to_i16_checked(dst.x),
|
||||
to_i16_checked(dst.y), tmpew, tmpeh);
|
||||
if (img->dim != 0 || img->color_inverted) {
|
||||
// Apply properties, if we reach here, then has_alpha == false
|
||||
assert(!has_alpha);
|
||||
if (img->color_inverted) {
|
||||
xcb_render_composite(xd->base.c->c,
|
||||
XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xcb_render_composite(xd->base.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xd->white_pixel, XCB_NONE, result, 0,
|
||||
0, 0, 0, to_i16_checked(dst.x),
|
||||
to_i16_checked(dst.y), tmpew, tmpeh);
|
||||
@@ -343,14 +338,13 @@ compose_impl(struct _xrender_data *xd, struct xrender_image *xrimg, coord_t dst,
|
||||
.height = tmpeh,
|
||||
};
|
||||
|
||||
xcb_render_fill_rectangles(xd->base.c->c,
|
||||
XCB_RENDER_PICT_OP_OVER,
|
||||
xcb_render_fill_rectangles(xd->base.c, XCB_RENDER_PICT_OP_OVER,
|
||||
result, dim_color, 1, &rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mask_allocated) {
|
||||
x_free_picture(xd->base.c, mask_pict);
|
||||
xcb_render_free_picture(xd->base.c, mask_pict);
|
||||
}
|
||||
pixman_region32_fini(®);
|
||||
}
|
||||
@@ -368,7 +362,7 @@ static void fill(backend_t *base, struct color c, const region_t *clip) {
|
||||
x_set_picture_clip_region(base->c, xd->back[2], 0, 0, clip);
|
||||
// color is in X fixed point representation
|
||||
xcb_render_fill_rectangles(
|
||||
base->c->c, XCB_RENDER_PICT_OP_OVER, xd->back[2],
|
||||
base->c, XCB_RENDER_PICT_OP_OVER, xd->back[2],
|
||||
(xcb_render_color_t){.red = (uint16_t)(c.red * 0xffff),
|
||||
.green = (uint16_t)(c.green * 0xffff),
|
||||
.blue = (uint16_t)(c.blue * 0xffff),
|
||||
@@ -388,7 +382,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
}
|
||||
|
||||
struct _xrender_data *xd = (void *)backend_data;
|
||||
auto c = xd->base.c;
|
||||
xcb_connection_t *c = xd->base.c;
|
||||
region_t reg_op;
|
||||
pixman_region32_init(®_op);
|
||||
pixman_region32_intersect(®_op, (region_t *)reg_blur, (region_t *)reg_visible);
|
||||
@@ -401,8 +395,8 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
resize_region(®_op, bctx->resize_width, bctx->resize_height);
|
||||
|
||||
const pixman_box32_t *extent_resized = pixman_region32_extents(®_op_resized);
|
||||
auto const height_resized = to_u16_checked(extent_resized->y2 - extent_resized->y1);
|
||||
auto const width_resized = to_u16_checked(extent_resized->x2 - extent_resized->x1);
|
||||
const auto height_resized = to_u16_checked(extent_resized->y2 - extent_resized->y1);
|
||||
const auto width_resized = to_u16_checked(extent_resized->x2 - extent_resized->x1);
|
||||
static const char *filter0 = "Nearest"; // The "null" filter
|
||||
static const char *filter = "convolution";
|
||||
|
||||
@@ -411,12 +405,10 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT;
|
||||
const xcb_render_create_picture_value_list_t pic_attrs = {.repeat = XCB_RENDER_REPEAT_PAD};
|
||||
xcb_render_picture_t tmp_picture[2] = {
|
||||
x_create_picture_with_visual(xd->base.c, width_resized, height_resized,
|
||||
xd->base.c->screen_info->root_visual,
|
||||
pic_attrs_mask, &pic_attrs),
|
||||
x_create_picture_with_visual(xd->base.c, width_resized, height_resized,
|
||||
xd->base.c->screen_info->root_visual,
|
||||
pic_attrs_mask, &pic_attrs)};
|
||||
x_create_picture_with_visual(xd->base.c, xd->base.root, width_resized, height_resized,
|
||||
xd->default_visual, pic_attrs_mask, &pic_attrs),
|
||||
x_create_picture_with_visual(xd->base.c, xd->base.root, width_resized, height_resized,
|
||||
xd->default_visual, pic_attrs_mask, &pic_attrs)};
|
||||
|
||||
if (!tmp_picture[0] || !tmp_picture[1]) {
|
||||
log_error("Failed to build intermediate Picture.");
|
||||
@@ -453,8 +445,8 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
// Copy from source picture to destination. The filter must
|
||||
// be applied on source picture, to get the nearby pixels outside the
|
||||
// window.
|
||||
xcb_render_set_picture_filter(c->c, src_pict,
|
||||
to_u16_checked(strlen(filter)), filter,
|
||||
xcb_render_set_picture_filter(c, src_pict, to_u16_checked(strlen(filter)),
|
||||
filter,
|
||||
to_u32_checked(bctx->x_blur_kernel[i]->size),
|
||||
bctx->x_blur_kernel[i]->kernel);
|
||||
|
||||
@@ -462,21 +454,21 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
// First pass, back buffer -> tmp picture
|
||||
// (we do this even if this is also the last pass, because we
|
||||
// cannot do back buffer -> back buffer)
|
||||
xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
|
||||
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
|
||||
dst_pict, to_i16_checked(extent_resized->x1),
|
||||
to_i16_checked(extent_resized->y1), 0, 0, 0,
|
||||
0, width_resized, height_resized);
|
||||
} else if (i < bctx->x_blur_kernel_count - 1) {
|
||||
// This is not the last pass or the first pass,
|
||||
// tmp picture 1 -> tmp picture 2
|
||||
xcb_render_composite(c->c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
||||
xcb_render_composite(c, XCB_RENDER_PICT_OP_SRC, src_pict,
|
||||
XCB_NONE, dst_pict, 0, 0, 0, 0, 0, 0,
|
||||
width_resized, height_resized);
|
||||
} else {
|
||||
x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op);
|
||||
// This is the last pass, and we are doing more than 1 pass
|
||||
xcb_render_composite(
|
||||
c->c, XCB_RENDER_PICT_OP_OVER, src_pict, mask_pict, xd->back[2],
|
||||
c, XCB_RENDER_PICT_OP_OVER, src_pict, mask_pict, xd->back[2],
|
||||
0, 0, to_i16_checked(extent_resized->x1 - mask_dst.x + 1),
|
||||
to_i16_checked(extent_resized->y1 - mask_dst.y + 1),
|
||||
to_i16_checked(extent_resized->x1),
|
||||
@@ -485,7 +477,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
|
||||
// reset filter
|
||||
xcb_render_set_picture_filter(
|
||||
c->c, src_pict, to_u16_checked(strlen(filter0)), filter0, 0, NULL);
|
||||
c, src_pict, to_u16_checked(strlen(filter0)), filter0, 0, NULL);
|
||||
|
||||
src_pict = tmp_picture[current];
|
||||
dst_pict = tmp_picture[!current];
|
||||
@@ -496,18 +488,15 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
if (i == 1) {
|
||||
x_set_picture_clip_region(c, xd->back[2], 0, 0, ®_op);
|
||||
xcb_render_composite(
|
||||
c->c, XCB_RENDER_PICT_OP_OVER, src_pict, mask_pict, xd->back[2], 0, 0,
|
||||
c, XCB_RENDER_PICT_OP_OVER, src_pict, mask_pict, xd->back[2], 0, 0,
|
||||
to_i16_checked(extent_resized->x1 - mask_dst.x + 1),
|
||||
to_i16_checked(extent_resized->y1 - mask_dst.y + 1),
|
||||
to_i16_checked(extent_resized->x1),
|
||||
to_i16_checked(extent_resized->y1), width_resized, height_resized);
|
||||
}
|
||||
|
||||
if (mask_allocated) {
|
||||
x_free_picture(c, mask_pict);
|
||||
}
|
||||
x_free_picture(c, tmp_picture[0]);
|
||||
x_free_picture(c, tmp_picture[1]);
|
||||
xcb_render_free_picture(c, tmp_picture[0]);
|
||||
xcb_render_free_picture(c, tmp_picture[1]);
|
||||
pixman_region32_fini(®_op);
|
||||
pixman_region32_fini(®_op_resized);
|
||||
return true;
|
||||
@@ -516,7 +505,7 @@ static bool blur(backend_t *backend_data, double opacity, void *ctx_, void *mask
|
||||
static void *
|
||||
bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool owned) {
|
||||
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, xcb_get_geometry(base->c, pixmap), &e);
|
||||
if (!r) {
|
||||
log_error("Invalid pixmap: %#010x", pixmap);
|
||||
x_print_error(e->full_sequence, e->major_code, e->minor_code, e->error_code);
|
||||
@@ -531,9 +520,8 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
|
||||
inner->height = img->base.eheight = r->height;
|
||||
inner->pixmap = pixmap;
|
||||
inner->has_alpha = fmt.alpha_size != 0;
|
||||
xcb_render_create_picture_value_list_t pic_attrs = {.repeat = XCB_RENDER_REPEAT_NORMAL};
|
||||
inner->pict = x_create_picture_with_visual_and_pixmap(
|
||||
base->c, fmt.visual, pixmap, XCB_RENDER_CP_REPEAT, &pic_attrs);
|
||||
inner->pict =
|
||||
x_create_picture_with_visual_and_pixmap(base->c, fmt.visual, pixmap, 0, NULL);
|
||||
inner->owned = owned;
|
||||
inner->visual = fmt.visual;
|
||||
inner->refcount = 1;
|
||||
@@ -551,9 +539,9 @@ bind_pixmap(backend_t *base, xcb_pixmap_t pixmap, struct xvisual_info fmt, bool
|
||||
return img;
|
||||
}
|
||||
static void release_image_inner(backend_t *base, struct _xrender_image_data_inner *inner) {
|
||||
x_free_picture(base->c, inner->pict);
|
||||
xcb_render_free_picture(base->c, inner->pict);
|
||||
if (inner->owned) {
|
||||
xcb_free_pixmap(base->c->c, inner->pixmap);
|
||||
xcb_free_pixmap(base->c, inner->pixmap);
|
||||
}
|
||||
free(inner);
|
||||
}
|
||||
@@ -567,7 +555,7 @@ release_rounded_corner_cache(backend_t *base, struct xrender_rounded_rectangle_c
|
||||
assert(cache->refcount > 0);
|
||||
cache->refcount--;
|
||||
if (cache->refcount == 0) {
|
||||
x_free_picture(base->c, cache->p);
|
||||
xcb_render_free_picture(base->c, cache->p);
|
||||
free(cache);
|
||||
}
|
||||
}
|
||||
@@ -586,22 +574,22 @@ static void release_image(backend_t *base, void *image) {
|
||||
static void deinit(backend_t *backend_data) {
|
||||
struct _xrender_data *xd = (void *)backend_data;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
x_free_picture(xd->base.c, xd->alpha_pict[i]);
|
||||
xcb_render_free_picture(xd->base.c, xd->alpha_pict[i]);
|
||||
}
|
||||
x_free_picture(xd->base.c, xd->target);
|
||||
xcb_render_free_picture(xd->base.c, xd->target);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (xd->back[i] != XCB_NONE) {
|
||||
x_free_picture(xd->base.c, xd->back[i]);
|
||||
xcb_render_free_picture(xd->base.c, xd->back[i]);
|
||||
}
|
||||
if (xd->back_pixmap[i] != XCB_NONE) {
|
||||
xcb_free_pixmap(xd->base.c->c, xd->back_pixmap[i]);
|
||||
xcb_free_pixmap(xd->base.c, xd->back_pixmap[i]);
|
||||
}
|
||||
}
|
||||
if (xd->present_event) {
|
||||
xcb_unregister_for_special_event(xd->base.c->c, xd->present_event);
|
||||
xcb_unregister_for_special_event(xd->base.c, xd->present_event);
|
||||
}
|
||||
x_free_picture(xd->base.c, xd->white_pixel);
|
||||
x_free_picture(xd->base.c, xd->black_pixel);
|
||||
xcb_render_free_picture(xd->base.c, xd->white_pixel);
|
||||
xcb_render_free_picture(xd->base.c, xd->black_pixel);
|
||||
free(xd);
|
||||
}
|
||||
|
||||
@@ -620,7 +608,7 @@ static void present(backend_t *base, const region_t *region) {
|
||||
x_clear_picture_clip_region(base->c, xd->back[xd->curr_back]);
|
||||
|
||||
// Update the back buffer first, then present
|
||||
xcb_render_composite(base->c->c, XCB_RENDER_PICT_OP_SRC, xd->back[2],
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2],
|
||||
XCB_NONE, xd->back[xd->curr_back], orig_x, orig_y, 0,
|
||||
0, orig_x, orig_y, region_width, region_height);
|
||||
|
||||
@@ -629,10 +617,10 @@ static void present(backend_t *base, const region_t *region) {
|
||||
// Make sure we got reply from PresentPixmap before waiting for events,
|
||||
// to avoid deadlock
|
||||
auto e = xcb_request_check(
|
||||
base->c->c, xcb_present_pixmap_checked(
|
||||
xd->base.c->c, xd->target_win,
|
||||
xd->back_pixmap[xd->curr_back], 0, XCB_NONE, xregion, 0,
|
||||
0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
|
||||
base->c, xcb_present_pixmap_checked(
|
||||
xd->base.c, xd->target_win,
|
||||
xd->back_pixmap[xd->curr_back], 0, XCB_NONE, xregion, 0,
|
||||
0, XCB_NONE, XCB_NONE, XCB_NONE, 0, 0, 0, 0, 0, NULL));
|
||||
x_destroy_region(base->c, xregion);
|
||||
if (e) {
|
||||
log_error("Failed to present pixmap");
|
||||
@@ -641,7 +629,7 @@ static void present(backend_t *base, const region_t *region) {
|
||||
}
|
||||
// TODO(yshui) don't block wait for present completion
|
||||
xcb_present_generic_event_t *pev =
|
||||
(void *)xcb_wait_for_special_event(base->c->c, xd->present_event);
|
||||
(void *)xcb_wait_for_special_event(base->c, xd->present_event);
|
||||
if (!pev) {
|
||||
// We don't know what happened, maybe X died
|
||||
// But reset buffer age, so in case we do recover, we will
|
||||
@@ -665,7 +653,7 @@ static void present(backend_t *base, const region_t *region) {
|
||||
free(pev);
|
||||
} else {
|
||||
// No vsync needed, draw into the target picture directly
|
||||
xcb_render_composite(base->c->c, XCB_RENDER_PICT_OP_SRC, xd->back[2],
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, xd->back[2],
|
||||
XCB_NONE, xd->target, orig_x, orig_y, 0, 0, orig_x,
|
||||
orig_y, region_width, region_height);
|
||||
}
|
||||
@@ -684,7 +672,7 @@ static int buffer_age(backend_t *backend_data) {
|
||||
static struct _xrender_image_data_inner *
|
||||
new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
|
||||
auto new_inner = ccalloc(1, struct _xrender_image_data_inner);
|
||||
new_inner->pixmap = x_create_pixmap(base->c, depth, w, h);
|
||||
new_inner->pixmap = x_create_pixmap(base->c, depth, base->root, w, h);
|
||||
if (new_inner->pixmap == XCB_NONE) {
|
||||
log_error("Failed to create pixmap for copy");
|
||||
free(new_inner);
|
||||
@@ -694,7 +682,7 @@ new_inner(backend_t *base, int w, int h, xcb_visualid_t visual, uint8_t depth) {
|
||||
base->c, visual, new_inner->pixmap, 0, NULL);
|
||||
if (new_inner->pict == XCB_NONE) {
|
||||
log_error("Failed to create picture for copy");
|
||||
xcb_free_pixmap(base->c->c, new_inner->pixmap);
|
||||
xcb_free_pixmap(base->c, new_inner->pixmap);
|
||||
free(new_inner);
|
||||
return NULL;
|
||||
}
|
||||
@@ -716,12 +704,12 @@ static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
||||
auto inner =
|
||||
new_inner(base, size.width + 2, size.height + 2,
|
||||
x_get_visual_for_standard(base->c, XCB_PICT_STANDARD_ARGB_32), 32);
|
||||
xcb_render_change_picture(base->c->c, inner->pict, XCB_RENDER_CP_REPEAT,
|
||||
xcb_render_change_picture(base->c, inner->pict, XCB_RENDER_CP_REPEAT,
|
||||
(uint32_t[]){XCB_RENDER_REPEAT_PAD});
|
||||
const rect_t *extent = pixman_region32_extents((region_t *)reg);
|
||||
x_set_picture_clip_region(base->c, xd->back[2], 1, 1, reg);
|
||||
xcb_render_fill_rectangles(
|
||||
base->c->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
base->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
(xcb_render_color_t){.red = 0, .green = 0, .blue = 0, .alpha = 0xffff}, 1,
|
||||
(xcb_rectangle_t[]){{.x = to_i16_checked(extent->x1 + 1),
|
||||
.y = to_i16_checked(extent->y1 + 1),
|
||||
@@ -731,7 +719,7 @@ static void *make_mask(backend_t *base, geometry_t size, const region_t *reg) {
|
||||
|
||||
// Paint the border transparent
|
||||
xcb_render_fill_rectangles(
|
||||
base->c->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
base->c, XCB_RENDER_PICT_OP_SRC, inner->pict,
|
||||
(xcb_render_color_t){.red = 0, .green = 0, .blue = 0, .alpha = 0}, 4,
|
||||
(xcb_rectangle_t[]){{.x = 0, .y = 0, .width = w16, .height = 1},
|
||||
{.x = 0, .y = 0, .width = 1, .height = h16},
|
||||
@@ -769,7 +757,7 @@ static bool decouple_image(backend_t *base, struct backend_image *img, const reg
|
||||
}
|
||||
|
||||
x_set_picture_clip_region(base->c, inner->pict, 0, 0, reg);
|
||||
xcb_render_composite(base->c->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_SRC, inner->pict, XCB_NONE,
|
||||
inner2->pict, 0, 0, 0, 0, 0, 0, to_u16_checked(inner->width),
|
||||
to_u16_checked(inner->height));
|
||||
|
||||
@@ -808,8 +796,8 @@ static bool image_op(backend_t *base, enum image_operations op, void *image,
|
||||
auto inner = (struct _xrender_image_data_inner *)img->inner;
|
||||
auto alpha_pict = xd->alpha_pict[(int)((1 - dargs[0]) * MAX_ALPHA)];
|
||||
x_set_picture_clip_region(base->c, inner->pict, 0, 0, ®);
|
||||
xcb_render_composite(base->c->c, XCB_RENDER_PICT_OP_OUT_REVERSE,
|
||||
alpha_pict, XCB_NONE, inner->pict, 0, 0, 0, 0, 0, 0,
|
||||
xcb_render_composite(base->c, XCB_RENDER_PICT_OP_OUT_REVERSE, alpha_pict,
|
||||
XCB_NONE, inner->pict, 0, 0, 0, 0, 0, 0,
|
||||
to_u16_checked(inner->width),
|
||||
to_u16_checked(inner->height));
|
||||
inner->has_alpha = true;
|
||||
@@ -878,7 +866,7 @@ static void get_blur_size(void *blur_context, int *width, int *height) {
|
||||
*height = ctx->resize_height;
|
||||
}
|
||||
|
||||
static backend_t *backend_xrender_init(session_t *ps, xcb_window_t target) {
|
||||
static backend_t *backend_xrender_init(session_t *ps) {
|
||||
if (ps->o.dithered_present) {
|
||||
log_warn("\"dithered-present\" is not supported by the xrender backend.");
|
||||
}
|
||||
@@ -888,30 +876,36 @@ static backend_t *backend_xrender_init(session_t *ps, xcb_window_t target) {
|
||||
|
||||
for (int i = 0; i <= MAX_ALPHA; ++i) {
|
||||
double o = (double)i / (double)MAX_ALPHA;
|
||||
xd->alpha_pict[i] = solid_picture(&ps->c, false, o, 0, 0, 0);
|
||||
xd->alpha_pict[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
|
||||
assert(xd->alpha_pict[i] != XCB_NONE);
|
||||
}
|
||||
|
||||
xd->target_width = ps->root_width;
|
||||
xd->target_height = ps->root_height;
|
||||
xd->black_pixel = solid_picture(&ps->c, true, 1, 0, 0, 0);
|
||||
xd->white_pixel = solid_picture(&ps->c, true, 1, 1, 1, 1);
|
||||
xd->default_visual = ps->vis;
|
||||
xd->black_pixel = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0);
|
||||
xd->white_pixel = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1);
|
||||
|
||||
xd->target_win = target;
|
||||
xd->target_win = session_get_target_window(ps);
|
||||
xcb_render_create_picture_value_list_t pa = {
|
||||
.subwindowmode = XCB_SUBWINDOW_MODE_INCLUDE_INFERIORS,
|
||||
};
|
||||
xd->target = x_create_picture_with_visual_and_pixmap(
|
||||
&ps->c, ps->c.screen_info->root_visual, xd->target_win,
|
||||
XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
||||
ps->c, ps->vis, xd->target_win, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
||||
|
||||
auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis);
|
||||
if (!pictfmt) {
|
||||
log_fatal("Default visual is invalid");
|
||||
abort();
|
||||
}
|
||||
|
||||
xd->vsync = ps->o.vsync;
|
||||
if (ps->present_exists) {
|
||||
auto eid = x_new_id(&ps->c);
|
||||
auto eid = x_new_id(ps->c);
|
||||
auto e =
|
||||
xcb_request_check(ps->c.c, xcb_present_select_input_checked(
|
||||
ps->c.c, eid, xd->target_win,
|
||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
|
||||
xcb_request_check(ps->c, xcb_present_select_input_checked(
|
||||
ps->c, eid, xd->target_win,
|
||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY));
|
||||
if (e) {
|
||||
log_error("Cannot select present input, vsync will be disabled");
|
||||
xd->vsync = false;
|
||||
@@ -919,7 +913,7 @@ static backend_t *backend_xrender_init(session_t *ps, xcb_window_t target) {
|
||||
}
|
||||
|
||||
xd->present_event =
|
||||
xcb_register_for_special_xge(ps->c.c, &xcb_present_id, eid, NULL);
|
||||
xcb_register_for_special_xge(ps->c, &xcb_present_id, eid, NULL);
|
||||
if (!xd->present_event) {
|
||||
log_error("Cannot register for special XGE, vsync will be "
|
||||
"disabled");
|
||||
@@ -933,15 +927,14 @@ static backend_t *backend_xrender_init(session_t *ps, xcb_window_t target) {
|
||||
// double buffering.
|
||||
int first_buffer_index = xd->vsync ? 0 : 2;
|
||||
for (int i = first_buffer_index; i < 3; i++) {
|
||||
xd->back_pixmap[i] = x_create_pixmap(&ps->c, ps->c.screen_info->root_depth,
|
||||
xd->back_pixmap[i] = x_create_pixmap(ps->c, pictfmt->depth, ps->root,
|
||||
to_u16_checked(ps->root_width),
|
||||
to_u16_checked(ps->root_height));
|
||||
const uint32_t pic_attrs_mask = XCB_RENDER_CP_REPEAT;
|
||||
const xcb_render_create_picture_value_list_t pic_attrs = {
|
||||
.repeat = XCB_RENDER_REPEAT_PAD};
|
||||
xd->back[i] = x_create_picture_with_visual_and_pixmap(
|
||||
&ps->c, ps->c.screen_info->root_visual, xd->back_pixmap[i],
|
||||
pic_attrs_mask, &pic_attrs);
|
||||
xd->back[i] = x_create_picture_with_pictfmt_and_pixmap(
|
||||
ps->c, pictfmt, xd->back_pixmap[i], pic_attrs_mask, &pic_attrs);
|
||||
xd->buffer_age[i] = -1;
|
||||
if (xd->back_pixmap[i] == XCB_NONE || xd->back[i] == XCB_NONE) {
|
||||
log_error("Cannot create pixmap for rendering");
|
||||
@@ -990,8 +983,10 @@ struct backend_operations xrender_ops = {
|
||||
.release_image = release_image,
|
||||
.create_shadow_context = default_create_shadow_context,
|
||||
.destroy_shadow_context = default_destroy_shadow_context,
|
||||
.render_shadow = default_render_shadow,
|
||||
.render_shadow = default_backend_render_shadow,
|
||||
.make_mask = make_mask,
|
||||
//.prepare_win = prepare_win,
|
||||
//.release_win = release_win,
|
||||
.is_image_transparent = default_is_image_transparent,
|
||||
.buffer_age = buffer_age,
|
||||
.max_buffer_age = 2,
|
||||
|
||||
133
src/c2.c
133
src/c2.c
@@ -233,16 +233,14 @@ static inline long winprop_get_int(winprop_t prop, size_t index) {
|
||||
*/
|
||||
static inline int strcmp_wd(const char *needle, const char *src) {
|
||||
int ret = mstrncmp(needle, src);
|
||||
if (ret) {
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
char c = src[strlen(needle)];
|
||||
if (isalnum((unsigned char)c) || '_' == c) {
|
||||
if (isalnum((unsigned char)c) || '_' == c)
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,9 +254,8 @@ static inline attr_unused bool c2_ptr_isempty(const c2_ptr_t p) {
|
||||
* Reset a c2_ptr_t.
|
||||
*/
|
||||
static inline void c2_ptr_reset(c2_ptr_t *pp) {
|
||||
if (pp) {
|
||||
if (pp)
|
||||
memcpy(pp, &C2_PTR_NULL, sizeof(c2_ptr_t));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -339,19 +336,17 @@ static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_p
|
||||
* Parse a condition string.
|
||||
*/
|
||||
c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data) {
|
||||
if (!pattern) {
|
||||
if (!pattern)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Parse the pattern
|
||||
c2_ptr_t result = C2_PTR_INIT;
|
||||
int offset = -1;
|
||||
|
||||
if (strlen(pattern) >= 2 && ':' == pattern[1]) {
|
||||
if (strlen(pattern) >= 2 && ':' == pattern[1])
|
||||
offset = c2_parse_legacy(pattern, 0, &result);
|
||||
} else {
|
||||
else
|
||||
offset = c2_parse_grp(pattern, 0, &result, 0);
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
c2_freep(&result);
|
||||
@@ -400,13 +395,11 @@ c2_lptr_t *c2_parse(c2_lptr_t **pcondlst, const char *pattern, void *data) {
|
||||
*/
|
||||
static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int level) {
|
||||
// Check for recursion levels
|
||||
if (level > C2_MAX_LEVELS) {
|
||||
if (level > C2_MAX_LEVELS)
|
||||
c2_error("Exceeded maximum recursion levels.");
|
||||
}
|
||||
|
||||
if (!pattern) {
|
||||
if (!pattern)
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Expected end character
|
||||
const char endchar = (offset ? ')' : '\0');
|
||||
@@ -435,20 +428,17 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
assert(elei <= 2);
|
||||
|
||||
// Jump over spaces
|
||||
if (isspace((unsigned char)pattern[offset])) {
|
||||
if (isspace((unsigned char)pattern[offset]))
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle end of group
|
||||
if (')' == pattern[offset]) {
|
||||
if (')' == pattern[offset])
|
||||
break;
|
||||
}
|
||||
|
||||
// Handle "!"
|
||||
if ('!' == pattern[offset]) {
|
||||
if (!next_expected) {
|
||||
if (!next_expected)
|
||||
c2_error("Unexpected \"!\".");
|
||||
}
|
||||
|
||||
neg = !neg;
|
||||
continue;
|
||||
@@ -456,9 +446,8 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
|
||||
// Handle AND and OR
|
||||
if ('&' == pattern[offset] || '|' == pattern[offset]) {
|
||||
if (next_expected) {
|
||||
if (next_expected)
|
||||
c2_error("Unexpected logical operator.");
|
||||
}
|
||||
|
||||
next_expected = true;
|
||||
if (!mstrncmp("&&", pattern + offset)) {
|
||||
@@ -467,17 +456,15 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
} else if (!mstrncmp("||", pattern + offset)) {
|
||||
ops[elei] = C2_B_OOR;
|
||||
++offset;
|
||||
} else {
|
||||
} else
|
||||
c2_error("Illegal logical operator.");
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parsing an element
|
||||
if (!next_expected) {
|
||||
if (!next_expected)
|
||||
c2_error("Unexpected expression.");
|
||||
}
|
||||
|
||||
assert(!elei || ops[elei]);
|
||||
|
||||
@@ -504,25 +491,21 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
|
||||
// It's a subgroup if it starts with '('
|
||||
if ('(' == pattern[offset]) {
|
||||
if ((offset = c2_parse_grp(pattern, offset + 1, pele, level + 1)) < 0) {
|
||||
if ((offset = c2_parse_grp(pattern, offset + 1, pele, level + 1)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
// Otherwise it's a leaf
|
||||
else {
|
||||
if ((offset = c2_parse_target(pattern, offset, pele)) < 0) {
|
||||
if ((offset = c2_parse_target(pattern, offset, pele)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
assert(!pele->isbranch && !c2_ptr_isempty(*pele));
|
||||
|
||||
if ((offset = c2_parse_op(pattern, offset, pele)) < 0) {
|
||||
if ((offset = c2_parse_op(pattern, offset, pele)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if ((offset = c2_parse_pattern(pattern, offset, pele)) < 0) {
|
||||
if ((offset = c2_parse_pattern(pattern, offset, pele)) < 0)
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
// Decrement offset -- we will increment it in loop update
|
||||
--offset;
|
||||
@@ -530,11 +513,10 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
// Apply negation
|
||||
if (neg) {
|
||||
neg = false;
|
||||
if (pele->isbranch) {
|
||||
if (pele->isbranch)
|
||||
pele->b->neg = !pele->b->neg;
|
||||
} else {
|
||||
else
|
||||
pele->l->neg = !pele->l->neg;
|
||||
}
|
||||
}
|
||||
|
||||
next_expected = false;
|
||||
@@ -543,12 +525,10 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
}
|
||||
|
||||
// Wrong end character?
|
||||
if (pattern[offset] && !endchar) {
|
||||
if (pattern[offset] && !endchar)
|
||||
c2_error("Expected end of string but found '%c'.", pattern[offset]);
|
||||
}
|
||||
if (!pattern[offset] && endchar) {
|
||||
if (!pattern[offset] && endchar)
|
||||
c2_error("Expected '%c' but found end of string.", endchar);
|
||||
}
|
||||
|
||||
// Handle end of group
|
||||
if (!elei) {
|
||||
@@ -564,9 +544,8 @@ static int c2_parse_grp(const char *pattern, int offset, c2_ptr_t *presult, int
|
||||
|
||||
*presult = eles[0];
|
||||
|
||||
if (')' == pattern[offset]) {
|
||||
if (')' == pattern[offset])
|
||||
++offset;
|
||||
}
|
||||
|
||||
return offset;
|
||||
|
||||
@@ -799,11 +778,11 @@ static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) {
|
||||
|
||||
// Parse operator
|
||||
while ('=' == pattern[offset] || '>' == pattern[offset] || '<' == pattern[offset]) {
|
||||
if ('=' == pattern[offset] && C2_L_OGT == pleaf->op) {
|
||||
if ('=' == pattern[offset] && C2_L_OGT == pleaf->op)
|
||||
pleaf->op = C2_L_OGTEQ;
|
||||
} else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op) {
|
||||
else if ('=' == pattern[offset] && C2_L_OLT == pleaf->op)
|
||||
pleaf->op = C2_L_OLTEQ;
|
||||
} else if (pleaf->op) {
|
||||
else if (pleaf->op) {
|
||||
c2_error("Duplicate operator.");
|
||||
} else {
|
||||
switch (pattern[offset]) {
|
||||
@@ -818,10 +797,9 @@ static int c2_parse_op(const char *pattern, int offset, c2_ptr_t *presult) {
|
||||
}
|
||||
|
||||
// Check for problems
|
||||
if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase)) {
|
||||
if (C2_L_OEQ != pleaf->op && (pleaf->match || pleaf->match_ignorecase))
|
||||
c2_error("Exists/greater-than/less-than operators cannot have a "
|
||||
"qualifier.");
|
||||
}
|
||||
|
||||
return offset;
|
||||
|
||||
@@ -913,10 +891,9 @@ static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult)
|
||||
char *pstr = NULL;
|
||||
long val = strtol(
|
||||
tstr, &pstr, ('o' == pattern[offset] ? 8 : 16));
|
||||
if (pstr != &tstr[2] || val <= 0) {
|
||||
if (pstr != &tstr[2] || val <= 0)
|
||||
c2_error("Invalid octal/hex escape "
|
||||
"sequence.");
|
||||
}
|
||||
*(ptptnstr++) = to_char_checked(val);
|
||||
offset += 2;
|
||||
break;
|
||||
@@ -927,9 +904,8 @@ static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult)
|
||||
*(ptptnstr++) = pattern[offset];
|
||||
}
|
||||
}
|
||||
if (!pattern[offset]) {
|
||||
if (!pattern[offset])
|
||||
c2_error("Premature end of pattern string.");
|
||||
}
|
||||
++offset;
|
||||
*ptptnstr = '\0';
|
||||
pleaf->ptnstr = strdup(tptnstr);
|
||||
@@ -938,32 +914,27 @@ static int c2_parse_pattern(const char *pattern, int offset, c2_ptr_t *presult)
|
||||
|
||||
C2H_SKIP_SPACES();
|
||||
|
||||
if (!pleaf->ptntype) {
|
||||
if (!pleaf->ptntype)
|
||||
c2_error("Invalid pattern type.");
|
||||
}
|
||||
|
||||
// Check if the type is correct
|
||||
if (!(((C2_L_TSTRING == pleaf->type || C2_L_TATOM == pleaf->type) &&
|
||||
C2_L_PTSTRING == pleaf->ptntype) ||
|
||||
((C2_L_TCARDINAL == pleaf->type || C2_L_TWINDOW == pleaf->type ||
|
||||
C2_L_TDRAWABLE == pleaf->type) &&
|
||||
C2_L_PTINT == pleaf->ptntype))) {
|
||||
C2_L_PTINT == pleaf->ptntype)))
|
||||
c2_error("Pattern type incompatible with target type.");
|
||||
}
|
||||
|
||||
if (C2_L_PTINT == pleaf->ptntype && pleaf->match) {
|
||||
if (C2_L_PTINT == pleaf->ptntype && pleaf->match)
|
||||
c2_error("Integer/boolean pattern cannot have operator qualifiers.");
|
||||
}
|
||||
|
||||
if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase) {
|
||||
if (C2_L_PTINT == pleaf->ptntype && pleaf->match_ignorecase)
|
||||
c2_error("Integer/boolean pattern cannot have flags.");
|
||||
}
|
||||
|
||||
if (C2_L_PTSTRING == pleaf->ptntype &&
|
||||
(C2_L_OGT == pleaf->op || C2_L_OGTEQ == pleaf->op || C2_L_OLT == pleaf->op ||
|
||||
C2_L_OLTEQ == pleaf->op)) {
|
||||
C2_L_OLTEQ == pleaf->op))
|
||||
c2_error("String pattern cannot have an arithmetic operator.");
|
||||
}
|
||||
|
||||
return offset;
|
||||
|
||||
@@ -991,7 +962,7 @@ static int c2_parse_legacy(const char *pattern, int offset, c2_ptr_t *presult) {
|
||||
|
||||
// Determine the pattern target
|
||||
#define TGTFILL(pdefid) \
|
||||
(pleaf->predef = (pdefid), pleaf->type = C2_PREDEFS[pdefid].type, \
|
||||
(pleaf->predef = pdefid, pleaf->type = C2_PREDEFS[pdefid].type, \
|
||||
pleaf->format = C2_PREDEFS[pdefid].format)
|
||||
switch (pattern[offset]) {
|
||||
case 'n': TGTFILL(C2_L_PNAME); break;
|
||||
@@ -1202,8 +1173,9 @@ c2_lptr_t *c2_free_lptr(c2_lptr_t *lp, c2_userdata_free f) {
|
||||
static const char *c2h_dump_str_tgt(const c2_l_t *pleaf) {
|
||||
if (pleaf->predef != C2_L_PUNDEFINED) {
|
||||
return C2_PREDEFS[pleaf->predef].name;
|
||||
} else {
|
||||
return pleaf->tgt;
|
||||
}
|
||||
return pleaf->tgt;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1336,7 +1308,7 @@ static xcb_atom_t c2_get_atom_type(const c2_l_t *pleaf) {
|
||||
case C2_L_TDRAWABLE: return XCB_ATOM_DRAWABLE;
|
||||
default: assert(0); break;
|
||||
}
|
||||
unreachable();
|
||||
unreachable;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1406,11 +1378,11 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
|
||||
int word_count = 1;
|
||||
if (pleaf->index < 0) {
|
||||
// Get length of property in 32-bit multiples
|
||||
auto prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom);
|
||||
auto prop_info = x_get_prop_info(ps->c, wid, pleaf->tgtatom);
|
||||
word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
|
||||
}
|
||||
winprop_t prop = x_get_prop_with_offset(
|
||||
&ps->c, wid, pleaf->tgtatom, idx, word_count,
|
||||
ps->c, wid, pleaf->tgtatom, idx, word_count,
|
||||
c2_get_atom_type(pleaf), pleaf->format);
|
||||
|
||||
ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
|
||||
@@ -1483,11 +1455,11 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
|
||||
int word_count = 1;
|
||||
if (pleaf->index < 0) {
|
||||
// Get length of property in 32-bit multiples
|
||||
auto prop_info = x_get_prop_info(&ps->c, wid, pleaf->tgtatom);
|
||||
auto prop_info = x_get_prop_info(ps->c, wid, pleaf->tgtatom);
|
||||
word_count = to_int_checked((prop_info.length + 4 - 1) / 4);
|
||||
}
|
||||
winprop_t prop = x_get_prop_with_offset(
|
||||
&ps->c, wid, pleaf->tgtatom, idx, word_count,
|
||||
ps->c, wid, pleaf->tgtatom, idx, word_count,
|
||||
c2_get_atom_type(pleaf), pleaf->format);
|
||||
|
||||
ntargets = (pleaf->index < 0 ? prop.nitems : min2(prop.nitems, 1));
|
||||
@@ -1498,7 +1470,7 @@ static inline void c2_match_once_leaf(session_t *ps, const struct managed_win *w
|
||||
xcb_atom_t atom = (xcb_atom_t)winprop_get_int(prop, i);
|
||||
if (atom) {
|
||||
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(
|
||||
ps->c.c, xcb_get_atom_name(ps->c.c, atom), NULL);
|
||||
ps->c, xcb_get_atom_name(ps->c, atom), NULL);
|
||||
if (reply) {
|
||||
targets[i] = targets_free_inner[i] = strndup(
|
||||
xcb_get_atom_name_name(reply),
|
||||
@@ -1627,9 +1599,8 @@ static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_p
|
||||
if (cond.isbranch) {
|
||||
const c2_b_t *pb = cond.b;
|
||||
|
||||
if (!pb) {
|
||||
if (!pb)
|
||||
return false;
|
||||
}
|
||||
|
||||
error = false;
|
||||
|
||||
@@ -1659,9 +1630,8 @@ static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_p
|
||||
else {
|
||||
const c2_l_t *pleaf = cond.l;
|
||||
|
||||
if (!pleaf) {
|
||||
if (!pleaf)
|
||||
return false;
|
||||
}
|
||||
|
||||
c2_match_once_leaf(ps, w, pleaf, &result, &error);
|
||||
|
||||
@@ -1681,13 +1651,11 @@ static bool c2_match_once(session_t *ps, const struct managed_win *w, const c2_p
|
||||
}
|
||||
|
||||
// Postprocess the result
|
||||
if (error) {
|
||||
if (error)
|
||||
result = false;
|
||||
}
|
||||
|
||||
if (cond.isbranch ? cond.b->neg : cond.l->neg) {
|
||||
if (cond.isbranch ? cond.b->neg : cond.l->neg)
|
||||
result = !result;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -1705,9 +1673,8 @@ bool c2_match(session_t *ps, const struct managed_win *w, const c2_lptr_t *condl
|
||||
// Then go through the whole linked list
|
||||
for (; condlst; condlst = condlst->next) {
|
||||
if (c2_match_once(ps, w, condlst->ptr)) {
|
||||
if (pdata) {
|
||||
if (pdata)
|
||||
*pdata = condlst->data;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
109
src/common.h
109
src/common.h
@@ -58,7 +58,6 @@
|
||||
#include "list.h"
|
||||
#include "region.h"
|
||||
#include "render.h"
|
||||
#include "statistics.h"
|
||||
#include "types.h"
|
||||
#include "utils.h"
|
||||
#include "win_defs.h"
|
||||
@@ -84,6 +83,18 @@ struct glx_session;
|
||||
struct atom;
|
||||
struct conv;
|
||||
|
||||
enum pending_reply_action {
|
||||
PENDING_REPLY_ACTION_IGNORE,
|
||||
PENDING_REPLY_ACTION_ABORT,
|
||||
PENDING_REPLY_ACTION_DEBUG_ABORT,
|
||||
};
|
||||
|
||||
typedef struct pending_reply {
|
||||
struct pending_reply *next;
|
||||
unsigned long sequence;
|
||||
enum pending_reply_action action;
|
||||
} pending_reply_t;
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
#ifdef DEBUG_GLX_DEBUG_CONTEXT
|
||||
typedef GLXContext (*f_glXCreateContextAttribsARB)(Display *dpy, GLXFBConfig config,
|
||||
@@ -139,6 +150,8 @@ typedef struct session {
|
||||
// === Event handlers ===
|
||||
/// ev_io for X connection
|
||||
ev_io xiow;
|
||||
/// Timer for checking DPMS power level
|
||||
ev_timer dpms_check_timer;
|
||||
/// Timeout for delayed unredirection.
|
||||
ev_timer unredir_timer;
|
||||
/// Timer for fading
|
||||
@@ -148,8 +161,6 @@ typedef struct session {
|
||||
/// Use an ev_idle callback for drawing
|
||||
/// So we only start drawing when events are processed
|
||||
ev_idle draw_idle;
|
||||
/// Use an ev_timer callback for drawing
|
||||
ev_timer draw_timer;
|
||||
/// Called every time we have timeouts or new data on socket,
|
||||
/// so we can be sure if xcb read from X socket at anytime during event
|
||||
/// handling, we will not left any event unhandled in the queue
|
||||
@@ -174,13 +185,27 @@ typedef struct session {
|
||||
struct shader_info *shaders;
|
||||
|
||||
// === Display related ===
|
||||
/// X connection
|
||||
struct x_connection c;
|
||||
/// Whether the X server is grabbed by us
|
||||
bool server_grabbed;
|
||||
/// Display in use.
|
||||
Display *dpy;
|
||||
/// Previous handler of X errors
|
||||
XErrorHandler previous_xerror_handler;
|
||||
/// Default screen.
|
||||
int scr;
|
||||
/// XCB connection.
|
||||
xcb_connection_t *c;
|
||||
/// Default visual.
|
||||
xcb_visualid_t vis;
|
||||
/// Default depth.
|
||||
int depth;
|
||||
/// Root window.
|
||||
xcb_window_t root;
|
||||
/// Height of root window.
|
||||
int root_height;
|
||||
/// Width of root window.
|
||||
int root_width;
|
||||
int root_height;
|
||||
// Damage of root window.
|
||||
/// X Composite overlay window.
|
||||
xcb_window_t overlay;
|
||||
/// The target window for debug mode
|
||||
@@ -216,22 +241,6 @@ typedef struct session {
|
||||
bool first_frame;
|
||||
/// Whether screen has been turned off
|
||||
bool screen_is_off;
|
||||
/// When last MSC event happened, in useconds.
|
||||
uint64_t last_msc_instant;
|
||||
/// The last MSC number
|
||||
uint64_t last_msc;
|
||||
/// The delay between when the last frame was scheduled to be rendered, and when
|
||||
/// the render actually started.
|
||||
uint64_t last_schedule_delay;
|
||||
/// When do we want our next frame to start rendering.
|
||||
uint64_t next_render;
|
||||
/// Whether we can perform frame pacing.
|
||||
bool frame_pacing;
|
||||
/// Vblank event scheduler
|
||||
struct vblank_scheduler *vblank_scheduler;
|
||||
|
||||
/// Render statistics
|
||||
struct render_statistics render_stats;
|
||||
|
||||
// === Operation related ===
|
||||
/// Flags related to the root window
|
||||
@@ -240,18 +249,8 @@ typedef struct session {
|
||||
options_t o;
|
||||
/// Whether we have hit unredirection timeout.
|
||||
bool tmout_unredir_hit;
|
||||
/// If the backend is busy. This means two things:
|
||||
/// 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
|
||||
/// 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
|
||||
/// back buffer to be become available again. In either case, we will be wasting
|
||||
/// time.
|
||||
bool backend_busy;
|
||||
/// Whether a render is queued. This generally means there are pending updates
|
||||
/// to the screen that's neither included in the current render, nor on the
|
||||
/// screen.
|
||||
bool render_queued;
|
||||
/// Whether we need to redraw the screen
|
||||
bool redraw_needed;
|
||||
|
||||
/// Cache a xfixes region so we don't need to allocate it every time.
|
||||
/// A workaround for yshui/picom#301
|
||||
@@ -292,6 +291,8 @@ typedef struct session {
|
||||
int size_expose;
|
||||
/// Index of the next free slot in <code>expose_rects</code>.
|
||||
int n_expose;
|
||||
/// Current desktop of display
|
||||
uint32_t cur_desktop;
|
||||
|
||||
// === Window related ===
|
||||
/// A hash table of all windows.
|
||||
@@ -372,8 +373,10 @@ typedef struct session {
|
||||
int glx_event;
|
||||
/// Error base number for X GLX extension.
|
||||
int glx_error;
|
||||
/// Information about monitors.
|
||||
struct x_monitors monitors;
|
||||
/// Number of X RandR monitors.
|
||||
int randr_nmonitors;
|
||||
/// X RandR monitor regions.
|
||||
region_t *randr_monitor_regs;
|
||||
/// Whether X Sync extension exists.
|
||||
bool xsync_exists;
|
||||
/// Event base number for X Sync extension.
|
||||
@@ -471,7 +474,7 @@ static inline struct timespec get_time_timespec(void) {
|
||||
* Return the painting target window.
|
||||
*/
|
||||
static inline xcb_window_t get_tgt_window(session_t *ps) {
|
||||
return ps->overlay != XCB_NONE ? ps->overlay : ps->c.screen_info->root;
|
||||
return ps->overlay != XCB_NONE ? ps->overlay : ps->root;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -481,6 +484,35 @@ static inline bool bkend_use_glx(session_t *ps) {
|
||||
return BKEND_GLX == ps->o.backend || BKEND_XR_GLX_HYBRID == ps->o.backend;
|
||||
}
|
||||
|
||||
static void
|
||||
set_reply_action(session_t *ps, uint32_t sequence, enum pending_reply_action action) {
|
||||
auto i = cmalloc(pending_reply_t);
|
||||
if (!i) {
|
||||
abort();
|
||||
}
|
||||
|
||||
i->sequence = sequence;
|
||||
i->next = 0;
|
||||
i->action = action;
|
||||
*ps->pending_reply_tail = i;
|
||||
ps->pending_reply_tail = &i->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore X errors caused by given X request.
|
||||
*/
|
||||
static inline void set_ignore_cookie(session_t *ps, xcb_void_cookie_t cookie) {
|
||||
if (ps->o.show_all_xerrors) {
|
||||
return;
|
||||
}
|
||||
|
||||
set_reply_action(ps, cookie.sequence, PENDING_REPLY_ACTION_IGNORE);
|
||||
}
|
||||
|
||||
static inline void set_cant_fail_cookie(session_t *ps, xcb_void_cookie_t cookie) {
|
||||
set_reply_action(ps, cookie.sequence, PENDING_REPLY_ACTION_ABORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a window has a specific property.
|
||||
*
|
||||
@@ -491,8 +523,7 @@ static inline bool bkend_use_glx(session_t *ps) {
|
||||
*/
|
||||
static inline bool wid_has_prop(const session_t *ps, xcb_window_t w, xcb_atom_t atom) {
|
||||
auto r = xcb_get_property_reply(
|
||||
ps->c.c,
|
||||
xcb_get_property(ps->c.c, 0, w, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, 0), NULL);
|
||||
ps->c, xcb_get_property(ps->c, 0, w, atom, XCB_GET_PROPERTY_TYPE_ANY, 0, 0), NULL);
|
||||
if (!r) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
#endif
|
||||
|
||||
// clang-format off
|
||||
#if __STDC_VERSION__ <= 201710L
|
||||
#define auto __auto_type
|
||||
#endif
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#define likely_if(x) if (likely(x))
|
||||
@@ -103,12 +101,10 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef unreachable
|
||||
# if defined(__GNUC__) || defined(__clang__)
|
||||
# define unreachable() __builtin_unreachable()
|
||||
# else
|
||||
# define unreachable() do {} while(0)
|
||||
# endif
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
# define unreachable __builtin_unreachable()
|
||||
#else
|
||||
# define unreachable do {} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef __has_include
|
||||
|
||||
124
src/config.c
124
src/config.c
@@ -8,8 +8,6 @@
|
||||
#include <limits.h>
|
||||
#include <math.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -135,9 +133,8 @@ bool parse_long(const char *s, long *dest) {
|
||||
log_error("Invalid number: %s", s);
|
||||
return false;
|
||||
}
|
||||
while (isspace((unsigned char)*endptr)) {
|
||||
while (isspace((unsigned char)*endptr))
|
||||
++endptr;
|
||||
}
|
||||
if (*endptr) {
|
||||
log_error("Trailing characters: %s", s);
|
||||
return false;
|
||||
@@ -185,19 +182,20 @@ const char *parse_readnum(const char *src, double *dest) {
|
||||
}
|
||||
|
||||
enum blur_method parse_blur_method(const char *src) {
|
||||
if (strcmp(src, "box") == 0) {
|
||||
return BLUR_METHOD_BOX;
|
||||
}
|
||||
if (strcmp(src, "dual_kawase") == 0) {
|
||||
return BLUR_METHOD_DUAL_KAWASE;
|
||||
}
|
||||
if (strcmp(src, "gaussian") == 0) {
|
||||
return BLUR_METHOD_GAUSSIAN;
|
||||
}
|
||||
if (strcmp(src, "kernel") == 0) {
|
||||
return BLUR_METHOD_KERNEL;
|
||||
}
|
||||
if (strcmp(src, "none") == 0) {
|
||||
} else if (strcmp(src, "box") == 0) {
|
||||
return BLUR_METHOD_BOX;
|
||||
} else if (strcmp(src, "gaussian") == 0) {
|
||||
return BLUR_METHOD_GAUSSIAN;
|
||||
} else if (strcmp(src, "dual_kawase") == 0) {
|
||||
return BLUR_METHOD_DUAL_KAWASE;
|
||||
} else if (strcmp(src, "kawase") == 0) {
|
||||
log_warn("Blur method 'kawase' has been renamed to 'dual_kawase'. "
|
||||
"Interpreted as 'dual_kawase', but this will stop working "
|
||||
"soon.");
|
||||
return BLUR_METHOD_DUAL_KAWASE;
|
||||
} else if (strcmp(src, "none") == 0) {
|
||||
return BLUR_METHOD_NONE;
|
||||
}
|
||||
return BLUR_METHOD_INVALID;
|
||||
@@ -218,14 +216,12 @@ conv *parse_blur_kern(const char *src, const char **endptr, bool *hasneg) {
|
||||
|
||||
// Get matrix width and height
|
||||
double val = 0.0;
|
||||
if (src == (pc = parse_readnum(src, &val))) {
|
||||
if (src == (pc = parse_readnum(src, &val)))
|
||||
goto err1;
|
||||
}
|
||||
src = pc;
|
||||
width = (int)val;
|
||||
if (src == (pc = parse_readnum(src, &val))) {
|
||||
if (src == (pc = parse_readnum(src, &val)))
|
||||
goto err1;
|
||||
}
|
||||
src = pc;
|
||||
height = (int)val;
|
||||
|
||||
@@ -238,10 +234,9 @@ conv *parse_blur_kern(const char *src, const char **endptr, bool *hasneg) {
|
||||
log_error("Blur kernel width/height must be odd.");
|
||||
goto err1;
|
||||
}
|
||||
if (width > 16 || height > 16) {
|
||||
if (width > 16 || height > 16)
|
||||
log_warn("Blur kernel width/height too large, may slow down"
|
||||
"rendering, and/or consume lots of memory");
|
||||
}
|
||||
|
||||
// Allocate memory
|
||||
conv *matrix = cvalloc(sizeof(conv) + (size_t)(width * height) * sizeof(double));
|
||||
@@ -367,9 +362,8 @@ struct conv **parse_blur_kern_lst(const char *src, bool *hasneg, int *count) {
|
||||
*hasneg = false;
|
||||
for (unsigned int i = 0;
|
||||
i < sizeof(CONV_KERN_PREDEF) / sizeof(CONV_KERN_PREDEF[0]); ++i) {
|
||||
if (!strcmp(CONV_KERN_PREDEF[i].name, src)) {
|
||||
if (!strcmp(CONV_KERN_PREDEF[i].name, src))
|
||||
return parse_blur_kern_lst(CONV_KERN_PREDEF[i].kern_str, hasneg, count);
|
||||
}
|
||||
}
|
||||
|
||||
int nkernels = 1;
|
||||
@@ -617,82 +611,6 @@ char *locate_auxiliary_file(const char *scope, const char *path, const char *inc
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct debug_options_entry {
|
||||
const char *name;
|
||||
const char **choices;
|
||||
size_t offset;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
const char *vblank_scheduler_str[] = {
|
||||
[VBLANK_SCHEDULER_PRESENT] = "present",
|
||||
[VBLANK_SCHEDULER_SGI_VIDEO_SYNC] = "sgi_video_sync",
|
||||
[LAST_VBLANK_SCHEDULER] = NULL
|
||||
};
|
||||
static const struct debug_options_entry debug_options_entries[] = {
|
||||
{"smart_frame_pacing", NULL, offsetof(struct debug_options, smart_frame_pacing)},
|
||||
{"force_vblank_sched", vblank_scheduler_str, offsetof(struct debug_options, force_vblank_scheduler)},
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
void parse_debug_option_single(char *setting, struct debug_options *debug_options) {
|
||||
char *equal = strchr(setting, '=');
|
||||
size_t name_len = equal ? (size_t)(equal - setting) : strlen(setting);
|
||||
for (size_t i = 0; i < ARR_SIZE(debug_options_entries); i++) {
|
||||
if (strncmp(setting, debug_options_entries[i].name, name_len) != 0) {
|
||||
continue;
|
||||
}
|
||||
if (debug_options_entries[i].name[name_len] != '\0') {
|
||||
continue;
|
||||
}
|
||||
auto value = (int *)((void *)debug_options + debug_options_entries[i].offset);
|
||||
if (equal) {
|
||||
const char *const arg = equal + 1;
|
||||
if (debug_options_entries[i].choices != NULL) {
|
||||
for (size_t j = 0; debug_options_entries[i].choices[j]; j++) {
|
||||
if (strcmp(arg, debug_options_entries[i].choices[j]) ==
|
||||
0) {
|
||||
*value = (int)j;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!parse_int(arg, value)) {
|
||||
log_error("Invalid value for debug option %s: %s, it "
|
||||
"will be ignored.",
|
||||
debug_options_entries[i].name, arg);
|
||||
}
|
||||
} else if (debug_options_entries[i].choices == NULL) {
|
||||
*value = 1;
|
||||
} else {
|
||||
log_error(
|
||||
"Missing value for debug option %s, it will be ignored.", setting);
|
||||
}
|
||||
return;
|
||||
}
|
||||
log_error("Invalid debug option: %s", setting);
|
||||
}
|
||||
|
||||
/// Parse debug options from environment variable `PICOM_DEBUG`.
|
||||
void parse_debug_options(struct debug_options *debug_options) {
|
||||
const char *debug = getenv("PICOM_DEBUG");
|
||||
const struct debug_options default_debug_options = {
|
||||
.force_vblank_scheduler = LAST_VBLANK_SCHEDULER,
|
||||
};
|
||||
|
||||
*debug_options = default_debug_options;
|
||||
if (!debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
scoped_charp debug_copy = strdup(debug);
|
||||
char *tmp, *needle = strtok_r(debug_copy, ";", &tmp);
|
||||
while (needle) {
|
||||
parse_debug_option_single(needle, debug_options);
|
||||
needle = strtok_r(NULL, ";", &tmp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a list of window shader rules.
|
||||
*/
|
||||
@@ -737,13 +655,11 @@ bool parse_rule_window_shader(c2_lptr_t **res, const char *src, const char *incl
|
||||
* Add a pattern to a condition linked list.
|
||||
*/
|
||||
bool condlst_add(c2_lptr_t **pcondlst, const char *pattern) {
|
||||
if (!pattern) {
|
||||
if (!pattern)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!c2_parse(pcondlst, pattern, NULL)) {
|
||||
if (!c2_parse(pcondlst, pattern, NULL))
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -864,7 +780,6 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||
.logpath = NULL,
|
||||
|
||||
.use_damage = true,
|
||||
.no_frame_pacing = false,
|
||||
|
||||
.shadow_red = 0.0,
|
||||
.shadow_green = 0.0,
|
||||
@@ -946,6 +861,5 @@ char *parse_config(options_t *opt, const char *config_file, bool *shadow_enable,
|
||||
(void)hasneg;
|
||||
(void)winopt_mask;
|
||||
#endif
|
||||
parse_debug_options(&opt->debug_options);
|
||||
return ret;
|
||||
}
|
||||
|
||||
25
src/config.h
25
src/config.h
@@ -93,27 +93,6 @@ enum blur_method {
|
||||
|
||||
typedef struct _c2_lptr c2_lptr_t;
|
||||
|
||||
enum vblank_scheduler_type {
|
||||
/// X Present extension based vblank events
|
||||
VBLANK_SCHEDULER_PRESENT,
|
||||
/// GLX_SGI_video_sync based vblank events
|
||||
VBLANK_SCHEDULER_SGI_VIDEO_SYNC,
|
||||
/// An invalid scheduler, served as a scheduler count, and
|
||||
/// as a sentinel value.
|
||||
LAST_VBLANK_SCHEDULER,
|
||||
};
|
||||
|
||||
extern const char *vblank_scheduler_str[];
|
||||
|
||||
/// Internal, private options for debugging and development use.
|
||||
struct debug_options {
|
||||
/// Try to reduce frame latency by using vblank interval and render time
|
||||
/// estimates. Right now it's not working well across drivers.
|
||||
int smart_frame_pacing;
|
||||
/// Override the vblank scheduler chosen by the compositor.
|
||||
int force_vblank_scheduler;
|
||||
};
|
||||
|
||||
/// Structure representing all options.
|
||||
typedef struct options {
|
||||
// === Debugging ===
|
||||
@@ -181,8 +160,6 @@ typedef struct options {
|
||||
bool vsync_use_glfinish;
|
||||
/// Whether use damage information to help limit the area to paint
|
||||
bool use_damage;
|
||||
/// Disable frame pacing
|
||||
bool no_frame_pacing;
|
||||
|
||||
// === Shadow ===
|
||||
/// Red, green and blue tone of the shadow.
|
||||
@@ -337,8 +314,6 @@ typedef struct options {
|
||||
c2_lptr_t *transparent_clipping_blacklist;
|
||||
|
||||
bool dithered_present;
|
||||
|
||||
struct debug_options debug_options;
|
||||
} options_t;
|
||||
|
||||
extern const char *const BACKEND_STRS[NUM_BKEND + 1];
|
||||
|
||||
@@ -73,9 +73,8 @@ FILE *open_config_file(const char *cpath, char **ppath) {
|
||||
|
||||
if (cpath) {
|
||||
FILE *ret = fopen(cpath, "r");
|
||||
if (ret && ppath) {
|
||||
if (ret && ppath)
|
||||
*ppath = strdup(cpath);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -125,10 +124,9 @@ void parse_cfg_condlst(const config_t *pcfg, c2_lptr_t **pcondlst, const char *n
|
||||
// Parse an array of options
|
||||
if (config_setting_is_array(setting)) {
|
||||
int i = config_setting_length(setting);
|
||||
while (i--) {
|
||||
while (i--)
|
||||
condlst_add(pcondlst,
|
||||
config_setting_get_string_elem(setting, i));
|
||||
}
|
||||
}
|
||||
// Treat it as a single pattern if it's a string
|
||||
else if (CONFIG_TYPE_STRING == config_setting_type(setting)) {
|
||||
@@ -147,22 +145,18 @@ parse_cfg_condlst_corner(options_t *opt, const config_t *pcfg, const char *name)
|
||||
// Parse an array of options
|
||||
if (config_setting_is_array(setting)) {
|
||||
int i = config_setting_length(setting);
|
||||
while (i--) {
|
||||
while (i--)
|
||||
if (!parse_numeric_window_rule(
|
||||
&opt->corner_radius_rules,
|
||||
config_setting_get_string_elem(setting, i), 0,
|
||||
INT_MAX)) {
|
||||
config_setting_get_string_elem(setting, i), 0, INT_MAX))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Treat it as a single pattern if it's a string
|
||||
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
|
||||
if (!parse_numeric_window_rule(&opt->corner_radius_rules,
|
||||
config_setting_get_string(setting),
|
||||
0, INT_MAX)) {
|
||||
0, INT_MAX))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -177,21 +171,17 @@ parse_cfg_condlst_opct(options_t *opt, const config_t *pcfg, const char *name) {
|
||||
// Parse an array of options
|
||||
if (config_setting_is_array(setting)) {
|
||||
int i = config_setting_length(setting);
|
||||
while (i--) {
|
||||
while (i--)
|
||||
if (!parse_numeric_window_rule(
|
||||
&opt->opacity_rules,
|
||||
config_setting_get_string_elem(setting, i), 0, 100)) {
|
||||
config_setting_get_string_elem(setting, i), 0, 100))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Treat it as a single pattern if it's a string
|
||||
else if (config_setting_type(setting) == CONFIG_TYPE_STRING) {
|
||||
if (!parse_numeric_window_rule(&opt->opacity_rules,
|
||||
config_setting_get_string(setting),
|
||||
0, 100)) {
|
||||
if (!parse_numeric_window_rule(
|
||||
&opt->opacity_rules, config_setting_get_string(setting), 0, 100))
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -382,9 +372,6 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
// --corner-radius-rules
|
||||
parse_cfg_condlst_corner(opt, &cfg, "corner-radius-rules");
|
||||
|
||||
// --no-frame-pacing
|
||||
lcfg_lookup_bool(&cfg, "no-frame-pacing", &opt->no_frame_pacing);
|
||||
|
||||
// -e (frame_opacity)
|
||||
config_lookup_float(&cfg, "frame-opacity", &opt->frame_opacity);
|
||||
// -c (shadow_enable)
|
||||
@@ -400,9 +387,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
winopt_mask[WINTYPE_POPUP_MENU].opacity = true;
|
||||
}
|
||||
// -f (fading_enable)
|
||||
if (config_lookup_bool(&cfg, "fading", &ival)) {
|
||||
if (config_lookup_bool(&cfg, "fading", &ival))
|
||||
*fading_enable = ival;
|
||||
}
|
||||
// --no-fading-open-close
|
||||
lcfg_lookup_bool(&cfg, "no-fading-openclose", &opt->no_fading_openclose);
|
||||
// --no-fading-destroyed-argb
|
||||
@@ -422,9 +408,8 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
opt->shadow_blue = rgb.blue;
|
||||
}
|
||||
// --shadow-exclude-reg
|
||||
if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval)) {
|
||||
if (config_lookup_string(&cfg, "shadow-exclude-reg", &sval))
|
||||
opt->shadow_exclude_reg_str = strdup(sval);
|
||||
}
|
||||
// --inactive-opacity-override
|
||||
lcfg_lookup_bool(&cfg, "inactive-opacity-override", &opt->inactive_opacity_override);
|
||||
// --inactive-dim
|
||||
@@ -686,10 +671,9 @@ char *parse_config_libconfig(options_t *opt, const char *config_file, bool *shad
|
||||
// --xrender-sync-fence
|
||||
lcfg_lookup_bool(&cfg, "xrender-sync-fence", &opt->xrender_sync_fence);
|
||||
|
||||
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval)) {
|
||||
if (lcfg_lookup_bool(&cfg, "clear-shadow", &bval))
|
||||
log_warn("\"clear-shadow\" is removed as an option, and is always"
|
||||
" enabled now. Consider removing it from your config file");
|
||||
}
|
||||
|
||||
config_setting_t *blur_cfg = config_lookup(&cfg, "blur");
|
||||
if (blur_cfg) {
|
||||
|
||||
85
src/dbus.c
85
src/dbus.c
@@ -253,9 +253,8 @@ static dbus_bool_t cdbus_callback_add_timeout(DBusTimeout *timeout, void *data)
|
||||
t->t = timeout;
|
||||
dbus_timeout_set_data(timeout, t, NULL);
|
||||
|
||||
if (dbus_timeout_get_enabled(timeout)) {
|
||||
if (dbus_timeout_get_enabled(timeout))
|
||||
ev_timer_start(ps->loop, &t->w);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -303,12 +302,10 @@ typedef struct ev_dbus_io {
|
||||
void cdbus_io_callback(EV_P attr_unused, ev_io *w, int revents) {
|
||||
ev_dbus_io *dw = (void *)w;
|
||||
DBusWatchFlags flags = 0;
|
||||
if (revents & EV_READ) {
|
||||
if (revents & EV_READ)
|
||||
flags |= DBUS_WATCH_READABLE;
|
||||
}
|
||||
if (revents & EV_WRITE) {
|
||||
if (revents & EV_WRITE)
|
||||
flags |= DBUS_WATCH_WRITABLE;
|
||||
}
|
||||
dbus_watch_handle(dw->dw, flags);
|
||||
while (dbus_connection_dispatch(dw->cd->dbus_conn) != DBUS_DISPATCH_COMPLETE)
|
||||
;
|
||||
@@ -320,12 +317,10 @@ void cdbus_io_callback(EV_P attr_unused, ev_io *w, int revents) {
|
||||
static inline int cdbus_get_watch_cond(DBusWatch *watch) {
|
||||
const unsigned flags = dbus_watch_get_flags(watch);
|
||||
int condition = 0;
|
||||
if (flags & DBUS_WATCH_READABLE) {
|
||||
if (flags & DBUS_WATCH_READABLE)
|
||||
condition |= EV_READ;
|
||||
}
|
||||
if (flags & DBUS_WATCH_WRITABLE) {
|
||||
if (flags & DBUS_WATCH_WRITABLE)
|
||||
condition |= EV_WRITE;
|
||||
}
|
||||
|
||||
return condition;
|
||||
}
|
||||
@@ -343,9 +338,8 @@ static dbus_bool_t cdbus_callback_add_watch(DBusWatch *watch, void *data) {
|
||||
cdbus_get_watch_cond(watch));
|
||||
|
||||
// Leave disabled watches alone
|
||||
if (dbus_watch_get_enabled(watch)) {
|
||||
if (dbus_watch_get_enabled(watch))
|
||||
ev_io_start(ps->loop, &w->w);
|
||||
}
|
||||
|
||||
dbus_watch_set_data(watch, w, NULL);
|
||||
|
||||
@@ -369,11 +363,10 @@ static void cdbus_callback_remove_watch(DBusWatch *watch, void *data) {
|
||||
static void cdbus_callback_watch_toggled(DBusWatch *watch, void *data) {
|
||||
session_t *ps = data;
|
||||
ev_io *w = dbus_watch_get_data(watch);
|
||||
if (dbus_watch_get_enabled(watch)) {
|
||||
if (dbus_watch_get_enabled(watch))
|
||||
ev_io_start(ps->loop, w);
|
||||
} else {
|
||||
else
|
||||
ev_io_stop(ps->loop, w);
|
||||
}
|
||||
}
|
||||
|
||||
///@}
|
||||
@@ -520,9 +513,8 @@ static bool cdbus_apdarg_enum(session_t *ps attr_unused, DBusMessage *msg, const
|
||||
static bool
|
||||
cdbus_apdarg_string(session_t *ps attr_unused, DBusMessage *msg, const void *data) {
|
||||
const char *str = data;
|
||||
if (!str) {
|
||||
if (!str)
|
||||
str = "";
|
||||
}
|
||||
|
||||
if (!dbus_message_append_args(msg, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID)) {
|
||||
log_error("Failed to append argument.");
|
||||
@@ -1058,36 +1050,32 @@ static bool cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
|
||||
|
||||
if (!strcmp("shadow_force", target)) {
|
||||
cdbus_enum_t val = UNSET;
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
|
||||
return false;
|
||||
}
|
||||
win_set_shadow_force(ps, w, val);
|
||||
goto cdbus_process_win_set_success;
|
||||
}
|
||||
|
||||
if (!strcmp("fade_force", target)) {
|
||||
cdbus_enum_t val = UNSET;
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
|
||||
return false;
|
||||
}
|
||||
win_set_fade_force(w, val);
|
||||
goto cdbus_process_win_set_success;
|
||||
}
|
||||
|
||||
if (!strcmp("focused_force", target)) {
|
||||
cdbus_enum_t val = UNSET;
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
|
||||
return false;
|
||||
}
|
||||
win_set_focused_force(ps, w, val);
|
||||
goto cdbus_process_win_set_success;
|
||||
}
|
||||
|
||||
if (!strcmp("invert_color_force", target)) {
|
||||
cdbus_enum_t val = UNSET;
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 2, CDBUS_TYPE_ENUM, &val))
|
||||
return false;
|
||||
}
|
||||
win_set_invert_color_force(ps, w, val);
|
||||
goto cdbus_process_win_set_success;
|
||||
}
|
||||
@@ -1099,9 +1087,8 @@ static bool cdbus_process_win_set(session_t *ps, DBusMessage *msg) {
|
||||
return true;
|
||||
|
||||
cdbus_process_win_set_success:
|
||||
if (!dbus_message_get_no_reply(msg)) {
|
||||
if (!dbus_message_get_no_reply(msg))
|
||||
cdbus_reply_bool(ps, msg, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1111,18 +1098,16 @@ cdbus_process_win_set_success:
|
||||
static bool cdbus_process_find_win(session_t *ps, DBusMessage *msg) {
|
||||
const char *target = NULL;
|
||||
|
||||
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) {
|
||||
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_window_t wid = XCB_NONE;
|
||||
|
||||
// Find window by client window
|
||||
if (!strcmp("client", target)) {
|
||||
cdbus_window_t client = XCB_NONE;
|
||||
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client)) {
|
||||
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client))
|
||||
return false;
|
||||
}
|
||||
auto w = find_toplevel(ps, client);
|
||||
if (w) {
|
||||
wid = w->base.id;
|
||||
@@ -1151,9 +1136,8 @@ static bool cdbus_process_find_win(session_t *ps, DBusMessage *msg) {
|
||||
static bool cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
|
||||
const char *target = NULL;
|
||||
|
||||
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) {
|
||||
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
|
||||
return false;
|
||||
}
|
||||
|
||||
#define cdbus_m_opts_get_do(tgt, apdarg_func) \
|
||||
if (!strcmp(#tgt, target)) { \
|
||||
@@ -1181,7 +1165,7 @@ static bool cdbus_process_opts_get(session_t *ps, DBusMessage *msg) {
|
||||
|
||||
// display
|
||||
if (!strcmp("display", target)) {
|
||||
cdbus_reply_string(ps, msg, DisplayString(ps->c.dpy));
|
||||
cdbus_reply_string(ps, msg, DisplayString(ps->dpy));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1262,9 +1246,8 @@ void queue_redraw(session_t *ps);
|
||||
static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
const char *target = NULL;
|
||||
|
||||
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target)) {
|
||||
if (!cdbus_msg_get_arg(msg, 0, DBUS_TYPE_STRING, &target))
|
||||
return false;
|
||||
}
|
||||
|
||||
#define cdbus_m_opts_set_do(tgt, type, real_type) \
|
||||
if (!strcmp(#tgt, target)) { \
|
||||
@@ -1291,9 +1274,8 @@ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
// fade_in_step
|
||||
if (!strcmp("fade_in_step", target)) {
|
||||
double val = 0.0;
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
|
||||
return false;
|
||||
}
|
||||
ps->o.fade_in_step = normalize_d(val);
|
||||
goto cdbus_process_opts_set_success;
|
||||
}
|
||||
@@ -1301,9 +1283,8 @@ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
// fade_out_step
|
||||
if (!strcmp("fade_out_step", target)) {
|
||||
double val = 0.0;
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_DOUBLE, &val))
|
||||
return false;
|
||||
}
|
||||
ps->o.fade_out_step = normalize_d(val);
|
||||
goto cdbus_process_opts_set_success;
|
||||
}
|
||||
@@ -1311,9 +1292,8 @@ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
// no_fading_openclose
|
||||
if (!strcmp("no_fading_openclose", target)) {
|
||||
dbus_bool_t val = FALSE;
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
|
||||
return false;
|
||||
}
|
||||
opts_set_no_fading_openclose(ps, val);
|
||||
goto cdbus_process_opts_set_success;
|
||||
}
|
||||
@@ -1321,9 +1301,8 @@ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
// unredir_if_possible
|
||||
if (!strcmp("unredir_if_possible", target)) {
|
||||
dbus_bool_t val = FALSE;
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 1, DBUS_TYPE_BOOLEAN, &val))
|
||||
return false;
|
||||
}
|
||||
if (ps->o.unredir_if_possible != val) {
|
||||
ps->o.unredir_if_possible = val;
|
||||
queue_redraw(ps);
|
||||
@@ -1344,9 +1323,8 @@ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
// redirected_force
|
||||
if (!strcmp("redirected_force", target)) {
|
||||
cdbus_enum_t val = UNSET;
|
||||
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val)) {
|
||||
if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_ENUM, &val))
|
||||
return false;
|
||||
}
|
||||
ps->o.redirected_force = val;
|
||||
force_repaint(ps);
|
||||
goto cdbus_process_opts_set_success;
|
||||
@@ -1363,9 +1341,8 @@ static bool cdbus_process_opts_set(session_t *ps, DBusMessage *msg) {
|
||||
return true;
|
||||
|
||||
cdbus_process_opts_set_success:
|
||||
if (!dbus_message_get_no_reply(msg)) {
|
||||
if (!dbus_message_get_no_reply(msg))
|
||||
cdbus_reply_bool(ps, msg, true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1535,15 +1512,13 @@ cdbus_process(DBusConnection *c attr_unused, DBusMessage *msg, void *ud) {
|
||||
if (cdbus_m_ismethod("reset")) {
|
||||
log_info("picom is resetting...");
|
||||
ev_break(ps->loop, EVBREAK_ALL);
|
||||
if (!dbus_message_get_no_reply(msg)) {
|
||||
if (!dbus_message_get_no_reply(msg))
|
||||
cdbus_reply_bool(ps, msg, true);
|
||||
}
|
||||
handled = true;
|
||||
} else if (cdbus_m_ismethod("repaint")) {
|
||||
force_repaint(ps);
|
||||
if (!dbus_message_get_no_reply(msg)) {
|
||||
if (!dbus_message_get_no_reply(msg))
|
||||
cdbus_reply_bool(ps, msg, true);
|
||||
}
|
||||
handled = true;
|
||||
} else if (cdbus_m_ismethod("list_win")) {
|
||||
handled = cdbus_process_list_win(ps, msg);
|
||||
@@ -1591,9 +1566,8 @@ cdbus_process(DBusConnection *c attr_unused, DBusMessage *msg, void *ud) {
|
||||
dbus_message_get_member(msg));
|
||||
}
|
||||
if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) &&
|
||||
!dbus_message_get_no_reply(msg)) {
|
||||
!dbus_message_get_no_reply(msg))
|
||||
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S);
|
||||
}
|
||||
handled = true;
|
||||
}
|
||||
|
||||
@@ -1619,9 +1593,8 @@ cdbus_process_windows(DBusConnection *c attr_unused, DBusMessage *msg, void *ud)
|
||||
const char *last_segment = strrchr(path, '/');
|
||||
if (last_segment == NULL) {
|
||||
if (DBUS_MESSAGE_TYPE_METHOD_CALL == dbus_message_get_type(msg) &&
|
||||
!dbus_message_get_no_reply(msg)) {
|
||||
!dbus_message_get_no_reply(msg))
|
||||
cdbus_reply_err(ps, msg, CDBUS_ERROR_BADMSG, CDBUS_ERROR_BADMSG_S);
|
||||
}
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
}
|
||||
bool is_root = strncmp(last_segment, "/windows", 8) == 0;
|
||||
|
||||
@@ -39,7 +39,7 @@ void print_diagnostics(session_t *ps, const char *config_file, bool compositor_r
|
||||
for (int i = 0; i < NUM_BKEND; i++) {
|
||||
if (backend_list[i] && backend_list[i]->diagnostics) {
|
||||
printf("\n### Backend: %s\n\n", BACKEND_STRS[i]);
|
||||
auto data = backend_list[i]->init(ps, session_get_target_window(ps));
|
||||
auto data = backend_list[i]->init(ps);
|
||||
if (!data) {
|
||||
printf(" Cannot initialize this backend\n");
|
||||
} else {
|
||||
|
||||
92
src/event.c
92
src/event.c
@@ -7,7 +7,6 @@
|
||||
#include <X11/extensions/sync.h>
|
||||
#include <xcb/damage.h>
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/xcb_event.h>
|
||||
|
||||
#include "atom.h"
|
||||
#include "common.h"
|
||||
@@ -57,7 +56,7 @@ static inline const char *ev_window_name(session_t *ps, xcb_window_t wid) {
|
||||
char *name = "";
|
||||
if (wid) {
|
||||
name = "(Failed to get title)";
|
||||
if (ps->c.screen_info->root == wid) {
|
||||
if (ps->root == wid) {
|
||||
name = "(Root window)";
|
||||
} else if (ps->overlay == wid) {
|
||||
name = "(Overlay)";
|
||||
@@ -107,7 +106,7 @@ static inline xcb_window_t attr_pure ev_window(session_t *ps, xcb_generic_event_
|
||||
|
||||
static inline const char *ev_name(session_t *ps, xcb_generic_event_t *ev) {
|
||||
static char buf[128];
|
||||
switch (XCB_EVENT_RESPONSE_TYPE(ev)) {
|
||||
switch (ev->response_type & 0x7f) {
|
||||
CASESTRRET(FocusIn);
|
||||
CASESTRRET(FocusOut);
|
||||
CASESTRRET(CreateNotify);
|
||||
@@ -184,7 +183,7 @@ static inline void ev_focus_out(session_t *ps, xcb_focus_out_event_t *ev) {
|
||||
}
|
||||
|
||||
static inline void ev_create_notify(session_t *ps, xcb_create_notify_event_t *ev) {
|
||||
if (ev->parent == ps->c.screen_info->root) {
|
||||
if (ev->parent == ps->root) {
|
||||
add_win_top(ps, ev->window);
|
||||
}
|
||||
}
|
||||
@@ -239,7 +238,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||
}
|
||||
|
||||
// Recalculate which monitor this window is on
|
||||
win_update_monitor(&ps->monitors, mw);
|
||||
win_update_monitor(ps->randr_nmonitors, ps->randr_monitor_regs, mw);
|
||||
}
|
||||
|
||||
// override_redirect flag cannot be changed after window creation, as far
|
||||
@@ -250,7 +249,7 @@ static void configure_win(session_t *ps, xcb_configure_notify_event_t *ce) {
|
||||
static inline void ev_configure_notify(session_t *ps, xcb_configure_notify_event_t *ev) {
|
||||
log_debug("{ send_event: %d, id: %#010x, above: %#010x, override_redirect: %d }",
|
||||
ev->event, ev->window, ev->above_sibling, ev->override_redirect);
|
||||
if (ev->window == ps->c.screen_info->root) {
|
||||
if (ev->window == ps->root) {
|
||||
set_root_flags(ps, ROOT_FLAGS_CONFIGURED);
|
||||
} else {
|
||||
configure_win(ps, ev);
|
||||
@@ -284,8 +283,8 @@ static inline void ev_map_notify(session_t *ps, xcb_map_notify_event_t *ev) {
|
||||
// in redirected state.
|
||||
if (ps->overlay && ev->window == ps->overlay && !ps->redirected) {
|
||||
log_debug("Overlay is mapped while we are not redirected");
|
||||
auto e = xcb_request_check(
|
||||
ps->c.c, xcb_unmap_window_checked(ps->c.c, ps->overlay));
|
||||
auto e =
|
||||
xcb_request_check(ps->c, xcb_unmap_window_checked(ps->c, ps->overlay));
|
||||
if (e) {
|
||||
log_error("Failed to unmap the overlay window");
|
||||
free(e);
|
||||
@@ -323,7 +322,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
||||
ps->pending_updates = true;
|
||||
}
|
||||
|
||||
if (ev->parent == ps->c.screen_info->root) {
|
||||
if (ev->parent == ps->root) {
|
||||
// X will generate reparent notifiy even if the parent didn't actually
|
||||
// change (i.e. reparent again to current parent). So we check if that's
|
||||
// the case
|
||||
@@ -351,7 +350,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
||||
|
||||
// Reset event mask in case something wrong happens
|
||||
xcb_change_window_attributes(
|
||||
ps->c.c, ev->window, XCB_CW_EVENT_MASK,
|
||||
ps->c, ev->window, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN)});
|
||||
|
||||
if (!wid_has_prop(ps, ev->window, ps->atoms->aWM_STATE)) {
|
||||
@@ -360,7 +359,7 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
||||
"property change in case it gains one.",
|
||||
ev->window);
|
||||
xcb_change_window_attributes(
|
||||
ps->c.c, ev->window, XCB_CW_EVENT_MASK,
|
||||
ps->c, ev->window, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, ev->window, WIN_EVMODE_UNKNOWN) |
|
||||
XCB_EVENT_MASK_PROPERTY_CHANGE});
|
||||
} else {
|
||||
@@ -373,9 +372,9 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
||||
win_set_flags(w_real_top, WIN_FLAGS_CLIENT_STALE);
|
||||
ps->pending_updates = true;
|
||||
} else {
|
||||
if (!w_real_top) {
|
||||
if (!w_real_top)
|
||||
log_debug("parent %#010x not found", ev->parent);
|
||||
} else {
|
||||
else {
|
||||
// Window is not currently mapped, unmark its
|
||||
// client to trigger a client recheck when it is
|
||||
// mapped later.
|
||||
@@ -392,9 +391,8 @@ static inline void ev_reparent_notify(session_t *ps, xcb_reparent_notify_event_t
|
||||
static inline void ev_circulate_notify(session_t *ps, xcb_circulate_notify_event_t *ev) {
|
||||
auto w = find_win(ps, ev->window);
|
||||
|
||||
if (!w) {
|
||||
if (!w)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev->place == PlaceOnTop) {
|
||||
restack_top(ps, w);
|
||||
@@ -411,8 +409,7 @@ static inline void expose_root(session_t *ps, const rect_t *rects, int nrects) {
|
||||
}
|
||||
|
||||
static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
|
||||
if (ev->window == ps->c.screen_info->root ||
|
||||
(ps->overlay && ev->window == ps->overlay)) {
|
||||
if (ev->window == ps->root || (ps->overlay && ev->window == ps->overlay)) {
|
||||
int more = ev->count + 1;
|
||||
if (ps->n_expose == ps->size_expose) {
|
||||
if (ps->expose_rects) {
|
||||
@@ -441,8 +438,8 @@ static inline void ev_expose(session_t *ps, xcb_expose_event_t *ev) {
|
||||
static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t *ev) {
|
||||
if (unlikely(log_get_level_tls() <= LOG_LEVEL_TRACE)) {
|
||||
// Print out changed atom
|
||||
xcb_get_atom_name_reply_t *reply = xcb_get_atom_name_reply(
|
||||
ps->c.c, xcb_get_atom_name(ps->c.c, ev->atom), NULL);
|
||||
xcb_get_atom_name_reply_t *reply =
|
||||
xcb_get_atom_name_reply(ps->c, xcb_get_atom_name(ps->c, ev->atom), NULL);
|
||||
const char *name = "?";
|
||||
int name_len = 1;
|
||||
if (reply) {
|
||||
@@ -454,7 +451,29 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
||||
free(reply);
|
||||
}
|
||||
|
||||
if (ps->c.screen_info->root == ev->window) {
|
||||
if (ps->root == ev->window) {
|
||||
|
||||
if (ev->atom == ps->atoms->a_NET_CURRENT_DESKTOP) {
|
||||
auto prop = x_get_prop(ps->c, ps->root, ps->atoms->a_NET_CURRENT_DESKTOP,
|
||||
1L, XCB_ATOM_CARDINAL, 32);
|
||||
|
||||
if (prop.nitems) {
|
||||
if (ps->cur_desktop != *prop.c32) {
|
||||
win_stack_foreach_managed_safe(w, &ps->window_stack) {
|
||||
if (w->a.override_redirect) {
|
||||
continue;
|
||||
}
|
||||
if (w->cur_desktop & *prop.c32) {
|
||||
w->dwm_mask = ANIM_NEXT_TAG;
|
||||
} else if (w->cur_desktop & ps->cur_desktop) {
|
||||
w->dwm_mask = ANIM_PREV_TAG;
|
||||
}
|
||||
}
|
||||
ps->cur_desktop = *prop.c32;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) {
|
||||
// to update focus
|
||||
ps->pending_updates = true;
|
||||
@@ -475,7 +494,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t
|
||||
// Check whether it could be a client window
|
||||
if (!find_toplevel(ps, ev->window)) {
|
||||
// Reset event mask anyway
|
||||
xcb_change_window_attributes(ps->c.c, ev->window, XCB_CW_EVENT_MASK,
|
||||
xcb_change_window_attributes(ps->c, ev->window, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(
|
||||
ps, ev->window, WIN_EVMODE_UNKNOWN)});
|
||||
|
||||
@@ -588,16 +607,12 @@ static inline void repair_win(session_t *ps, struct managed_win *w) {
|
||||
|
||||
if (!w->ever_damaged) {
|
||||
win_extents(w, &parts);
|
||||
if (!ps->o.show_all_xerrors) {
|
||||
set_ignore_cookie(&ps->c, xcb_damage_subtract(ps->c.c, w->damage,
|
||||
XCB_NONE, XCB_NONE));
|
||||
}
|
||||
set_ignore_cookie(
|
||||
ps, xcb_damage_subtract(ps->c, w->damage, XCB_NONE, XCB_NONE));
|
||||
} else {
|
||||
if (!ps->o.show_all_xerrors) {
|
||||
set_ignore_cookie(&ps->c, xcb_damage_subtract(ps->c.c, w->damage, XCB_NONE,
|
||||
ps->damaged_region));
|
||||
}
|
||||
x_fetch_region(&ps->c, ps->damaged_region, &parts);
|
||||
set_ignore_cookie(
|
||||
ps, xcb_damage_subtract(ps->c, w->damage, XCB_NONE, ps->damaged_region));
|
||||
x_fetch_region(ps->c, ps->damaged_region, &parts);
|
||||
pixman_region32_translate(&parts, w->g.x + w->g.border_width,
|
||||
w->g.y + w->g.border_width);
|
||||
}
|
||||
@@ -672,8 +687,8 @@ ev_selection_clear(session_t *ps, xcb_selection_clear_event_t attr_unused *ev) {
|
||||
}
|
||||
|
||||
void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
|
||||
if (XCB_EVENT_RESPONSE_TYPE(ev) != KeymapNotify) {
|
||||
x_discard_pending(&ps->c, ev->full_sequence);
|
||||
if ((ev->response_type & 0x7f) != KeymapNotify) {
|
||||
discard_pending(ps, ev->full_sequence);
|
||||
}
|
||||
|
||||
xcb_window_t wid = ev_window(ps, ev);
|
||||
@@ -694,10 +709,9 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
|
||||
// For even more details, see:
|
||||
// https://bugs.freedesktop.org/show_bug.cgi?id=35945
|
||||
// https://lists.freedesktop.org/archives/xcb/2011-November/007337.html
|
||||
auto response_type = XCB_EVENT_RESPONSE_TYPE(ev);
|
||||
auto proc = XESetWireToEvent(ps->c.dpy, response_type, 0);
|
||||
auto proc = XESetWireToEvent(ps->dpy, ev->response_type, 0);
|
||||
if (proc) {
|
||||
XESetWireToEvent(ps->c.dpy, response_type, proc);
|
||||
XESetWireToEvent(ps->dpy, ev->response_type, proc);
|
||||
XEvent dummy;
|
||||
|
||||
// Stop Xlib from complaining about lost sequence numbers.
|
||||
@@ -707,8 +721,8 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
|
||||
//
|
||||
// We only need the low 16 bits
|
||||
uint16_t seq = ev->sequence;
|
||||
ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->c.dpy) & 0xffff);
|
||||
proc(ps->c.dpy, &dummy, (xEvent *)ev);
|
||||
ev->sequence = (uint16_t)(LastKnownRequestProcessed(ps->dpy) & 0xffff);
|
||||
proc(ps->dpy, &dummy, (xEvent *)ev);
|
||||
// Restore the sequence number
|
||||
ev->sequence = seq;
|
||||
}
|
||||
@@ -716,8 +730,6 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
|
||||
// XXX redraw needs to be more fine grained
|
||||
queue_redraw(ps);
|
||||
|
||||
// We intentionally ignore events sent via SendEvent. Those events has the 8th bit
|
||||
// of response_type set, meaning they will match none of the cases below.
|
||||
switch (ev->response_type) {
|
||||
case FocusIn: ev_focus_in(ps, (xcb_focus_in_event_t *)ev); break;
|
||||
case FocusOut: ev_focus_out(ps, (xcb_focus_out_event_t *)ev); break;
|
||||
@@ -743,7 +755,7 @@ void ev_handle(session_t *ps, xcb_generic_event_t *ev) {
|
||||
case SelectionClear:
|
||||
ev_selection_clear(ps, (xcb_selection_clear_event_t *)ev);
|
||||
break;
|
||||
case 0: x_handle_error(&ps->c, (xcb_generic_error_t *)ev); break;
|
||||
case 0: ev_xcb_error(ps, (xcb_generic_error_t *)ev); break;
|
||||
default:
|
||||
if (ps->shape_exists && ev->response_type == ps->shape_event) {
|
||||
ev_shape_notify(ps, (xcb_shape_notify_event_t *)ev);
|
||||
|
||||
@@ -54,9 +54,8 @@ static inline double attr_const gaussian(double r, double x, double y) {
|
||||
// Formula can be found here:
|
||||
// https://en.wikipedia.org/wiki/Gaussian_blur#Mathematics
|
||||
// Except a special case for r == 0 to produce sharp shadows
|
||||
if (r == 0) {
|
||||
if (r == 0)
|
||||
return 1;
|
||||
}
|
||||
return exp(-0.5 * (x * x + y * y) / (r * r)) / (2 * M_PI * r * r);
|
||||
}
|
||||
|
||||
|
||||
30
src/log.c
30
src/log.c
@@ -68,7 +68,6 @@ log_default_writev(struct log_target *tgt, const struct iovec *vec, int vcnt) {
|
||||
static attr_const const char *log_level_to_string(enum log_level level) {
|
||||
switch (level) {
|
||||
case LOG_LEVEL_TRACE: return "TRACE";
|
||||
case LOG_LEVEL_VERBOSE: return "VERBOSE";
|
||||
case LOG_LEVEL_DEBUG: return "DEBUG";
|
||||
case LOG_LEVEL_INFO: return "INFO";
|
||||
case LOG_LEVEL_WARN: return "WARN";
|
||||
@@ -79,24 +78,16 @@ static attr_const const char *log_level_to_string(enum log_level level) {
|
||||
}
|
||||
|
||||
enum log_level string_to_log_level(const char *str) {
|
||||
if (strcasecmp(str, "TRACE") == 0) {
|
||||
if (strcasecmp(str, "TRACE") == 0)
|
||||
return LOG_LEVEL_TRACE;
|
||||
}
|
||||
if (strcasecmp(str, "VERBOSE") == 0) {
|
||||
return LOG_LEVEL_VERBOSE;
|
||||
}
|
||||
if (strcasecmp(str, "DEBUG") == 0) {
|
||||
else if (strcasecmp(str, "DEBUG") == 0)
|
||||
return LOG_LEVEL_DEBUG;
|
||||
}
|
||||
if (strcasecmp(str, "INFO") == 0) {
|
||||
else if (strcasecmp(str, "INFO") == 0)
|
||||
return LOG_LEVEL_INFO;
|
||||
}
|
||||
if (strcasecmp(str, "WARN") == 0) {
|
||||
else if (strcasecmp(str, "WARN") == 0)
|
||||
return LOG_LEVEL_WARN;
|
||||
}
|
||||
if (strcasecmp(str, "ERROR") == 0) {
|
||||
else if (strcasecmp(str, "ERROR") == 0)
|
||||
return LOG_LEVEL_ERROR;
|
||||
}
|
||||
return LOG_LEVEL_INVALID;
|
||||
}
|
||||
|
||||
@@ -152,9 +143,8 @@ enum log_level log_get_level(const struct log *l) {
|
||||
attr_printf(4, 5) void log_printf(struct log *l, int level, const char *func,
|
||||
const char *fmt, ...) {
|
||||
assert(level <= LOG_LEVEL_FATAL && level >= 0);
|
||||
if (level < l->log_level) {
|
||||
if (level < l->log_level)
|
||||
return;
|
||||
}
|
||||
|
||||
char *buf = NULL;
|
||||
va_list args;
|
||||
@@ -238,10 +228,12 @@ struct log_target *null_logger_new(void) {
|
||||
|
||||
static void null_logger_write(struct log_target *tgt attr_unused,
|
||||
const char *str attr_unused, size_t len attr_unused) {
|
||||
return;
|
||||
}
|
||||
|
||||
static void null_logger_writev(struct log_target *tgt attr_unused,
|
||||
const struct iovec *vec attr_unused, int vcnt attr_unused) {
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct log_ops null_logger_ops = {
|
||||
@@ -277,7 +269,6 @@ static void file_logger_destroy(struct log_target *tgt) {
|
||||
static const char *terminal_colorize_begin(enum log_level level) {
|
||||
switch (level) {
|
||||
case LOG_LEVEL_TRACE: return ANSI("30;2");
|
||||
case LOG_LEVEL_VERBOSE:
|
||||
case LOG_LEVEL_DEBUG: return ANSI("37;2");
|
||||
case LOG_LEVEL_INFO: return ANSI("92");
|
||||
case LOG_LEVEL_WARN: return ANSI("33");
|
||||
@@ -349,7 +340,7 @@ static void
|
||||
gl_string_marker_logger_write(struct log_target *tgt, const char *str, size_t len) {
|
||||
auto g = (struct gl_string_marker_logger *)tgt;
|
||||
// strip newlines at the end of the string
|
||||
while (len > 0 && str[len - 1] == '\n') {
|
||||
while (len > 0 && str[len-1] == '\n') {
|
||||
len--;
|
||||
}
|
||||
g->gl_string_marker((GLsizei)len, str);
|
||||
@@ -367,9 +358,8 @@ struct log_target *gl_string_marker_logger_new(void) {
|
||||
}
|
||||
|
||||
void *fnptr = glXGetProcAddress((GLubyte *)"glStringMarkerGREMEDY");
|
||||
if (!fnptr) {
|
||||
if (!fnptr)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto ret = cmalloc(struct gl_string_marker_logger);
|
||||
ret->tgt.ops = &gl_string_marker_logger_ops;
|
||||
|
||||
@@ -9,19 +9,11 @@
|
||||
|
||||
enum log_level {
|
||||
LOG_LEVEL_INVALID = -1,
|
||||
/// Very noisy debug messages, many lines per frame.
|
||||
LOG_LEVEL_TRACE = 0,
|
||||
/// Frequent debug messages, a few lines per frame.
|
||||
LOG_LEVEL_VERBOSE,
|
||||
/// Less frequent debug messages.
|
||||
LOG_LEVEL_DEBUG,
|
||||
/// Informational messages.
|
||||
LOG_LEVEL_INFO,
|
||||
/// Warnings.
|
||||
LOG_LEVEL_WARN,
|
||||
/// Errors.
|
||||
LOG_LEVEL_ERROR,
|
||||
/// Fatal errors.
|
||||
LOG_LEVEL_FATAL,
|
||||
};
|
||||
|
||||
@@ -39,7 +31,6 @@ enum log_level {
|
||||
} \
|
||||
} while (0)
|
||||
#define log_trace(x, ...) LOG_UNLIKELY(TRACE, x, ##__VA_ARGS__)
|
||||
#define log_verbose(x, ...) LOG_UNLIKELY(VERBOSE, x, ##__VA_ARGS__)
|
||||
#define log_debug(x, ...) LOG_UNLIKELY(DEBUG, x, ##__VA_ARGS__)
|
||||
#define log_info(x, ...) LOG(INFO, x, ##__VA_ARGS__)
|
||||
#define log_warn(x, ...) LOG(WARN, x, ##__VA_ARGS__)
|
||||
|
||||
@@ -9,21 +9,18 @@ base_deps = [
|
||||
|
||||
srcs = [ files('picom.c', 'win.c', 'c2.c', 'x.c', 'config.c', 'vsync.c', 'utils.c',
|
||||
'diagnostic.c', 'string_utils.c', 'render.c', 'kernel.c', 'log.c',
|
||||
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c', 'statistics.c',
|
||||
'vblank.c') ]
|
||||
'options.c', 'event.c', 'cache.c', 'atom.c', 'file_watch.c') ]
|
||||
picom_inc = include_directories('.')
|
||||
|
||||
cflags = []
|
||||
|
||||
required_xcb_packages = [
|
||||
'xcb', 'xcb-composite', 'xcb-damage', 'xcb-dpms', 'xcb-glx', 'xcb-present',
|
||||
'xcb-randr', 'xcb-render', 'xcb-shape', 'xcb-sync', 'xcb-xfixes'
|
||||
'xcb-render', 'xcb-damage', 'xcb-randr', 'xcb-sync', 'xcb-composite',
|
||||
'xcb-shape', 'xcb-xfixes', 'xcb-present', 'xcb-glx', 'xcb-dpms', 'xcb'
|
||||
]
|
||||
|
||||
# Some XCB packages are here because their versioning differs (see check below).
|
||||
required_packages = [
|
||||
'pixman-1', 'x11', 'x11-xcb', 'xcb-image', 'xcb-renderutil', 'xcb-util',
|
||||
'xext'
|
||||
'x11', 'x11-xcb', 'xcb-renderutil', 'xcb-image', 'xext', 'pixman-1'
|
||||
]
|
||||
|
||||
foreach i : required_packages
|
||||
@@ -59,7 +56,7 @@ endif
|
||||
|
||||
if get_option('opengl')
|
||||
cflags += ['-DCONFIG_OPENGL', '-DGL_GLEXT_PROTOTYPES']
|
||||
deps += [dependency('gl', required: true), dependency('egl', required: true), dependency('threads', required:true)]
|
||||
deps += [dependency('gl', required: true), dependency('egl', required: true)]
|
||||
srcs += [ 'opengl.c' ]
|
||||
endif
|
||||
|
||||
|
||||
144
src/opengl.c
144
src/opengl.c
@@ -39,7 +39,7 @@ static inline XVisualInfo *get_visualinfo_from_visual(session_t *ps, xcb_visuali
|
||||
XVisualInfo vreq = {.visualid = visual};
|
||||
int nitems = 0;
|
||||
|
||||
return XGetVisualInfo(ps->c.dpy, VisualIDMask, &vreq, &nitems);
|
||||
return XGetVisualInfo(ps->dpy, VisualIDMask, &vreq, &nitems);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +56,7 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
}
|
||||
|
||||
// Get XVisualInfo
|
||||
pvis = get_visualinfo_from_visual(ps, ps->c.screen_info->root_visual);
|
||||
pvis = get_visualinfo_from_visual(ps, ps->vis);
|
||||
if (!pvis) {
|
||||
log_error("Failed to acquire XVisualInfo for current visual.");
|
||||
goto glx_init_end;
|
||||
@@ -65,22 +65,20 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
// Ensure the visual is double-buffered
|
||||
if (need_render) {
|
||||
int value = 0;
|
||||
if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_USE_GL, &value) || !value) {
|
||||
if (Success != glXGetConfig(ps->dpy, pvis, GLX_USE_GL, &value) || !value) {
|
||||
log_error("Root visual is not a GL visual.");
|
||||
goto glx_init_end;
|
||||
}
|
||||
|
||||
if (Success != glXGetConfig(ps->c.dpy, pvis, GLX_DOUBLEBUFFER, &value) ||
|
||||
!value) {
|
||||
if (Success != glXGetConfig(ps->dpy, pvis, GLX_DOUBLEBUFFER, &value) || !value) {
|
||||
log_error("Root visual is not a double buffered GL visual.");
|
||||
goto glx_init_end;
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure GLX_EXT_texture_from_pixmap exists
|
||||
if (need_render && !glxext.has_GLX_EXT_texture_from_pixmap) {
|
||||
if (need_render && !glxext.has_GLX_EXT_texture_from_pixmap)
|
||||
goto glx_init_end;
|
||||
}
|
||||
|
||||
// Initialize GLX data structure
|
||||
if (!ps->psglx) {
|
||||
@@ -114,7 +112,7 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
if (!psglx->context) {
|
||||
// Get GLX context
|
||||
#ifndef DEBUG_GLX_DEBUG_CONTEXT
|
||||
psglx->context = glXCreateContext(ps->c.dpy, pvis, None, GL_TRUE);
|
||||
psglx->context = glXCreateContext(ps->dpy, pvis, None, GL_TRUE);
|
||||
#else
|
||||
{
|
||||
GLXFBConfig fbconfig = get_fbconfig_from_visualinfo(ps, pvis);
|
||||
@@ -136,7 +134,7 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
static const int attrib_list[] = {
|
||||
GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_DEBUG_BIT_ARB, None};
|
||||
psglx->context = p_glXCreateContextAttribsARB(
|
||||
ps->c.dpy, fbconfig, NULL, GL_TRUE, attrib_list);
|
||||
ps->dpy, fbconfig, NULL, GL_TRUE, attrib_list);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -146,7 +144,7 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
}
|
||||
|
||||
// Attach GLX context
|
||||
if (!glXMakeCurrent(ps->c.dpy, get_tgt_window(ps), psglx->context)) {
|
||||
if (!glXMakeCurrent(ps->dpy, get_tgt_window(ps), psglx->context)) {
|
||||
log_error("Failed to attach GLX context.");
|
||||
goto glx_init_end;
|
||||
}
|
||||
@@ -179,10 +177,9 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
|
||||
// Check GL_ARB_texture_non_power_of_two, requires a GLX context and
|
||||
// must precede FBConfig fetching
|
||||
if (need_render) {
|
||||
if (need_render)
|
||||
psglx->has_texture_non_power_of_two =
|
||||
gl_has_extension("GL_ARB_texture_non_power_of_two");
|
||||
}
|
||||
|
||||
// Render preparations
|
||||
if (need_render) {
|
||||
@@ -202,9 +199,9 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
glClearColor(0.0F, 0.0F, 0.0F, 1.0F);
|
||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
// glXSwapBuffers(ps->c.dpy, get_tgt_window(ps));
|
||||
// glXSwapBuffers(ps->dpy, get_tgt_window(ps));
|
||||
}
|
||||
|
||||
success = true;
|
||||
@@ -212,17 +209,15 @@ bool glx_init(session_t *ps, bool need_render) {
|
||||
glx_init_end:
|
||||
XFree(pvis);
|
||||
|
||||
if (!success) {
|
||||
if (!success)
|
||||
glx_destroy(ps);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
static void glx_free_prog_main(glx_prog_main_t *pprogram) {
|
||||
if (!pprogram) {
|
||||
if (!pprogram)
|
||||
return;
|
||||
}
|
||||
if (pprogram->prog) {
|
||||
glDeleteProgram(pprogram->prog);
|
||||
pprogram->prog = 0;
|
||||
@@ -236,9 +231,8 @@ static void glx_free_prog_main(glx_prog_main_t *pprogram) {
|
||||
* Destroy GLX related resources.
|
||||
*/
|
||||
void glx_destroy(session_t *ps) {
|
||||
if (!ps->psglx) {
|
||||
if (!ps->psglx)
|
||||
return;
|
||||
}
|
||||
|
||||
// Free all GLX resources of windows
|
||||
win_stack_foreach_managed(w, &ps->window_stack) {
|
||||
@@ -272,8 +266,8 @@ void glx_destroy(session_t *ps) {
|
||||
|
||||
// Destroy GLX context
|
||||
if (ps->psglx->context) {
|
||||
glXMakeCurrent(ps->c.dpy, None, NULL);
|
||||
glXDestroyContext(ps->c.dpy, ps->psglx->context);
|
||||
glXMakeCurrent(ps->dpy, None, NULL);
|
||||
glXDestroyContext(ps->dpy, ps->psglx->context);
|
||||
ps->psglx->context = NULL;
|
||||
}
|
||||
|
||||
@@ -378,9 +372,8 @@ bool glx_init_blur(session_t *ps) {
|
||||
double sum = 0.0;
|
||||
for (int j = 0; j < height; ++j) {
|
||||
for (int k = 0; k < width; ++k) {
|
||||
if (height / 2 == j && width / 2 == k) {
|
||||
if (height / 2 == j && width / 2 == k)
|
||||
continue;
|
||||
}
|
||||
double val = kern->data[j * width + k];
|
||||
if (val == 0) {
|
||||
continue;
|
||||
@@ -697,9 +690,8 @@ bool glx_bind_texture(session_t *ps attr_unused, glx_texture_t **pptex, int x, i
|
||||
*/
|
||||
bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap, int width,
|
||||
int height, bool repeat, const struct glx_fbconfig_info *fbcfg) {
|
||||
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID) {
|
||||
if (ps->o.backend != BKEND_GLX && ps->o.backend != BKEND_XR_GLX_HYBRID)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!pixmap) {
|
||||
log_error("Binding to an empty pixmap %#010x. This can't work.", pixmap);
|
||||
@@ -740,7 +732,7 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
|
||||
// Retrieve pixmap parameters, if they aren't provided
|
||||
if (!width || !height) {
|
||||
auto r = xcb_get_geometry_reply(
|
||||
ps->c.c, xcb_get_geometry(ps->c.c, pixmap), NULL);
|
||||
ps->c, xcb_get_geometry(ps->c, pixmap), NULL);
|
||||
if (!r) {
|
||||
log_error("Failed to query info of pixmap %#010x.", pixmap);
|
||||
return false;
|
||||
@@ -761,15 +753,14 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
|
||||
// pixmap-specific parameters, and this may change in the future
|
||||
GLenum tex_tgt = 0;
|
||||
if (GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts &&
|
||||
ps->psglx->has_texture_non_power_of_two) {
|
||||
ps->psglx->has_texture_non_power_of_two)
|
||||
tex_tgt = GLX_TEXTURE_2D_EXT;
|
||||
} else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts) {
|
||||
else if (GLX_TEXTURE_RECTANGLE_BIT_EXT & fbcfg->texture_tgts)
|
||||
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
||||
} else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts)) {
|
||||
else if (!(GLX_TEXTURE_2D_BIT_EXT & fbcfg->texture_tgts))
|
||||
tex_tgt = GLX_TEXTURE_RECTANGLE_EXT;
|
||||
} else {
|
||||
else
|
||||
tex_tgt = GLX_TEXTURE_2D_EXT;
|
||||
}
|
||||
|
||||
log_debug("depth %d, tgt %#x, rgba %d", depth, tex_tgt,
|
||||
(GLX_TEXTURE_FORMAT_RGBA_EXT == fbcfg->texture_fmt));
|
||||
@@ -782,7 +773,7 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
|
||||
0,
|
||||
};
|
||||
|
||||
ptex->glpixmap = glXCreatePixmap(ps->c.dpy, fbcfg->cfg, pixmap, attrs);
|
||||
ptex->glpixmap = glXCreatePixmap(ps->dpy, fbcfg->cfg, pixmap, attrs);
|
||||
ptex->pixmap = pixmap;
|
||||
ptex->target =
|
||||
(GLX_TEXTURE_2D_EXT == tex_tgt ? GL_TEXTURE_2D : GL_TEXTURE_RECTANGLE);
|
||||
@@ -828,11 +819,10 @@ bool glx_bind_pixmap(session_t *ps, glx_texture_t **pptex, xcb_pixmap_t pixmap,
|
||||
|
||||
// The specification requires rebinding whenever the content changes...
|
||||
// We can't follow this, too slow.
|
||||
if (need_release) {
|
||||
glXReleaseTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
}
|
||||
if (need_release)
|
||||
glXReleaseTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
|
||||
glXBindTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
glXBindTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT, NULL);
|
||||
|
||||
// Cleanup
|
||||
glBindTexture(ptex->target, 0);
|
||||
@@ -850,13 +840,13 @@ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
|
||||
// Release binding
|
||||
if (ptex->glpixmap && ptex->texture) {
|
||||
glBindTexture(ptex->target, ptex->texture);
|
||||
glXReleaseTexImageEXT(ps->c.dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
glXReleaseTexImageEXT(ps->dpy, ptex->glpixmap, GLX_FRONT_LEFT_EXT);
|
||||
glBindTexture(ptex->target, 0);
|
||||
}
|
||||
|
||||
// Free GLX Pixmap
|
||||
if (ptex->glpixmap) {
|
||||
glXDestroyPixmap(ps->c.dpy, ptex->glpixmap);
|
||||
glXDestroyPixmap(ps->dpy, ptex->glpixmap);
|
||||
ptex->glpixmap = 0;
|
||||
}
|
||||
|
||||
@@ -868,16 +858,14 @@ void glx_release_pixmap(session_t *ps, glx_texture_t *ptex) {
|
||||
*/
|
||||
void glx_set_clip(session_t *ps, const region_t *reg) {
|
||||
// Quit if we aren't using stencils
|
||||
if (ps->o.glx_no_stencil) {
|
||||
if (ps->o.glx_no_stencil)
|
||||
return;
|
||||
}
|
||||
|
||||
glDisable(GL_STENCIL_TEST);
|
||||
glDisable(GL_SCISSOR_TEST);
|
||||
|
||||
if (!reg) {
|
||||
if (!reg)
|
||||
return;
|
||||
}
|
||||
|
||||
int nrects;
|
||||
const rect_t *rects = pixman_region32_rectangles((region_t *)reg, &nrects);
|
||||
@@ -925,9 +913,8 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
|
||||
// Calculate copy region size
|
||||
glx_blur_cache_t ibc = {.width = 0, .height = 0};
|
||||
if (!pbc) {
|
||||
if (!pbc)
|
||||
pbc = &ibc;
|
||||
}
|
||||
|
||||
int mdx = dx, mdy = dy, mwidth = width, mheight = height;
|
||||
// log_trace("%d, %d, %d, %d", mdx, mdy, mwidth, mheight);
|
||||
@@ -954,29 +941,24 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
*/
|
||||
|
||||
GLenum tex_tgt = GL_TEXTURE_RECTANGLE;
|
||||
if (ps->psglx->has_texture_non_power_of_two) {
|
||||
if (ps->psglx->has_texture_non_power_of_two)
|
||||
tex_tgt = GL_TEXTURE_2D;
|
||||
}
|
||||
|
||||
// Free textures if size inconsistency discovered
|
||||
if (mwidth != pbc->width || mheight != pbc->height) {
|
||||
if (mwidth != pbc->width || mheight != pbc->height)
|
||||
free_glx_bc_resize(ps, pbc);
|
||||
}
|
||||
|
||||
// Generate FBO and textures if needed
|
||||
if (!pbc->textures[0]) {
|
||||
if (!pbc->textures[0])
|
||||
pbc->textures[0] = glx_gen_texture(tex_tgt, mwidth, mheight);
|
||||
}
|
||||
GLuint tex_scr = pbc->textures[0];
|
||||
if (more_passes && !pbc->textures[1]) {
|
||||
if (more_passes && !pbc->textures[1])
|
||||
pbc->textures[1] = glx_gen_texture(tex_tgt, mwidth, mheight);
|
||||
}
|
||||
pbc->width = mwidth;
|
||||
pbc->height = mheight;
|
||||
GLuint tex_scr2 = pbc->textures[1];
|
||||
if (more_passes && !pbc->fbo) {
|
||||
if (more_passes && !pbc->fbo)
|
||||
glGenFramebuffers(1, &pbc->fbo);
|
||||
}
|
||||
const GLuint fbo = pbc->fbo;
|
||||
|
||||
if (!tex_scr || (more_passes && !tex_scr2)) {
|
||||
@@ -1004,7 +986,7 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
} */
|
||||
|
||||
// Texture scaling factor
|
||||
GLfloat texfac_x = 1.0F, texfac_y = 1.0F;
|
||||
GLfloat texfac_x = 1.0f, texfac_y = 1.0f;
|
||||
if (tex_tgt == GL_TEXTURE_2D) {
|
||||
texfac_x /= (GLfloat)mwidth;
|
||||
texfac_y /= (GLfloat)mheight;
|
||||
@@ -1037,12 +1019,10 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
} else {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glDrawBuffer(GL_BACK);
|
||||
if (have_scissors) {
|
||||
if (have_scissors)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
if (have_stencil) {
|
||||
if (have_stencil)
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
}
|
||||
|
||||
// Color negation for testing...
|
||||
@@ -1052,15 +1032,12 @@ bool glx_blur_dst(session_t *ps, int dx, int dy, int width, int height, float z,
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glUseProgram(ppass->prog);
|
||||
if (ppass->unifm_offset_x >= 0) {
|
||||
if (ppass->unifm_offset_x >= 0)
|
||||
glUniform1f(ppass->unifm_offset_x, texfac_x);
|
||||
}
|
||||
if (ppass->unifm_offset_y >= 0) {
|
||||
if (ppass->unifm_offset_y >= 0)
|
||||
glUniform1f(ppass->unifm_offset_y, texfac_y);
|
||||
}
|
||||
if (ppass->unifm_factor_center >= 0) {
|
||||
if (ppass->unifm_factor_center >= 0)
|
||||
glUniform1f(ppass->unifm_factor_center, factor_center);
|
||||
}
|
||||
|
||||
P_PAINTREG_START(crect) {
|
||||
auto rx = (GLfloat)(crect.x1 - mdx) * texfac_x;
|
||||
@@ -1110,12 +1087,10 @@ glx_blur_dst_end:
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(tex_tgt, 0);
|
||||
glDisable(tex_tgt);
|
||||
if (have_scissors) {
|
||||
if (have_scissors)
|
||||
glEnable(GL_SCISSOR_TEST);
|
||||
}
|
||||
if (have_stencil) {
|
||||
if (have_stencil)
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
}
|
||||
|
||||
if (&ibc == pbc) {
|
||||
free_glx_bc(ps, pbc);
|
||||
@@ -1300,7 +1275,7 @@ bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
|
||||
// considering all those mess in color negation and modulation
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
glColor4f(0.0F, 0.0F, 0.0F, factor);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, factor);
|
||||
|
||||
P_PAINTREG_START(crect) {
|
||||
// XXX what does all of these variables mean?
|
||||
@@ -1316,7 +1291,7 @@ bool glx_dim_dst(session_t *ps, int dx, int dy, int width, int height, int z,
|
||||
}
|
||||
P_PAINTREG_END();
|
||||
|
||||
glColor4f(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glDisable(GL_BLEND);
|
||||
|
||||
gl_check_err();
|
||||
@@ -1436,19 +1411,15 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
|
||||
glUseProgram(pprogram->prog);
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
if (pprogram->unifm_opacity >= 0) {
|
||||
if (pprogram->unifm_opacity >= 0)
|
||||
glUniform1f(pprogram->unifm_opacity, (float)opacity);
|
||||
}
|
||||
if (pprogram->unifm_invert_color >= 0) {
|
||||
if (pprogram->unifm_invert_color >= 0)
|
||||
glUniform1i(pprogram->unifm_invert_color, neg);
|
||||
}
|
||||
if (pprogram->unifm_tex >= 0) {
|
||||
if (pprogram->unifm_tex >= 0)
|
||||
glUniform1i(pprogram->unifm_tex, 0);
|
||||
}
|
||||
if (pprogram->unifm_time >= 0) {
|
||||
glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0F +
|
||||
(float)ts.tv_nsec / 1.0e6F);
|
||||
}
|
||||
if (pprogram->unifm_time >= 0)
|
||||
glUniform1f(pprogram->unifm_time, (float)ts.tv_sec * 1000.0f +
|
||||
(float)ts.tv_nsec / 1.0e6f);
|
||||
}
|
||||
|
||||
// log_trace("Draw: %d, %d, %d, %d -> %d, %d (%d, %d) z %d", x, y, width, height,
|
||||
@@ -1488,8 +1459,8 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
|
||||
// Invert Y if needed, this may not work as expected, though. I
|
||||
// don't have such a FBConfig to test with.
|
||||
if (!ptex->y_inverted) {
|
||||
ry = 1.0F - ry;
|
||||
rye = 1.0F - rye;
|
||||
ry = 1.0f - ry;
|
||||
rye = 1.0f - rye;
|
||||
}
|
||||
|
||||
// log_trace("Rect %d: %f, %f, %f, %f -> %d, %d, %d, %d", ri, rx,
|
||||
@@ -1521,7 +1492,7 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
|
||||
|
||||
// Cleanup
|
||||
glBindTexture(ptex->target, 0);
|
||||
glColor4f(0.0F, 0.0F, 0.0F, 0.0F);
|
||||
glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
||||
glDisable(GL_BLEND);
|
||||
glDisable(GL_COLOR_LOGIC_OP);
|
||||
@@ -1534,9 +1505,8 @@ bool glx_render(session_t *ps, const glx_texture_t *ptex, int x, int y, int dx,
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
}
|
||||
|
||||
if (has_prog) {
|
||||
if (has_prog)
|
||||
glUseProgram(0);
|
||||
}
|
||||
|
||||
gl_check_err();
|
||||
|
||||
|
||||
@@ -156,9 +156,8 @@ static inline bool glx_has_context(session_t *ps) {
|
||||
*/
|
||||
static inline bool ensure_glx_context(session_t *ps) {
|
||||
// Create GLX context
|
||||
if (!glx_has_context(ps)) {
|
||||
if (!glx_has_context(ps))
|
||||
glx_init(ps, false);
|
||||
}
|
||||
|
||||
return glx_has_context(ps);
|
||||
}
|
||||
|
||||
@@ -176,7 +176,6 @@ static const struct picom_option picom_options[] = {
|
||||
"rendered screen. Reduces banding artifacts, but might cause performance "
|
||||
"degradation. Only works with OpenGL."},
|
||||
// 340 is corner-radius-rules
|
||||
{"no-frame-pacing" , no_argument , 341, NULL , "Disable frame pacing. This might increase the latency."},
|
||||
{"legacy-backends" , no_argument , 733, NULL , "Use deprecated version of the backends."},
|
||||
{"monitor-repaint" , no_argument , 800, NULL , "Highlight the updated area of the screen. For debugging."},
|
||||
{"diagnostics" , no_argument , 801, NULL , "Print diagnostic information"},
|
||||
@@ -570,9 +569,8 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
case 290:
|
||||
// --backend
|
||||
opt->backend = parse_backend(optarg);
|
||||
if (opt->backend >= NUM_BKEND) {
|
||||
if (opt->backend >= NUM_BKEND)
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
P_CASEBOOL(291, glx_no_stencil);
|
||||
P_CASEINT(293, benchmark);
|
||||
@@ -632,9 +630,8 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
break;
|
||||
case 304:
|
||||
// --opacity-rule
|
||||
if (!parse_numeric_window_rule(&opt->opacity_rules, optarg, 0, 100)) {
|
||||
if (!parse_numeric_window_rule(&opt->opacity_rules, optarg, 0, 100))
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 305:
|
||||
// --shadow-exclude-reg
|
||||
@@ -739,9 +736,8 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
break;
|
||||
case 340:
|
||||
// --corner-radius-rules
|
||||
if (!parse_numeric_window_rule(&opt->corner_radius_rules, optarg, 0, INT_MAX)) {
|
||||
if (!parse_numeric_window_rule(&opt->corner_radius_rules, optarg, 0, INT_MAX))
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case 335:
|
||||
// --clip-shadow-above
|
||||
@@ -751,7 +747,6 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
// --dithered-present
|
||||
opt->dithered_present = true;
|
||||
break;
|
||||
P_CASEBOOL(341, no_frame_pacing);
|
||||
P_CASEBOOL(733, legacy_backends);
|
||||
P_CASEBOOL(800, monitor_repaint);
|
||||
case 801:
|
||||
@@ -861,13 +856,14 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
}
|
||||
|
||||
if (opt->window_shader_fg || opt->window_shader_fg_rules) {
|
||||
if (opt->backend == BKEND_XRENDER || opt->legacy_backends) {
|
||||
log_warn(opt->backend == BKEND_XRENDER
|
||||
? "Shader interface is not supported by the xrender "
|
||||
"backend."
|
||||
: "The new shader interface is not supported by the "
|
||||
"legacy glx backend. You may want to use "
|
||||
"--glx-fshader-win instead.");
|
||||
if (opt->legacy_backends || opt->backend != BKEND_GLX) {
|
||||
log_warn("The new window shader interface does not work with the "
|
||||
"legacy glx backend.%s",
|
||||
(opt->backend == BKEND_GLX) ? " You may want to use "
|
||||
"\"--glx-fshader-win\" "
|
||||
"instead on the legacy "
|
||||
"glx backend."
|
||||
: "");
|
||||
opt->window_shader_fg = NULL;
|
||||
c2_list_free(&opt->window_shader_fg_rules, free);
|
||||
}
|
||||
@@ -882,16 +878,18 @@ bool get_cfg(options_t *opt, int argc, char *const *argv, bool shadow_enable,
|
||||
opt->inactive_dim = normalize_d(opt->inactive_dim);
|
||||
opt->frame_opacity = normalize_d(opt->frame_opacity);
|
||||
opt->shadow_opacity = normalize_d(opt->shadow_opacity);
|
||||
|
||||
opt->max_brightness = normalize_d(opt->max_brightness);
|
||||
if (opt->max_brightness < 1.0) {
|
||||
if (opt->backend == BKEND_XRENDER || opt->legacy_backends) {
|
||||
log_warn("--max-brightness is not supported by the %s backend. "
|
||||
"Falling back to 1.0.",
|
||||
opt->backend == BKEND_XRENDER ? "xrender" : "legacy glx");
|
||||
opt->max_brightness = 1.0;
|
||||
} else if (opt->use_damage) {
|
||||
if (opt->use_damage) {
|
||||
log_warn("--max-brightness requires --no-use-damage. Falling "
|
||||
"back to 1.0.");
|
||||
"back to 1.0");
|
||||
opt->max_brightness = 1.0;
|
||||
}
|
||||
|
||||
if (opt->legacy_backends || opt->backend != BKEND_GLX) {
|
||||
log_warn("--max-brightness requires the new glx "
|
||||
"backend. Falling back to 1.0");
|
||||
opt->max_brightness = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
1008
src/picom.c
1008
src/picom.c
File diff suppressed because it is too large
Load Diff
@@ -60,11 +60,9 @@ uint8_t session_redirection_mode(session_t *ps);
|
||||
static inline void wintype_arr_enable_unset(switch_t arr[]) {
|
||||
wintype_t i;
|
||||
|
||||
for (i = 0; i < NUM_WINTYPES; ++i) {
|
||||
if (UNSET == arr[i]) {
|
||||
for (i = 0; i < NUM_WINTYPES; ++i)
|
||||
if (UNSET == arr[i])
|
||||
arr[i] = ON;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,7 +94,7 @@ free_win_res_glx(session_t *ps attr_unused, struct managed_win *w attr_unused) {
|
||||
* Dump an drawable's info.
|
||||
*/
|
||||
static inline void dump_drawable(session_t *ps, xcb_drawable_t drawable) {
|
||||
auto r = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, drawable), NULL);
|
||||
auto r = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, drawable), NULL);
|
||||
if (!r) {
|
||||
log_trace("Drawable %#010x: Failed", drawable);
|
||||
return;
|
||||
|
||||
@@ -23,10 +23,9 @@ static inline void dump_region(const region_t *x) {
|
||||
int nrects;
|
||||
const rect_t *rects = pixman_region32_rectangles((region_t *)x, &nrects);
|
||||
log_trace("nrects: %d", nrects);
|
||||
for (int i = 0; i < nrects; i++) {
|
||||
for (int i = 0; i < nrects; i++)
|
||||
log_trace("(%d, %d) - (%d, %d)", rects[i].x1, rects[i].y1, rects[i].x2,
|
||||
rects[i].y2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert one xcb rectangle to our rectangle type
|
||||
@@ -52,7 +51,8 @@ static inline rect_t *from_x_rects(int nrects, const xcb_rectangle_t *rects) {
|
||||
/**
|
||||
* Resize a region.
|
||||
*/
|
||||
static inline void _resize_region(const region_t *region, region_t *output, int dx, int dy) {
|
||||
static inline void _resize_region(const region_t *region, region_t *output, int dx,
|
||||
int dy) {
|
||||
if (!region || !output) {
|
||||
return;
|
||||
}
|
||||
@@ -77,7 +77,8 @@ static inline void _resize_region(const region_t *region, region_t *output, int
|
||||
if (wid <= 0 || hei <= 0) {
|
||||
continue;
|
||||
}
|
||||
newrects[nnewrects] = (rect_t){.x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2};
|
||||
newrects[nnewrects] =
|
||||
(rect_t){.x1 = x1, .x2 = x2, .y1 = y1, .y2 = y2};
|
||||
++nnewrects;
|
||||
}
|
||||
|
||||
|
||||
422
src/render.c
422
src/render.c
@@ -48,20 +48,20 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
|
||||
bool repeat, int depth, xcb_visualid_t visual, bool force) {
|
||||
#ifdef CONFIG_OPENGL
|
||||
// XXX This is a mess. But this will go away after the backend refactor.
|
||||
if (!ppaint->pixmap) {
|
||||
if (!ppaint->pixmap)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct glx_fbconfig_info *fbcfg;
|
||||
if (!visual) {
|
||||
assert(depth == 32);
|
||||
if (!ps->argb_fbconfig) {
|
||||
ps->argb_fbconfig = glx_find_fbconfig(
|
||||
&ps->c, (struct xvisual_info){.red_size = 8,
|
||||
.green_size = 8,
|
||||
.blue_size = 8,
|
||||
.alpha_size = 8,
|
||||
.visual_depth = 32});
|
||||
ps->argb_fbconfig =
|
||||
glx_find_fbconfig(ps->dpy, ps->scr,
|
||||
(struct xvisual_info){.red_size = 8,
|
||||
.green_size = 8,
|
||||
.blue_size = 8,
|
||||
.alpha_size = 8,
|
||||
.visual_depth = 32});
|
||||
}
|
||||
if (!ps->argb_fbconfig) {
|
||||
log_error("Failed to find appropriate FBConfig for 32 bit depth");
|
||||
@@ -69,7 +69,7 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
|
||||
}
|
||||
fbcfg = ps->argb_fbconfig;
|
||||
} else {
|
||||
auto m = x_get_visual_info(&ps->c, visual);
|
||||
auto m = x_get_visual_info(ps->c, visual);
|
||||
if (m.visual_depth < 0) {
|
||||
return false;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
|
||||
}
|
||||
|
||||
if (!ppaint->fbcfg) {
|
||||
ppaint->fbcfg = glx_find_fbconfig(&ps->c, m);
|
||||
ppaint->fbcfg = glx_find_fbconfig(ps->dpy, ps->scr, m);
|
||||
}
|
||||
if (!ppaint->fbcfg) {
|
||||
log_error("Failed to find appropriate FBConfig for X pixmap");
|
||||
@@ -89,10 +89,9 @@ static inline bool paint_bind_tex(session_t *ps, paint_t *ppaint, int wid, int h
|
||||
fbcfg = ppaint->fbcfg;
|
||||
}
|
||||
|
||||
if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap)) {
|
||||
if (force || !glx_tex_binded(ppaint->ptex, ppaint->pixmap))
|
||||
return glx_bind_pixmap(ps, &ppaint->ptex, ppaint->pixmap, wid, hei,
|
||||
repeat, fbcfg);
|
||||
}
|
||||
#else
|
||||
(void)ps;
|
||||
(void)ppaint;
|
||||
@@ -130,7 +129,7 @@ static int get_buffer_age(session_t *ps) {
|
||||
}
|
||||
if (ps->o.use_damage) {
|
||||
unsigned int val;
|
||||
glXQueryDrawable(ps->c.dpy, get_tgt_window(ps),
|
||||
glXQueryDrawable(ps->dpy, get_tgt_window(ps),
|
||||
GLX_BACK_BUFFER_AGE_EXT, &val);
|
||||
return (int)val ?: -1;
|
||||
}
|
||||
@@ -145,7 +144,7 @@ static int get_buffer_age(session_t *ps) {
|
||||
*/
|
||||
static inline void xrfilter_reset(session_t *ps, xcb_render_picture_t p) {
|
||||
#define FILTER "Nearest"
|
||||
xcb_render_set_picture_filter(ps->c.c, p, strlen(FILTER), FILTER, 0, NULL);
|
||||
xcb_render_set_picture_filter(ps->c, p, strlen(FILTER), FILTER, 0, NULL);
|
||||
#undef FILTER
|
||||
}
|
||||
|
||||
@@ -154,7 +153,7 @@ static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg)
|
||||
switch (ps->o.backend) {
|
||||
case BKEND_XRENDER:
|
||||
case BKEND_XR_GLX_HYBRID:
|
||||
x_set_picture_clip_region(&ps->c, ps->tgt_buffer.pict, 0, 0, reg);
|
||||
x_set_picture_clip_region(ps->c, ps->tgt_buffer.pict, 0, 0, reg);
|
||||
break;
|
||||
#ifdef CONFIG_OPENGL
|
||||
case BKEND_GLX: glx_set_clip(ps, reg); break;
|
||||
@@ -163,6 +162,16 @@ static inline void attr_nonnull(1, 2) set_tgt_clip(session_t *ps, region_t *reg)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a <code>Picture</code>.
|
||||
*/
|
||||
void free_picture(xcb_connection_t *c, xcb_render_picture_t *p) {
|
||||
if (*p) {
|
||||
xcb_render_free_picture(c, *p);
|
||||
*p = XCB_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Free paint_t.
|
||||
*/
|
||||
@@ -170,14 +179,10 @@ void free_paint(session_t *ps, paint_t *ppaint) {
|
||||
#ifdef CONFIG_OPENGL
|
||||
free_paint_glx(ps, ppaint);
|
||||
#endif
|
||||
if (ppaint->pict != XCB_NONE) {
|
||||
x_free_picture(&ps->c, ppaint->pict);
|
||||
ppaint->pict = XCB_NONE;
|
||||
}
|
||||
if (ppaint->pixmap) {
|
||||
xcb_free_pixmap(ps->c.c, ppaint->pixmap);
|
||||
ppaint->pixmap = XCB_NONE;
|
||||
}
|
||||
free_picture(ps->c, &ppaint->pict);
|
||||
if (ppaint->pixmap)
|
||||
xcb_free_pixmap(ps->c, ppaint->pixmap);
|
||||
ppaint->pixmap = XCB_NONE;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
@@ -248,7 +253,8 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
|
||||
if (alpha_step != 0) {
|
||||
if (cr) {
|
||||
xcb_render_picture_t p_tmp = x_create_picture_with_standard(
|
||||
&ps->c, fullwid, fullhei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
ps->c, ps->root, fullwid, fullhei,
|
||||
XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
xcb_render_color_t trans = {
|
||||
.red = 0, .blue = 0, .green = 0, .alpha = 0};
|
||||
const xcb_rectangle_t rect = {
|
||||
@@ -256,7 +262,7 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
|
||||
.y = 0,
|
||||
.width = to_u16_checked(fullwid),
|
||||
.height = to_u16_checked(fullhei)};
|
||||
xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_SRC,
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
p_tmp, trans, 1, &rect);
|
||||
|
||||
uint32_t max_ntraps = to_u32_checked(cr);
|
||||
@@ -266,24 +272,25 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
|
||||
traps, max_ntraps, cr, fullwid, fullhei);
|
||||
|
||||
xcb_render_trapezoids(
|
||||
ps->c.c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
|
||||
x_get_pictfmt_for_standard(&ps->c, XCB_PICT_STANDARD_A_8),
|
||||
ps->c, XCB_RENDER_PICT_OP_OVER, alpha_pict, p_tmp,
|
||||
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8),
|
||||
0, 0, n, traps);
|
||||
|
||||
xcb_render_composite(
|
||||
ps->c.c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp,
|
||||
ps->c, XCB_RENDER_PICT_OP_OVER, pict, p_tmp,
|
||||
ps->tgt_buffer.pict, to_i16_checked(x),
|
||||
to_i16_checked(y), to_i16_checked(x), to_i16_checked(y),
|
||||
to_i16_checked(dx), to_i16_checked(dy),
|
||||
to_u16_checked(wid), to_u16_checked(hei));
|
||||
|
||||
x_free_picture(&ps->c, p_tmp);
|
||||
xcb_render_free_picture(ps->c, p_tmp);
|
||||
|
||||
} else {
|
||||
xcb_render_picture_t p_tmp = alpha_pict;
|
||||
if (clip) {
|
||||
p_tmp = x_create_picture_with_standard(
|
||||
&ps->c, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
ps->c, ps->root, wid, hei,
|
||||
XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
|
||||
xcb_render_color_t black = {
|
||||
.red = 255, .blue = 255, .green = 255, .alpha = 255};
|
||||
@@ -292,18 +299,17 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
|
||||
.y = 0,
|
||||
.width = to_u16_checked(wid),
|
||||
.height = to_u16_checked(hei)};
|
||||
xcb_render_fill_rectangles(ps->c.c,
|
||||
XCB_RENDER_PICT_OP_SRC,
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
p_tmp, black, 1, &rect);
|
||||
if (alpha_pict) {
|
||||
xcb_render_composite(
|
||||
ps->c.c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
alpha_pict, XCB_NONE, p_tmp, 0, 0, 0,
|
||||
0, 0, 0, to_u16_checked(wid),
|
||||
to_u16_checked(hei));
|
||||
}
|
||||
xcb_render_composite(
|
||||
ps->c.c, XCB_RENDER_PICT_OP_OUT_REVERSE,
|
||||
ps->c, XCB_RENDER_PICT_OP_OUT_REVERSE,
|
||||
clip->pict, XCB_NONE, p_tmp, 0, 0, 0, 0,
|
||||
to_i16_checked(clip->x), to_i16_checked(clip->y),
|
||||
to_u16_checked(wid), to_u16_checked(hei));
|
||||
@@ -313,12 +319,12 @@ void render(session_t *ps, int x, int y, int dx, int dy, int wid, int hei, int f
|
||||
: XCB_RENDER_PICT_OP_OVER);
|
||||
|
||||
xcb_render_composite(
|
||||
ps->c.c, op, pict, p_tmp, ps->tgt_buffer.pict,
|
||||
ps->c, op, pict, p_tmp, ps->tgt_buffer.pict,
|
||||
to_i16_checked(x), to_i16_checked(y), 0, 0,
|
||||
to_i16_checked(dx), to_i16_checked(dy),
|
||||
to_u16_checked(wid), to_u16_checked(hei));
|
||||
if (clip) {
|
||||
x_free_picture(&ps->c, p_tmp);
|
||||
xcb_render_free_picture(ps->c, p_tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -369,18 +375,15 @@ paint_region(session_t *ps, const struct managed_win *w, int x, int y, int wid,
|
||||
static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
|
||||
// Don't check for presence of Pixmap here, because older X Composite doesn't
|
||||
// provide it
|
||||
if (!ppaint) {
|
||||
if (!ppaint)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bkend_use_xrender(ps) && !ppaint->pict) {
|
||||
if (bkend_use_xrender(ps) && !ppaint->pict)
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, XCB_NONE)) {
|
||||
if (BKEND_GLX == ps->o.backend && !glx_tex_binded(ppaint->ptex, XCB_NONE))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -392,9 +395,9 @@ static inline bool paint_isvalid(session_t *ps, const paint_t *ppaint) {
|
||||
void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint) {
|
||||
// Fetch Pixmap
|
||||
if (!w->paint.pixmap) {
|
||||
w->paint.pixmap = x_new_id(&ps->c);
|
||||
set_ignore_cookie(&ps->c, xcb_composite_name_window_pixmap(
|
||||
ps->c.c, w->base.id, w->paint.pixmap));
|
||||
w->paint.pixmap = x_new_id(ps->c);
|
||||
set_ignore_cookie(ps, xcb_composite_name_window_pixmap(ps->c, w->base.id,
|
||||
w->paint.pixmap));
|
||||
}
|
||||
|
||||
xcb_drawable_t draw = w->paint.pixmap;
|
||||
@@ -412,7 +415,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
};
|
||||
|
||||
w->paint.pict = x_create_picture_with_pictfmt_and_pixmap(
|
||||
&ps->c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
||||
ps->c, w->pictfmt, draw, XCB_RENDER_CP_SUBWINDOW_MODE, &pa);
|
||||
}
|
||||
|
||||
// GLX: Build texture
|
||||
@@ -439,8 +442,8 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
|
||||
// Invert window color, if required
|
||||
if (bkend_use_xrender(ps) && w->invert_color) {
|
||||
xcb_render_picture_t newpict =
|
||||
x_create_picture_with_pictfmt(&ps->c, wid, hei, w->pictfmt, 0, NULL);
|
||||
xcb_render_picture_t newpict = x_create_picture_with_pictfmt(
|
||||
ps->c, ps->root, wid, hei, w->pictfmt, 0, NULL);
|
||||
if (newpict) {
|
||||
// Apply clipping region to save some CPU
|
||||
if (reg_paint) {
|
||||
@@ -453,18 +456,17 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
pixman_region32_fini(®);
|
||||
}
|
||||
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, pict,
|
||||
XCB_NONE, newpict, 0, 0, 0, 0, 0, 0, wid, hei);
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, pict, XCB_NONE,
|
||||
newpict, 0, 0, 0, 0, 0, 0, wid, hei);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_DIFFERENCE,
|
||||
ps->white_picture, XCB_NONE, newpict, 0, 0,
|
||||
0, 0, 0, 0, wid, hei);
|
||||
// We use an extra PictOpInReverse operation to get correct
|
||||
// pixel alpha. There could be a better solution.
|
||||
if (win_has_alpha(w)) {
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
if (win_has_alpha(w))
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_IN_REVERSE,
|
||||
pict, XCB_NONE, newpict, 0, 0, 0, 0,
|
||||
0, 0, wid, hei);
|
||||
}
|
||||
pict = newpict;
|
||||
}
|
||||
}
|
||||
@@ -474,10 +476,10 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
} else {
|
||||
// Painting parameters
|
||||
const margin_t extents = win_calc_frame_extents(w);
|
||||
auto const t = extents.top;
|
||||
auto const l = extents.left;
|
||||
auto const b = extents.bottom;
|
||||
auto const r = extents.right;
|
||||
const auto t = extents.top;
|
||||
const auto l = extents.left;
|
||||
const auto b = extents.bottom;
|
||||
const auto r = extents.right;
|
||||
|
||||
#define COMP_BDR(cx, cy, cwid, chei) \
|
||||
paint_region(ps, w, (cx), (cy), (cwid), (chei), w->frame_opacity * w->opacity, \
|
||||
@@ -492,51 +494,43 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
// ctop = checked top
|
||||
// Make sure top margin is smaller than height
|
||||
int ctop = min2(body_height, t);
|
||||
if (ctop > 0) {
|
||||
if (ctop > 0)
|
||||
COMP_BDR(0, 0, wid, ctop);
|
||||
}
|
||||
|
||||
body_height -= ctop;
|
||||
if (body_height <= 0) {
|
||||
if (body_height <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// bottom
|
||||
// cbot = checked bottom
|
||||
// Make sure bottom margin is not too large
|
||||
int cbot = min2(body_height, b);
|
||||
if (cbot > 0) {
|
||||
if (cbot > 0)
|
||||
COMP_BDR(0, hei - cbot, wid, cbot);
|
||||
}
|
||||
|
||||
// Height of window exclude the margin
|
||||
body_height -= cbot;
|
||||
if (body_height <= 0) {
|
||||
if (body_height <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// left
|
||||
int body_width = wid;
|
||||
int cleft = min2(body_width, l);
|
||||
if (cleft > 0) {
|
||||
if (cleft > 0)
|
||||
COMP_BDR(0, ctop, cleft, body_height);
|
||||
}
|
||||
|
||||
body_width -= cleft;
|
||||
if (body_width <= 0) {
|
||||
if (body_width <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// right
|
||||
int cright = min2(body_width, r);
|
||||
if (cright > 0) {
|
||||
if (cright > 0)
|
||||
COMP_BDR(wid - cright, ctop, cright, body_height);
|
||||
}
|
||||
|
||||
body_width -= cright;
|
||||
if (body_width <= 0) {
|
||||
if (body_width <= 0)
|
||||
break;
|
||||
}
|
||||
|
||||
// body
|
||||
paint_region(ps, w, cleft, ctop, body_width, body_height,
|
||||
@@ -546,17 +540,14 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
|
||||
#undef COMP_BDR
|
||||
|
||||
if (pict != w->paint.pict) {
|
||||
x_free_picture(&ps->c, pict);
|
||||
pict = XCB_NONE;
|
||||
}
|
||||
if (pict != w->paint.pict)
|
||||
free_picture(ps->c, &pict);
|
||||
|
||||
// Dimming the window if needed
|
||||
if (w->dim) {
|
||||
double dim_opacity = ps->o.inactive_dim;
|
||||
if (!ps->o.inactive_dim_fixed) {
|
||||
if (!ps->o.inactive_dim_fixed)
|
||||
dim_opacity *= w->opacity;
|
||||
}
|
||||
|
||||
switch (ps->o.backend) {
|
||||
case BKEND_XRENDER:
|
||||
@@ -578,7 +569,7 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
.height = hei,
|
||||
};
|
||||
|
||||
xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_OVER,
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_OVER,
|
||||
ps->tgt_buffer.pict, color, 1, &rect);
|
||||
} break;
|
||||
#ifdef CONFIG_OPENGL
|
||||
@@ -592,6 +583,8 @@ void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint)
|
||||
}
|
||||
}
|
||||
|
||||
extern const char *background_props_str[];
|
||||
|
||||
static bool get_root_tile(session_t *ps) {
|
||||
/*
|
||||
if (ps->o.paint_on_overlay) {
|
||||
@@ -602,17 +595,15 @@ static bool get_root_tile(session_t *ps) {
|
||||
ps->root_tile_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->root, ps->atoms);
|
||||
|
||||
// Make sure the pixmap we got is valid
|
||||
if (pixmap && !x_validate_pixmap(&ps->c, pixmap)) {
|
||||
if (pixmap && !x_validate_pixmap(ps->c, pixmap))
|
||||
pixmap = XCB_NONE;
|
||||
}
|
||||
|
||||
// Create a pixmap if there isn't any
|
||||
if (!pixmap) {
|
||||
pixmap =
|
||||
x_create_pixmap(&ps->c, (uint8_t)ps->c.screen_info->root_depth, 1, 1);
|
||||
pixmap = x_create_pixmap(ps->c, (uint8_t)ps->depth, ps->root, 1, 1);
|
||||
if (pixmap == XCB_NONE) {
|
||||
log_error("Failed to create pixmaps for root tile.");
|
||||
return false;
|
||||
@@ -625,7 +616,7 @@ static bool get_root_tile(session_t *ps) {
|
||||
.repeat = true,
|
||||
};
|
||||
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, ps->vis, pixmap, XCB_RENDER_CP_REPEAT, &pa);
|
||||
|
||||
// Fill pixmap if needed
|
||||
if (fill) {
|
||||
@@ -638,17 +629,15 @@ static bool get_root_tile(session_t *ps) {
|
||||
rect.x = rect.y = 0;
|
||||
rect.width = rect.height = 1;
|
||||
|
||||
xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_SRC,
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->root_tile_paint.pict, col, 1, &rect);
|
||||
}
|
||||
|
||||
ps->root_tile_fill = fill;
|
||||
ps->root_tile_paint.pixmap = pixmap;
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (BKEND_GLX == ps->o.backend) {
|
||||
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0,
|
||||
ps->c.screen_info->root_visual, false);
|
||||
}
|
||||
if (BKEND_GLX == ps->o.backend)
|
||||
return paint_bind_tex(ps, &ps->root_tile_paint, 0, 0, true, 0, ps->vis, false);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
@@ -681,16 +670,16 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit
|
||||
xcb_render_picture_t shadow_picture = XCB_NONE, shadow_picture_argb = XCB_NONE;
|
||||
xcb_gcontext_t gc = XCB_NONE;
|
||||
|
||||
shadow_image =
|
||||
make_shadow(&ps->c, (conv *)ps->shadow_context, opacity, width, height);
|
||||
shadow_image = make_shadow(ps->c, (conv *)ps->shadow_context, opacity, width, height);
|
||||
if (!shadow_image) {
|
||||
log_error("failed to make shadow");
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
shadow_pixmap = x_create_pixmap(&ps->c, 8, shadow_image->width, shadow_image->height);
|
||||
shadow_pixmap =
|
||||
x_create_pixmap(ps->c, 8, ps->root, shadow_image->width, shadow_image->height);
|
||||
shadow_pixmap_argb =
|
||||
x_create_pixmap(&ps->c, 32, shadow_image->width, shadow_image->height);
|
||||
x_create_pixmap(ps->c, 32, ps->root, shadow_image->width, shadow_image->height);
|
||||
|
||||
if (!shadow_pixmap || !shadow_pixmap_argb) {
|
||||
log_error("failed to create shadow pixmaps");
|
||||
@@ -698,18 +687,18 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit
|
||||
}
|
||||
|
||||
shadow_picture = x_create_picture_with_standard_and_pixmap(
|
||||
&ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
|
||||
ps->c, XCB_PICT_STANDARD_A_8, shadow_pixmap, 0, NULL);
|
||||
shadow_picture_argb = x_create_picture_with_standard_and_pixmap(
|
||||
&ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
|
||||
ps->c, XCB_PICT_STANDARD_ARGB_32, shadow_pixmap_argb, 0, NULL);
|
||||
if (!shadow_picture || !shadow_picture_argb) {
|
||||
goto shadow_picture_err;
|
||||
}
|
||||
|
||||
gc = x_new_id(&ps->c);
|
||||
xcb_create_gc(ps->c.c, gc, shadow_pixmap, 0, NULL);
|
||||
gc = x_new_id(ps->c);
|
||||
xcb_create_gc(ps->c, gc, shadow_pixmap, 0, NULL);
|
||||
|
||||
xcb_image_put(ps->c.c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture,
|
||||
xcb_image_put(ps->c, shadow_pixmap, gc, shadow_image, 0, 0, 0);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, ps->cshadow_picture,
|
||||
shadow_picture, shadow_picture_argb, 0, 0, 0, 0, 0, 0,
|
||||
shadow_image->width, shadow_image->height);
|
||||
|
||||
@@ -718,32 +707,26 @@ static bool win_build_shadow(session_t *ps, struct managed_win *w, double opacit
|
||||
assert(!w->shadow_paint.pict);
|
||||
w->shadow_paint.pict = shadow_picture_argb;
|
||||
|
||||
xcb_free_gc(ps->c.c, gc);
|
||||
xcb_free_gc(ps->c, gc);
|
||||
xcb_image_destroy(shadow_image);
|
||||
xcb_free_pixmap(ps->c.c, shadow_pixmap);
|
||||
x_free_picture(&ps->c, shadow_picture);
|
||||
xcb_free_pixmap(ps->c, shadow_pixmap);
|
||||
xcb_render_free_picture(ps->c, shadow_picture);
|
||||
|
||||
return true;
|
||||
|
||||
shadow_picture_err:
|
||||
if (shadow_image) {
|
||||
if (shadow_image)
|
||||
xcb_image_destroy(shadow_image);
|
||||
}
|
||||
if (shadow_pixmap) {
|
||||
xcb_free_pixmap(ps->c.c, shadow_pixmap);
|
||||
}
|
||||
if (shadow_pixmap_argb) {
|
||||
xcb_free_pixmap(ps->c.c, shadow_pixmap_argb);
|
||||
}
|
||||
if (shadow_picture) {
|
||||
x_free_picture(&ps->c, shadow_picture);
|
||||
}
|
||||
if (shadow_picture_argb) {
|
||||
x_free_picture(&ps->c, shadow_picture_argb);
|
||||
}
|
||||
if (gc) {
|
||||
xcb_free_gc(ps->c.c, gc);
|
||||
}
|
||||
if (shadow_pixmap)
|
||||
xcb_free_pixmap(ps->c, shadow_pixmap);
|
||||
if (shadow_pixmap_argb)
|
||||
xcb_free_pixmap(ps->c, shadow_pixmap_argb);
|
||||
if (shadow_picture)
|
||||
xcb_render_free_picture(ps->c, shadow_picture);
|
||||
if (shadow_picture_argb)
|
||||
xcb_render_free_picture(ps->c, shadow_picture_argb);
|
||||
if (gc)
|
||||
xcb_free_gc(ps->c, gc);
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -772,22 +755,23 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
|
||||
traps, max_ntraps, w->corner_radius, w->widthb, w->heightb);
|
||||
|
||||
td = x_create_picture_with_standard(
|
||||
&ps->c, w->widthb, w->heightb, XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
ps->c, ps->root, w->widthb, w->heightb,
|
||||
XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
xcb_render_color_t trans = {
|
||||
.red = 0, .blue = 0, .green = 0, .alpha = 0};
|
||||
const xcb_rectangle_t rect = {.x = 0,
|
||||
.y = 0,
|
||||
.width = to_u16_checked(w->widthb),
|
||||
.height = to_u16_checked(w->heightb)};
|
||||
xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_SRC, td,
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td,
|
||||
trans, 1, &rect);
|
||||
|
||||
auto solid = solid_picture(&ps->c, false, 1, 0, 0, 0);
|
||||
auto solid = solid_picture(ps->c, ps->root, false, 1, 0, 0, 0);
|
||||
xcb_render_trapezoids(
|
||||
ps->c.c, XCB_RENDER_PICT_OP_OVER, solid, td,
|
||||
x_get_pictfmt_for_standard(&ps->c, XCB_PICT_STANDARD_A_8), 0,
|
||||
ps->c, XCB_RENDER_PICT_OP_OVER, solid, td,
|
||||
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0,
|
||||
0, n, traps);
|
||||
x_free_picture(&ps->c, solid);
|
||||
xcb_render_free_picture(ps->c, solid);
|
||||
} else {
|
||||
// Not implemented
|
||||
}
|
||||
@@ -803,7 +787,7 @@ win_paint_shadow(session_t *ps, struct managed_win *w, region_t *reg_paint) {
|
||||
w->shadow_paint.pict, w->shadow_paint.ptex, reg_paint, NULL,
|
||||
should_clip ? &clip : NULL);
|
||||
if (td) {
|
||||
x_free_picture(&ps->c, td);
|
||||
xcb_render_free_picture(ps->c, td);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,17 +815,16 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y
|
||||
|
||||
// Directly copying from tgt_buffer to it does not work, so we create a
|
||||
// Picture in the middle.
|
||||
xcb_render_picture_t tmp_picture = x_create_picture_with_visual(
|
||||
&ps->c, wid, hei, ps->c.screen_info->root_visual, 0, NULL);
|
||||
xcb_render_picture_t tmp_picture =
|
||||
x_create_picture_with_visual(ps->c, ps->root, wid, hei, ps->vis, 0, NULL);
|
||||
|
||||
if (!tmp_picture) {
|
||||
log_error("Failed to build intermediate Picture.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (reg_clip && tmp_picture) {
|
||||
x_set_picture_clip_region(&ps->c, tmp_picture, 0, 0, reg_clip);
|
||||
}
|
||||
if (reg_clip && tmp_picture)
|
||||
x_set_picture_clip_region(ps->c, tmp_picture, 0, 0, reg_clip);
|
||||
|
||||
xcb_render_picture_t src_pict = tgt_buffer, dst_pict = tmp_picture;
|
||||
for (int i = 0; i < nkernels; ++i) {
|
||||
@@ -855,9 +838,9 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y
|
||||
// be applied on source picture, to get the nearby pixels outside the
|
||||
// window.
|
||||
xcb_render_set_picture_filter(
|
||||
ps->c.c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION,
|
||||
ps->c, src_pict, strlen(XRFILTER_CONVOLUTION), XRFILTER_CONVOLUTION,
|
||||
(uint32_t)(kwid * khei + 2), convolution_blur);
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, src_pict, XCB_NONE,
|
||||
dst_pict, (rd_from_tgt ? x : 0),
|
||||
(rd_from_tgt ? y : 0), 0, 0, (rd_from_tgt ? 0 : x),
|
||||
(rd_from_tgt ? 0 : y), wid, hei);
|
||||
@@ -870,12 +853,11 @@ xr_blur_dst(session_t *ps, xcb_render_picture_t tgt_buffer, int16_t x, int16_t y
|
||||
}
|
||||
}
|
||||
|
||||
if (src_pict != tgt_buffer) {
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded,
|
||||
if (src_pict != tgt_buffer)
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, src_pict, rounded,
|
||||
tgt_buffer, 0, 0, 0, 0, x, y, wid, hei);
|
||||
}
|
||||
|
||||
x_free_picture(&ps->c, tmp_picture);
|
||||
free_picture(ps->c, &tmp_picture);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -888,8 +870,8 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
|
||||
const region_t *reg_paint) {
|
||||
const int16_t x = w->g.x;
|
||||
const int16_t y = w->g.y;
|
||||
auto const wid = to_u16_checked(w->widthb);
|
||||
auto const hei = to_u16_checked(w->heightb);
|
||||
const auto wid = to_u16_checked(w->widthb);
|
||||
const auto hei = to_u16_checked(w->heightb);
|
||||
const int cr = w ? w->corner_radius : 0;
|
||||
|
||||
double factor_center = 1.0;
|
||||
@@ -930,23 +912,23 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
|
||||
make_rounded_window_shape(traps, max_ntraps, cr, wid, hei);
|
||||
|
||||
td = x_create_picture_with_standard(
|
||||
&ps->c, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
ps->c, ps->root, wid, hei, XCB_PICT_STANDARD_ARGB_32, 0, 0);
|
||||
xcb_render_color_t trans = {
|
||||
.red = 0, .blue = 0, .green = 0, .alpha = 0};
|
||||
const xcb_rectangle_t rect = {.x = 0,
|
||||
.y = 0,
|
||||
.width = to_u16_checked(wid),
|
||||
.height = to_u16_checked(hei)};
|
||||
xcb_render_fill_rectangles(ps->c.c, XCB_RENDER_PICT_OP_SRC, td,
|
||||
xcb_render_fill_rectangles(ps->c, XCB_RENDER_PICT_OP_SRC, td,
|
||||
trans, 1, &rect);
|
||||
|
||||
auto solid = solid_picture(&ps->c, false, 1, 0, 0, 0);
|
||||
auto solid = solid_picture(ps->c, ps->root, false, 1, 0, 0, 0);
|
||||
|
||||
xcb_render_trapezoids(
|
||||
ps->c.c, XCB_RENDER_PICT_OP_OVER, solid, td,
|
||||
x_get_pictfmt_for_standard(&ps->c, XCB_PICT_STANDARD_A_8), 0,
|
||||
ps->c, XCB_RENDER_PICT_OP_OVER, solid, td,
|
||||
x_get_pictfmt_for_standard(ps->c, XCB_PICT_STANDARD_A_8), 0,
|
||||
0, n, traps);
|
||||
x_free_picture(&ps->c, solid);
|
||||
xcb_render_free_picture(ps->c, solid);
|
||||
}
|
||||
|
||||
// Minimize the region we try to blur, if the window itself is not
|
||||
@@ -966,14 +948,14 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
|
||||
xr_blur_dst(ps, tgt_buffer, x, y, wid, hei, ps->blur_kerns_cache,
|
||||
ps->o.blur_kernel_count, ®_blur, td);
|
||||
if (td) {
|
||||
x_free_picture(&ps->c, td);
|
||||
xcb_render_free_picture(ps->c, td);
|
||||
}
|
||||
pixman_region32_clear(®_blur);
|
||||
} break;
|
||||
#ifdef CONFIG_OPENGL
|
||||
case BKEND_GLX:
|
||||
// TODO(compton) Handle frame opacity
|
||||
glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5F,
|
||||
glx_blur_dst(ps, x, y, wid, hei, (float)ps->psglx->z - 0.5f,
|
||||
(float)factor_center, reg_paint, &w->glx_blur_cache);
|
||||
break;
|
||||
#endif
|
||||
@@ -987,12 +969,12 @@ win_blur_background(session_t *ps, struct managed_win *w, xcb_render_picture_t t
|
||||
/// paint all windows
|
||||
/// region = ??
|
||||
/// region_real = the damage region
|
||||
void paint_all(session_t *ps, struct managed_win *t) {
|
||||
void paint_all(session_t *ps, struct managed_win *t, bool ignore_damage) {
|
||||
if (ps->o.xrender_sync_fence || (ps->drivers & DRIVER_NVIDIA)) {
|
||||
if (ps->xsync_exists && !x_fence_sync(&ps->c, ps->sync_fence)) {
|
||||
if (ps->xsync_exists && !x_fence_sync(ps->c, ps->sync_fence)) {
|
||||
log_error("x_fence_sync failed, xrender-sync-fence will be "
|
||||
"disabled from now on.");
|
||||
xcb_sync_destroy_fence(ps->c.c, ps->sync_fence);
|
||||
xcb_sync_destroy_fence(ps->c, ps->sync_fence);
|
||||
ps->sync_fence = XCB_NONE;
|
||||
ps->o.xrender_sync_fence = false;
|
||||
ps->xsync_exists = false;
|
||||
@@ -1002,7 +984,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
region_t region;
|
||||
pixman_region32_init(®ion);
|
||||
int buffer_age = get_buffer_age(ps);
|
||||
if (buffer_age == -1 || buffer_age > ps->ndamage) {
|
||||
if (buffer_age == -1 || buffer_age > ps->ndamage || ignore_damage) {
|
||||
pixman_region32_copy(®ion, &ps->screen_reg);
|
||||
} else {
|
||||
for (int i = 0; i < get_buffer_age(ps); i++) {
|
||||
@@ -1030,7 +1012,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
if (!ps->tgt_buffer.pixmap) {
|
||||
free_paint(ps, &ps->tgt_buffer);
|
||||
ps->tgt_buffer.pixmap =
|
||||
x_create_pixmap(&ps->c, ps->c.screen_info->root_depth,
|
||||
x_create_pixmap(ps->c, (uint8_t)ps->depth, ps->root,
|
||||
ps->root_width, ps->root_height);
|
||||
if (ps->tgt_buffer.pixmap == XCB_NONE) {
|
||||
log_fatal("Failed to allocate a screen-sized pixmap for"
|
||||
@@ -1039,20 +1021,18 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
}
|
||||
}
|
||||
|
||||
if (BKEND_GLX != ps->o.backend) {
|
||||
if (BKEND_GLX != ps->o.backend)
|
||||
ps->tgt_buffer.pict = x_create_picture_with_visual_and_pixmap(
|
||||
&ps->c, ps->c.screen_info->root_visual, ps->tgt_buffer.pixmap,
|
||||
0, 0);
|
||||
}
|
||||
ps->c, ps->vis, ps->tgt_buffer.pixmap, 0, 0);
|
||||
}
|
||||
|
||||
if (BKEND_XRENDER == ps->o.backend) {
|
||||
x_set_picture_clip_region(&ps->c, ps->tgt_picture, 0, 0, ®ion);
|
||||
x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, ®ion);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (bkend_use_glx(ps)) {
|
||||
ps->psglx->z = 0;
|
||||
ps->psglx->z = 0.0;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1088,21 +1068,18 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
// Painting shadow
|
||||
if (w->shadow) {
|
||||
// Lazy shadow building
|
||||
if (!w->shadow_paint.pixmap) {
|
||||
if (!win_build_shadow(ps, w, 1)) {
|
||||
if (!w->shadow_paint.pixmap)
|
||||
if (!win_build_shadow(ps, w, 1))
|
||||
log_error("build shadow failed");
|
||||
}
|
||||
}
|
||||
|
||||
// Shadow doesn't need to be painted underneath the body
|
||||
// of the windows above. Because no one can see it
|
||||
pixman_region32_subtract(®_tmp, ®ion, w->reg_ignore);
|
||||
|
||||
// Mask out the region we don't want shadow on
|
||||
if (pixman_region32_not_empty(&ps->shadow_exclude_reg)) {
|
||||
if (pixman_region32_not_empty(&ps->shadow_exclude_reg))
|
||||
pixman_region32_subtract(®_tmp, ®_tmp,
|
||||
&ps->shadow_exclude_reg);
|
||||
}
|
||||
if (pixman_region32_not_empty(®_shadow_clip)) {
|
||||
pixman_region32_subtract(®_tmp, ®_tmp, ®_shadow_clip);
|
||||
}
|
||||
@@ -1118,12 +1095,11 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
// needed Doing it here instead of in make_shadow() for
|
||||
// saving GPU power and handling shaped windows (XXX
|
||||
// unconfirmed)
|
||||
if (!ps->o.wintype_option[w->window_type].full_shadow) {
|
||||
if (!ps->o.wintype_option[w->window_type].full_shadow)
|
||||
pixman_region32_subtract(®_tmp, ®_tmp, &bshape_no_corners);
|
||||
}
|
||||
|
||||
if (ps->o.crop_shadow_to_monitor && w->randr_monitor >= 0 &&
|
||||
w->randr_monitor < ps->monitors.count) {
|
||||
w->randr_monitor < ps->randr_nmonitors) {
|
||||
// There can be a window where number of monitors is
|
||||
// updated, but the monitor number attached to the window
|
||||
// have not.
|
||||
@@ -1133,7 +1109,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
// bounds.
|
||||
pixman_region32_intersect(
|
||||
®_tmp, ®_tmp,
|
||||
&ps->monitors.regions[w->randr_monitor]);
|
||||
&ps->randr_monitor_regs[w->randr_monitor]);
|
||||
}
|
||||
|
||||
// Detect if the region is empty before painting
|
||||
@@ -1174,8 +1150,8 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) {
|
||||
const int16_t x = w->g.x;
|
||||
const int16_t y = w->g.y;
|
||||
auto const wid = to_u16_checked(w->widthb);
|
||||
auto const hei = to_u16_checked(w->heightb);
|
||||
const auto wid = to_u16_checked(w->widthb);
|
||||
const auto hei = to_u16_checked(w->heightb);
|
||||
glx_bind_texture(ps, &w->glx_texture_bg, x, y, wid, hei);
|
||||
}
|
||||
#endif
|
||||
@@ -1195,8 +1171,8 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
// Rounded corners for XRender is implemented inside render()
|
||||
// Round window corners
|
||||
if (w->corner_radius > 0 && ps->o.backend == BKEND_GLX) {
|
||||
auto const wid = to_u16_checked(w->widthb);
|
||||
auto const hei = to_u16_checked(w->heightb);
|
||||
const auto wid = to_u16_checked(w->widthb);
|
||||
const auto hei = to_u16_checked(w->heightb);
|
||||
glx_round_corners_dst(ps, w, w->glx_texture_bg, w->g.x,
|
||||
w->g.y, wid, hei,
|
||||
(float)ps->psglx->z - 0.5F,
|
||||
@@ -1223,14 +1199,13 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
if (ps->o.vsync) {
|
||||
// Make sure all previous requests are processed to achieve best
|
||||
// effect
|
||||
x_sync(&ps->c);
|
||||
x_sync(ps->c);
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (glx_has_context(ps)) {
|
||||
if (ps->o.vsync_use_glfinish) {
|
||||
if (ps->o.vsync_use_glfinish)
|
||||
glFinish();
|
||||
} else {
|
||||
else
|
||||
glFlush();
|
||||
}
|
||||
glXWaitX();
|
||||
}
|
||||
#endif
|
||||
@@ -1251,63 +1226,57 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
|
||||
// First we create a new picture, and copy content from the buffer
|
||||
// to it
|
||||
auto pictfmt = x_get_pictform_for_visual(
|
||||
&ps->c, ps->c.screen_info->root_visual);
|
||||
auto pictfmt = x_get_pictform_for_visual(ps->c, ps->vis);
|
||||
xcb_render_picture_t new_pict = x_create_picture_with_pictfmt(
|
||||
&ps->c, rwidth, rheight, pictfmt, 0, NULL);
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->c, ps->root, rwidth, rheight, pictfmt, 0, NULL);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->tgt_buffer.pict, XCB_NONE, new_pict, 0,
|
||||
0, 0, 0, 0, 0, rwidth, rheight);
|
||||
|
||||
// Next, we set the region of paint and highlight it
|
||||
x_set_picture_clip_region(&ps->c, new_pict, 0, 0, ®ion);
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
|
||||
x_set_picture_clip_region(ps->c, new_pict, 0, 0, ®ion);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_OVER, ps->white_picture,
|
||||
ps->alpha_picts[MAX_ALPHA / 2], new_pict, 0,
|
||||
0, 0, 0, 0, 0, rwidth, rheight);
|
||||
|
||||
// Finally, clear clip regions of new_pict and the screen, and put
|
||||
// the whole thing on screen
|
||||
x_set_picture_clip_region(&ps->c, new_pict, 0, 0, &ps->screen_reg);
|
||||
x_set_picture_clip_region(&ps->c, ps->tgt_picture, 0, 0,
|
||||
&ps->screen_reg);
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC, new_pict,
|
||||
x_set_picture_clip_region(ps->c, new_pict, 0, 0, &ps->screen_reg);
|
||||
x_set_picture_clip_region(ps->c, ps->tgt_picture, 0, 0, &ps->screen_reg);
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC, new_pict,
|
||||
XCB_NONE, ps->tgt_picture, 0, 0, 0, 0, 0, 0,
|
||||
rwidth, rheight);
|
||||
x_free_picture(&ps->c, new_pict);
|
||||
} else {
|
||||
xcb_render_composite(ps->c.c, XCB_RENDER_PICT_OP_SRC,
|
||||
xcb_render_free_picture(ps->c, new_pict);
|
||||
} else
|
||||
xcb_render_composite(ps->c, XCB_RENDER_PICT_OP_SRC,
|
||||
ps->tgt_buffer.pict, XCB_NONE, ps->tgt_picture,
|
||||
0, 0, 0, 0, 0, 0, rwidth, rheight);
|
||||
}
|
||||
break;
|
||||
#ifdef CONFIG_OPENGL
|
||||
case BKEND_XR_GLX_HYBRID:
|
||||
x_sync(&ps->c);
|
||||
if (ps->o.vsync_use_glfinish) {
|
||||
x_sync(ps->c);
|
||||
if (ps->o.vsync_use_glfinish)
|
||||
glFinish();
|
||||
} else {
|
||||
else
|
||||
glFlush();
|
||||
}
|
||||
glXWaitX();
|
||||
assert(ps->tgt_buffer.pixmap);
|
||||
paint_bind_tex(ps, &ps->tgt_buffer, ps->root_width, ps->root_height,
|
||||
false, ps->c.screen_info->root_depth,
|
||||
ps->c.screen_info->root_visual, !ps->o.glx_no_rebind_pixmap);
|
||||
if (ps->o.vsync_use_glfinish) {
|
||||
false, ps->depth, ps->vis, !ps->o.glx_no_rebind_pixmap);
|
||||
if (ps->o.vsync_use_glfinish)
|
||||
glFinish();
|
||||
} else {
|
||||
else
|
||||
glFlush();
|
||||
}
|
||||
glXWaitX();
|
||||
glx_render(ps, ps->tgt_buffer.ptex, 0, 0, 0, 0, ps->root_width,
|
||||
ps->root_height, 0, 1.0, false, false, ®ion, NULL);
|
||||
fallthrough();
|
||||
case BKEND_GLX: glXSwapBuffers(ps->c.dpy, get_tgt_window(ps)); break;
|
||||
case BKEND_GLX: glXSwapBuffers(ps->dpy, get_tgt_window(ps)); break;
|
||||
#endif
|
||||
default: assert(0);
|
||||
}
|
||||
|
||||
x_sync(&ps->c);
|
||||
x_sync(ps->c);
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (glx_has_context(ps)) {
|
||||
@@ -1337,7 +1306,7 @@ void paint_all(session_t *ps, struct managed_win *t) {
|
||||
static bool xr_init_blur(session_t *ps) {
|
||||
// Query filters
|
||||
xcb_render_query_filters_reply_t *pf = xcb_render_query_filters_reply(
|
||||
ps->c.c, xcb_render_query_filters(ps->c.c, get_tgt_window(ps)), NULL);
|
||||
ps->c, xcb_render_query_filters(ps->c, get_tgt_window(ps)), NULL);
|
||||
if (pf) {
|
||||
xcb_str_iterator_t iter = xcb_render_query_filters_filters_iterator(pf);
|
||||
for (; iter.rem; xcb_str_next(&iter)) {
|
||||
@@ -1345,9 +1314,8 @@ static bool xr_init_blur(session_t *ps) {
|
||||
char *name = xcb_str_name(iter.data);
|
||||
// Check for the convolution filter
|
||||
if (strlen(XRFILTER_CONVOLUTION) == len &&
|
||||
!memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION))) {
|
||||
!memcmp(XRFILTER_CONVOLUTION, name, strlen(XRFILTER_CONVOLUTION)))
|
||||
ps->xrfilter_convolution_exists = true;
|
||||
}
|
||||
}
|
||||
free(pf);
|
||||
}
|
||||
@@ -1371,10 +1339,9 @@ static bool init_alpha_picts(session_t *ps) {
|
||||
|
||||
for (int i = 0; i <= MAX_ALPHA; ++i) {
|
||||
double o = (double)i / MAX_ALPHA;
|
||||
ps->alpha_picts[i] = solid_picture(&ps->c, false, o, 0, 0, 0);
|
||||
if (ps->alpha_picts[i] == XCB_NONE) {
|
||||
ps->alpha_picts[i] = solid_picture(ps->c, ps->root, false, o, 0, 0, 0);
|
||||
if (ps->alpha_picts[i] == XCB_NONE)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1386,13 +1353,12 @@ bool init_render(session_t *ps) {
|
||||
|
||||
// Initialize OpenGL as early as possible
|
||||
#ifdef CONFIG_OPENGL
|
||||
glxext_init(ps->c.dpy, ps->c.screen);
|
||||
glxext_init(ps->dpy, ps->scr);
|
||||
#endif
|
||||
if (bkend_use_glx(ps)) {
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (!glx_init(ps, true)) {
|
||||
if (!glx_init(ps, true))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
log_error("GLX backend support not compiled in.");
|
||||
return false;
|
||||
@@ -1407,9 +1373,8 @@ bool init_render(session_t *ps) {
|
||||
// Initialize window GL shader
|
||||
if (BKEND_GLX == ps->o.backend && ps->o.glx_fshader_win_str) {
|
||||
#ifdef CONFIG_OPENGL
|
||||
if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win)) {
|
||||
if (!glx_load_prog_main(NULL, ps->o.glx_fshader_win_str, &ps->glx_prog_win))
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
log_error("GLSL supported not compiled in, can't load "
|
||||
"shader.");
|
||||
@@ -1448,8 +1413,8 @@ bool init_render(session_t *ps) {
|
||||
}
|
||||
}
|
||||
|
||||
ps->black_picture = solid_picture(&ps->c, true, 1, 0, 0, 0);
|
||||
ps->white_picture = solid_picture(&ps->c, true, 1, 1, 1, 1);
|
||||
ps->black_picture = solid_picture(ps->c, ps->root, true, 1, 0, 0, 0);
|
||||
ps->white_picture = solid_picture(ps->c, ps->root, true, 1, 1, 1, 1);
|
||||
|
||||
if (ps->black_picture == XCB_NONE || ps->white_picture == XCB_NONE) {
|
||||
log_error("Failed to create solid xrender pictures.");
|
||||
@@ -1461,7 +1426,7 @@ bool init_render(session_t *ps) {
|
||||
if (ps->o.shadow_red == 0 && ps->o.shadow_green == 0 && ps->o.shadow_blue == 0) {
|
||||
ps->cshadow_picture = ps->black_picture;
|
||||
} else {
|
||||
ps->cshadow_picture = solid_picture(&ps->c, true, 1, ps->o.shadow_red,
|
||||
ps->cshadow_picture = solid_picture(ps->c, ps->root, true, 1, ps->o.shadow_red,
|
||||
ps->o.shadow_green, ps->o.shadow_blue);
|
||||
if (ps->cshadow_picture == XCB_NONE) {
|
||||
log_error("Failed to create shadow picture.");
|
||||
@@ -1487,14 +1452,14 @@ bool init_render(session_t *ps) {
|
||||
* Free root tile related things.
|
||||
*/
|
||||
void free_root_tile(session_t *ps) {
|
||||
x_free_picture(&ps->c, ps->root_tile_paint.pict);
|
||||
free_picture(ps->c, &ps->root_tile_paint.pict);
|
||||
#ifdef CONFIG_OPENGL
|
||||
free_texture(ps, &ps->root_tile_paint.ptex);
|
||||
#else
|
||||
assert(!ps->root_tile_paint.ptex);
|
||||
#endif
|
||||
if (ps->root_tile_fill) {
|
||||
xcb_free_pixmap(ps->c.c, ps->root_tile_paint.pixmap);
|
||||
xcb_free_pixmap(ps->c, ps->root_tile_paint.pixmap);
|
||||
ps->root_tile_paint.pixmap = XCB_NONE;
|
||||
}
|
||||
ps->root_tile_paint.pixmap = XCB_NONE;
|
||||
@@ -1503,20 +1468,19 @@ void free_root_tile(session_t *ps) {
|
||||
|
||||
void deinit_render(session_t *ps) {
|
||||
// Free alpha_picts
|
||||
for (int i = 0; i <= MAX_ALPHA; ++i) {
|
||||
x_free_picture(&ps->c, ps->alpha_picts[i]);
|
||||
}
|
||||
for (int i = 0; i <= MAX_ALPHA; ++i)
|
||||
free_picture(ps->c, &ps->alpha_picts[i]);
|
||||
free(ps->alpha_picts);
|
||||
ps->alpha_picts = NULL;
|
||||
|
||||
// Free cshadow_picture and black_picture
|
||||
if (ps->cshadow_picture != ps->black_picture) {
|
||||
x_free_picture(&ps->c, ps->cshadow_picture);
|
||||
}
|
||||
if (ps->cshadow_picture == ps->black_picture)
|
||||
ps->cshadow_picture = XCB_NONE;
|
||||
else
|
||||
free_picture(ps->c, &ps->cshadow_picture);
|
||||
|
||||
x_free_picture(&ps->c, ps->black_picture);
|
||||
x_free_picture(&ps->c, ps->white_picture);
|
||||
ps->cshadow_picture = ps->black_picture = ps->white_picture = XCB_NONE;
|
||||
free_picture(ps->c, &ps->black_picture);
|
||||
free_picture(ps->c, &ps->white_picture);
|
||||
|
||||
// Free other X resources
|
||||
free_root_tile(ps);
|
||||
|
||||
@@ -37,7 +37,9 @@ void render(session_t *ps, int x, int y, int dx, int dy, int w, int h, int fullw
|
||||
const glx_prog_main_t *pprogram, clip_t *clip);
|
||||
void paint_one(session_t *ps, struct managed_win *w, const region_t *reg_paint);
|
||||
|
||||
void paint_all(session_t *ps, struct managed_win *const t);
|
||||
void paint_all(session_t *ps, struct managed_win *const t, bool ignore_damage);
|
||||
|
||||
void free_picture(xcb_connection_t *c, xcb_render_picture_t *p);
|
||||
|
||||
void free_paint(session_t *ps, paint_t *ppaint);
|
||||
void free_root_tile(session_t *ps);
|
||||
|
||||
@@ -1,89 +0,0 @@
|
||||
//! Rendering statistics
|
||||
//!
|
||||
//! Tracks how long it takes to render a frame, for measuring performance, and for pacing
|
||||
//! the frames.
|
||||
|
||||
#include "statistics.h"
|
||||
#include "log.h"
|
||||
#include "utils.h"
|
||||
|
||||
void render_statistics_init(struct render_statistics *rs, int window_size) {
|
||||
*rs = (struct render_statistics){0};
|
||||
|
||||
rolling_window_init(&rs->render_times, window_size);
|
||||
rolling_quantile_init_with_tolerance(&rs->render_time_quantile, window_size,
|
||||
/* q */ 0.98, /* tolerance */ 0.01);
|
||||
}
|
||||
|
||||
void render_statistics_add_vblank_time_sample(struct render_statistics *rs, int time_us) {
|
||||
auto sample_sd = sqrt(cumulative_mean_and_var_get_var(&rs->vblank_time_us));
|
||||
auto current_estimate = render_statistics_get_vblank_time(rs);
|
||||
if (current_estimate != 0 && fabs((double)time_us - current_estimate) > sample_sd * 3) {
|
||||
// Deviated from the mean by more than 3 sigma (p < 0.003)
|
||||
log_debug("vblank time outlier: %d %f %f", time_us, rs->vblank_time_us.mean,
|
||||
cumulative_mean_and_var_get_var(&rs->vblank_time_us));
|
||||
// An outlier sample, this could mean things like refresh rate changes, so
|
||||
// we reset the statistics. This could also be benign, but we like to be
|
||||
// cautious.
|
||||
cumulative_mean_and_var_init(&rs->vblank_time_us);
|
||||
}
|
||||
|
||||
if (rs->vblank_time_us.mean != 0) {
|
||||
auto nframes_in_10_seconds =
|
||||
(unsigned int)(10. * 1000000. / rs->vblank_time_us.mean);
|
||||
if (rs->vblank_time_us.n > 20 && rs->vblank_time_us.n > nframes_in_10_seconds) {
|
||||
// We collected 10 seconds worth of samples, we assume the
|
||||
// estimated refresh rate is stable. We will still reset the
|
||||
// statistics if we get an outlier sample though, see above.
|
||||
return;
|
||||
}
|
||||
}
|
||||
cumulative_mean_and_var_update(&rs->vblank_time_us, time_us);
|
||||
}
|
||||
|
||||
void render_statistics_add_render_time_sample(struct render_statistics *rs, int time_us) {
|
||||
int oldest;
|
||||
if (rolling_window_push_back(&rs->render_times, time_us, &oldest)) {
|
||||
rolling_quantile_pop_front(&rs->render_time_quantile, oldest);
|
||||
}
|
||||
|
||||
rolling_quantile_push_back(&rs->render_time_quantile, time_us);
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
if (rs->render_times.nelem < rs->render_times.window_size) {
|
||||
// No valid render time estimates yet. Assume maximum budget.
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
// N-th percentile of render times, see render_statistics_init for N.
|
||||
auto render_time_percentile =
|
||||
rolling_quantile_estimate(&rs->render_time_quantile, &rs->render_times);
|
||||
return (unsigned int)render_time_percentile;
|
||||
}
|
||||
|
||||
unsigned int render_statistics_get_vblank_time(struct render_statistics *rs) {
|
||||
if (rs->vblank_time_us.n <= 20 || rs->vblank_time_us.mean < 100) {
|
||||
// Not enough samples yet, or the vblank time is too short to be
|
||||
// meaningful. Assume maximum budget.
|
||||
return 0;
|
||||
}
|
||||
return (unsigned int)rs->vblank_time_us.mean;
|
||||
}
|
||||
|
||||
void render_statistics_reset(struct render_statistics *rs) {
|
||||
rolling_window_reset(&rs->render_times);
|
||||
rolling_quantile_reset(&rs->render_time_quantile);
|
||||
rs->vblank_time_us = (struct cumulative_mean_and_var){0};
|
||||
}
|
||||
|
||||
void render_statistics_destroy(struct render_statistics *rs) {
|
||||
render_statistics_reset(rs);
|
||||
rolling_window_destroy(&rs->render_times);
|
||||
rolling_quantile_destroy(&rs->render_time_quantile);
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#define NTIERS (3)
|
||||
|
||||
struct render_statistics {
|
||||
/// Rolling window of rendering times (in us) and the tiers they belong to.
|
||||
/// We keep track of the tiers because the vblank time estimate can change over
|
||||
/// time.
|
||||
struct rolling_window render_times;
|
||||
/// Estimate the 95-th percentile of rendering times
|
||||
struct rolling_quantile render_time_quantile;
|
||||
/// Time between each vblanks
|
||||
struct cumulative_mean_and_var vblank_time_us;
|
||||
};
|
||||
|
||||
void render_statistics_init(struct render_statistics *rs, int window_size);
|
||||
void render_statistics_reset(struct render_statistics *rs);
|
||||
void render_statistics_destroy(struct render_statistics *rs);
|
||||
|
||||
void render_statistics_add_vblank_time_sample(struct render_statistics *rs, int time_us);
|
||||
void render_statistics_add_render_time_sample(struct render_statistics *rs, int time_us);
|
||||
|
||||
/// How much time budget we should give to the backend for rendering, in microseconds.
|
||||
unsigned int render_statistics_get_budget(struct render_statistics *rs);
|
||||
|
||||
/// Return the measured vblank interval in microseconds. Returns 0 if not enough
|
||||
/// samples have been collected yet.
|
||||
unsigned int render_statistics_get_vblank_time(struct render_statistics *rs);
|
||||
@@ -24,9 +24,8 @@ static inline int uitostr(unsigned int n, char *buf) {
|
||||
ret++;
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (ret == 0)
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
int pos = ret;
|
||||
while (pos--) {
|
||||
@@ -37,22 +36,18 @@ static inline int uitostr(unsigned int n, char *buf) {
|
||||
}
|
||||
|
||||
static inline const char *skip_space_const(const char *src) {
|
||||
if (!src) {
|
||||
if (!src)
|
||||
return NULL;
|
||||
}
|
||||
while (*src && isspace((unsigned char)*src)) {
|
||||
while (*src && isspace((unsigned char)*src))
|
||||
src++;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
static inline char *skip_space_mut(char *src) {
|
||||
if (!src) {
|
||||
if (!src)
|
||||
return NULL;
|
||||
}
|
||||
while (*src && isspace((unsigned char)*src)) {
|
||||
while (*src && isspace((unsigned char)*src))
|
||||
src++;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
|
||||
245
src/utils.c
245
src/utils.c
@@ -30,7 +30,7 @@ void report_allocation_failure(const char *func, const char *file, unsigned int
|
||||
ssize_t _ attr_unused = writev(STDERR_FILENO, v, ARR_SIZE(v));
|
||||
abort();
|
||||
|
||||
unreachable();
|
||||
unreachable;
|
||||
}
|
||||
|
||||
///
|
||||
@@ -48,43 +48,7 @@ int next_power_of_two(int n) {
|
||||
return n;
|
||||
}
|
||||
|
||||
void rolling_window_destroy(struct rolling_window *rw) {
|
||||
free(rw->elem);
|
||||
rw->elem = NULL;
|
||||
}
|
||||
|
||||
void rolling_window_reset(struct rolling_window *rw) {
|
||||
rw->nelem = 0;
|
||||
rw->elem_head = 0;
|
||||
}
|
||||
|
||||
void rolling_window_init(struct rolling_window *rw, int size) {
|
||||
rw->elem = ccalloc(size, int);
|
||||
rw->window_size = size;
|
||||
rolling_window_reset(rw);
|
||||
}
|
||||
|
||||
int rolling_window_pop_front(struct rolling_window *rw) {
|
||||
assert(rw->nelem > 0);
|
||||
auto ret = rw->elem[rw->elem_head];
|
||||
rw->elem_head = (rw->elem_head + 1) % rw->window_size;
|
||||
rw->nelem--;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool rolling_window_push_back(struct rolling_window *rw, int val, int *front) {
|
||||
bool full = rw->nelem == rw->window_size;
|
||||
if (full) {
|
||||
*front = rolling_window_pop_front(rw);
|
||||
}
|
||||
rw->elem[(rw->elem_head + rw->nelem) % rw->window_size] = val;
|
||||
rw->nelem++;
|
||||
return full;
|
||||
}
|
||||
|
||||
/// Track the maximum member of a FIFO queue of integers. Integers are pushed to the back
|
||||
/// and popped from the front, the maximum of the current members in the queue is
|
||||
/// tracked.
|
||||
/// Track the rolling maximum of a stream of integers.
|
||||
struct rolling_max {
|
||||
/// A priority queue holding the indices of the maximum element candidates.
|
||||
/// The head of the queue is the index of the maximum element.
|
||||
@@ -95,26 +59,32 @@ struct rolling_max {
|
||||
/// it's called the "original" indices.
|
||||
int *p;
|
||||
int p_head, np;
|
||||
/// The maximum number of in flight elements.
|
||||
int capacity;
|
||||
|
||||
/// The elemets
|
||||
int *elem;
|
||||
int elem_head, nelem;
|
||||
|
||||
int window_size;
|
||||
};
|
||||
|
||||
void rolling_max_destroy(struct rolling_max *rm) {
|
||||
free(rm->elem);
|
||||
free(rm->p);
|
||||
free(rm);
|
||||
}
|
||||
|
||||
struct rolling_max *rolling_max_new(int capacity) {
|
||||
struct rolling_max *rolling_max_new(int size) {
|
||||
auto rm = ccalloc(1, struct rolling_max);
|
||||
if (!rm) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
rm->p = ccalloc(capacity, int);
|
||||
if (!rm->p) {
|
||||
rm->p = ccalloc(size, int);
|
||||
rm->elem = ccalloc(size, int);
|
||||
rm->window_size = size;
|
||||
if (!rm->p || !rm->elem) {
|
||||
goto err;
|
||||
}
|
||||
rm->capacity = capacity;
|
||||
|
||||
return rm;
|
||||
|
||||
@@ -126,21 +96,33 @@ err:
|
||||
void rolling_max_reset(struct rolling_max *rm) {
|
||||
rm->p_head = 0;
|
||||
rm->np = 0;
|
||||
rm->nelem = 0;
|
||||
rm->elem_head = 0;
|
||||
}
|
||||
|
||||
#define IDX(n) ((n) % rm->capacity)
|
||||
/// Remove the oldest element in the window. The caller must maintain the list of elements
|
||||
/// themselves, i.e. the behavior is undefined if `front` does not 1match the oldest
|
||||
/// element.
|
||||
void rolling_max_pop_front(struct rolling_max *rm, int front) {
|
||||
if (rm->p[rm->p_head] == front) {
|
||||
// rm->p.pop_front()
|
||||
rm->p_head = IDX(rm->p_head + 1);
|
||||
rm->np--;
|
||||
void rolling_max_push(struct rolling_max *rm, int val) {
|
||||
#define IDX(n) ((n) % rm->window_size)
|
||||
if (rm->nelem == rm->window_size) {
|
||||
auto old_head = rm->elem_head;
|
||||
// Discard the oldest element.
|
||||
// rm->elem.pop_front();
|
||||
rm->nelem--;
|
||||
rm->elem_head = IDX(rm->elem_head + 1);
|
||||
|
||||
// Remove discarded element from the priority queue too.
|
||||
assert(rm->np);
|
||||
if (rm->p[rm->p_head] == old_head) {
|
||||
// rm->p.pop_front()
|
||||
rm->p_head = IDX(rm->p_head + 1);
|
||||
rm->np--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void rolling_max_push_back(struct rolling_max *rm, int val) {
|
||||
// Add the new element to the queue.
|
||||
// rm->elem.push_back(val)
|
||||
rm->elem[IDX(rm->elem_head + rm->nelem)] = val;
|
||||
rm->nelem++;
|
||||
|
||||
// Update the prority queue.
|
||||
// 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
|
||||
@@ -148,7 +130,7 @@ void rolling_max_push_back(struct rolling_max *rm, int val) {
|
||||
// element, so they will never become the maximum element.
|
||||
while (rm->np) {
|
||||
int p_tail = IDX(rm->p_head + rm->np - 1);
|
||||
if (rm->p[p_tail] > val) {
|
||||
if (rm->elem[rm->p[p_tail]] > val) {
|
||||
break;
|
||||
}
|
||||
// rm->p.pop_back()
|
||||
@@ -156,119 +138,108 @@ void rolling_max_push_back(struct rolling_max *rm, int val) {
|
||||
}
|
||||
// Add the new element to the end of the queue.
|
||||
// rm->p.push_back(rm->start_index + rm->nelem - 1)
|
||||
assert(rm->np < rm->capacity);
|
||||
rm->p[IDX(rm->p_head + rm->np)] = val;
|
||||
rm->p[IDX(rm->p_head + rm->np)] = IDX(rm->elem_head + rm->nelem - 1);
|
||||
rm->np++;
|
||||
}
|
||||
#undef IDX
|
||||
}
|
||||
|
||||
int rolling_max_get_max(struct rolling_max *rm) {
|
||||
if (rm->np == 0) {
|
||||
return INT_MIN;
|
||||
}
|
||||
return rm->p[rm->p_head];
|
||||
return rm->elem[rm->p[rm->p_head]];
|
||||
}
|
||||
|
||||
TEST_CASE(rolling_max_test) {
|
||||
#define NELEM 15
|
||||
struct rolling_window queue;
|
||||
rolling_window_init(&queue, 3);
|
||||
auto rm = rolling_max_new(3);
|
||||
const int data[NELEM] = {1, 2, 3, 1, 4, 5, 2, 3, 6, 5, 4, 3, 2, 0, 0};
|
||||
const int expected_max[NELEM] = {1, 2, 3, 3, 4, 5, 5, 5, 6, 6, 6, 5, 4, 3, 2};
|
||||
int max[NELEM] = {0};
|
||||
for (int i = 0; i < NELEM; i++) {
|
||||
int front;
|
||||
bool full = rolling_window_push_back(&queue, data[i], &front);
|
||||
if (full) {
|
||||
rolling_max_pop_front(rm, front);
|
||||
}
|
||||
rolling_max_push_back(rm, data[i]);
|
||||
rolling_max_push(rm, data[i]);
|
||||
max[i] = rolling_max_get_max(rm);
|
||||
}
|
||||
rolling_window_destroy(&queue);
|
||||
rolling_max_destroy(rm);
|
||||
TEST_TRUE(memcmp(max, expected_max, sizeof(max)) == 0);
|
||||
#undef NELEM
|
||||
}
|
||||
|
||||
// Find the k-th smallest element in an array.
|
||||
int quickselect(int *elems, int nelem, int k) {
|
||||
int l = 0, r = nelem; // [l, r) is the range of candidates
|
||||
while (l != r) {
|
||||
int pivot = elems[l];
|
||||
int i = l, j = r;
|
||||
while (i < j) {
|
||||
while (i < j && elems[--j] >= pivot) {
|
||||
}
|
||||
elems[i] = elems[j];
|
||||
while (i < j && elems[++i] <= pivot) {
|
||||
}
|
||||
elems[j] = elems[i];
|
||||
}
|
||||
elems[i] = pivot;
|
||||
/// A rolling average of a stream of integers.
|
||||
struct rolling_avg {
|
||||
/// The sum of the elements in the window.
|
||||
int64_t sum;
|
||||
|
||||
if (i == k) {
|
||||
break;
|
||||
}
|
||||
/// The elements in the window.
|
||||
int *elem;
|
||||
int head, nelem;
|
||||
|
||||
if (i < k) {
|
||||
l = i + 1;
|
||||
} else {
|
||||
r = i;
|
||||
}
|
||||
int window_size;
|
||||
};
|
||||
|
||||
struct rolling_avg *rolling_avg_new(int size) {
|
||||
auto rm = ccalloc(1, struct rolling_avg);
|
||||
if (!rm) {
|
||||
return NULL;
|
||||
}
|
||||
return elems[k];
|
||||
}
|
||||
|
||||
void rolling_quantile_init(struct rolling_quantile *rq, int capacity, int mink, int maxk) {
|
||||
*rq = (struct rolling_quantile){0};
|
||||
rq->tmp_buffer = malloc(sizeof(int) * (size_t)capacity);
|
||||
rq->capacity = capacity;
|
||||
rq->min_target_rank = mink;
|
||||
rq->max_target_rank = maxk;
|
||||
}
|
||||
|
||||
void rolling_quantile_init_with_tolerance(struct rolling_quantile *rq, int window_size,
|
||||
double target, double tolerance) {
|
||||
rolling_quantile_init(rq, window_size, (int)((target - tolerance) * window_size),
|
||||
(int)((target + tolerance) * window_size));
|
||||
}
|
||||
|
||||
void rolling_quantile_reset(struct rolling_quantile *rq) {
|
||||
rq->current_rank = 0;
|
||||
rq->estimate = 0;
|
||||
}
|
||||
|
||||
void rolling_quantile_destroy(struct rolling_quantile *rq) {
|
||||
free(rq->tmp_buffer);
|
||||
}
|
||||
|
||||
int rolling_quantile_estimate(struct rolling_quantile *rq, struct rolling_window *elements) {
|
||||
if (rq->current_rank < rq->min_target_rank || rq->current_rank > rq->max_target_rank) {
|
||||
if (elements->nelem != elements->window_size) {
|
||||
return INT_MIN;
|
||||
}
|
||||
// Re-estimate the quantile.
|
||||
assert(elements->nelem <= rq->capacity);
|
||||
rolling_window_copy_to_array(elements, rq->tmp_buffer);
|
||||
const int target_rank =
|
||||
rq->min_target_rank + (rq->max_target_rank - rq->min_target_rank) / 2;
|
||||
rq->estimate = quickselect(rq->tmp_buffer, elements->nelem, target_rank);
|
||||
rq->current_rank = target_rank;
|
||||
rm->elem = ccalloc(size, int);
|
||||
rm->window_size = size;
|
||||
if (!rm->elem) {
|
||||
free(rm);
|
||||
return NULL;
|
||||
}
|
||||
return rq->estimate;
|
||||
|
||||
return rm;
|
||||
}
|
||||
|
||||
void rolling_quantile_push_back(struct rolling_quantile *rq, int x) {
|
||||
if (x <= rq->estimate) {
|
||||
rq->current_rank++;
|
||||
void rolling_avg_destroy(struct rolling_avg *rm) {
|
||||
free(rm->elem);
|
||||
free(rm);
|
||||
}
|
||||
|
||||
void rolling_avg_reset(struct rolling_avg *ra) {
|
||||
ra->sum = 0;
|
||||
ra->nelem = 0;
|
||||
ra->head = 0;
|
||||
}
|
||||
|
||||
void rolling_avg_push(struct rolling_avg *ra, int val) {
|
||||
if (ra->nelem == ra->window_size) {
|
||||
// Discard the oldest element.
|
||||
// rm->elem.pop_front();
|
||||
ra->sum -= ra->elem[ra->head % ra->window_size];
|
||||
ra->nelem--;
|
||||
ra->head = (ra->head + 1) % ra->window_size;
|
||||
}
|
||||
|
||||
// Add the new element to the queue.
|
||||
// rm->elem.push_back(val)
|
||||
ra->elem[(ra->head + ra->nelem) % ra->window_size] = val;
|
||||
ra->sum += val;
|
||||
ra->nelem++;
|
||||
}
|
||||
|
||||
void rolling_quantile_pop_front(struct rolling_quantile *rq, int x) {
|
||||
if (x <= rq->estimate) {
|
||||
rq->current_rank--;
|
||||
double rolling_avg_get_avg(struct rolling_avg *ra) {
|
||||
if (ra->nelem == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (double)ra->sum / (double)ra->nelem;
|
||||
}
|
||||
|
||||
TEST_CASE(rolling_avg_test) {
|
||||
#define NELEM 15
|
||||
auto rm = rolling_avg_new(3);
|
||||
const int data[NELEM] = {1, 2, 3, 1, 4, 5, 2, 3, 6, 5, 4, 3, 2, 0, 0};
|
||||
const double expected_avg[NELEM] = {
|
||||
1, 1.5, 2, 2, 8.0 / 3.0, 10.0 / 3.0, 11.0 / 3.0, 10.0 / 3.0,
|
||||
11.0 / 3.0, 14.0 / 3.0, 5, 4, 3, 5.0 / 3.0, 2.0 / 3.0};
|
||||
double avg[NELEM] = {0};
|
||||
for (int i = 0; i < NELEM; i++) {
|
||||
rolling_avg_push(rm, data[i]);
|
||||
avg[i] = rolling_avg_get_avg(rm);
|
||||
}
|
||||
for (int i = 0; i < NELEM; i++) {
|
||||
TEST_EQUAL(avg[i], expected_avg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
118
src/utils.h
118
src/utils.h
@@ -17,7 +17,6 @@
|
||||
#include <time.h>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "log.h"
|
||||
#include "types.h"
|
||||
|
||||
#define ARR_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
@@ -126,13 +125,11 @@ safe_isnan(double a) {
|
||||
* @param max maximum value
|
||||
* @return normalized value
|
||||
*/
|
||||
static inline int attr_const attr_unused normalize_i_range(int i, int min, int max) {
|
||||
if (i > max) {
|
||||
static inline int attr_const normalize_i_range(int i, int min, int max) {
|
||||
if (i > max)
|
||||
return max;
|
||||
}
|
||||
if (i < min) {
|
||||
if (i < min)
|
||||
return min;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@@ -149,12 +146,6 @@ static inline int attr_const lerp_range(int a, int b, int c, int d, int value) {
|
||||
return (d-c)*(value-a)/(b-a) + c;
|
||||
}
|
||||
|
||||
/// Generic integer abs()
|
||||
#define iabs(val) \
|
||||
({ \
|
||||
__auto_type __tmp = (val); \
|
||||
__tmp > 0 ? __tmp : -__tmp; \
|
||||
})
|
||||
#define min2(a, b) ((a) > (b) ? (b) : (a))
|
||||
#define max2(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define min3(a, b, c) min2(a, min2(b, c))
|
||||
@@ -171,12 +162,10 @@ static inline int attr_const lerp_range(int a, int b, int c, int d, int value) {
|
||||
* @return normalized value
|
||||
*/
|
||||
static inline double attr_const normalize_d_range(double d, double min, double max) {
|
||||
if (d > max) {
|
||||
if (d > max)
|
||||
return max;
|
||||
}
|
||||
if (d < min) {
|
||||
if (d < min)
|
||||
return min;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
@@ -186,7 +175,7 @@ static inline double attr_const normalize_d_range(double d, double min, double m
|
||||
* @param d double value to normalize
|
||||
* @return normalized value
|
||||
*/
|
||||
static inline double attr_const attr_unused normalize_d(double d) {
|
||||
static inline double attr_const normalize_d(double d) {
|
||||
return normalize_d_range(d, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@@ -314,97 +303,20 @@ static inline void free_charpp(char **str) {
|
||||
///
|
||||
int next_power_of_two(int n);
|
||||
|
||||
struct rolling_window {
|
||||
int *elem;
|
||||
int elem_head, nelem;
|
||||
int window_size;
|
||||
};
|
||||
|
||||
void rolling_window_destroy(struct rolling_window *rw);
|
||||
void rolling_window_reset(struct rolling_window *rw);
|
||||
void rolling_window_init(struct rolling_window *rw, int size);
|
||||
int rolling_window_pop_front(struct rolling_window *rw);
|
||||
bool rolling_window_push_back(struct rolling_window *rw, int val, int *front);
|
||||
|
||||
/// Copy the contents of the rolling window to an array. The array is assumed to
|
||||
/// have enough space to hold the contents of the rolling window.
|
||||
static inline void attr_unused rolling_window_copy_to_array(struct rolling_window *rw,
|
||||
int *arr) {
|
||||
// The length from head to the end of the array
|
||||
auto head_len = (size_t)(rw->window_size - rw->elem_head);
|
||||
if (head_len >= (size_t)rw->nelem) {
|
||||
memcpy(arr, rw->elem + rw->elem_head, sizeof(int) * (size_t)rw->nelem);
|
||||
} else {
|
||||
auto tail_len = (size_t)((size_t)rw->nelem - head_len);
|
||||
memcpy(arr, rw->elem + rw->elem_head, sizeof(int) * head_len);
|
||||
memcpy(arr + head_len, rw->elem, sizeof(int) * tail_len);
|
||||
}
|
||||
}
|
||||
|
||||
struct rolling_max;
|
||||
|
||||
struct rolling_max *rolling_max_new(int capacity);
|
||||
void rolling_max_destroy(struct rolling_max *rm);
|
||||
struct rolling_max *rolling_max_new(int window_size);
|
||||
void rolling_max_free(struct rolling_max *rm);
|
||||
void rolling_max_reset(struct rolling_max *rm);
|
||||
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(struct rolling_max *rm, int val);
|
||||
int rolling_max_get_max(struct rolling_max *rm);
|
||||
|
||||
/// Estimate the mean and variance of random variable X using Welford's online
|
||||
/// algorithm.
|
||||
struct cumulative_mean_and_var {
|
||||
double mean;
|
||||
double m2;
|
||||
unsigned int n;
|
||||
};
|
||||
|
||||
static inline attr_unused void
|
||||
cumulative_mean_and_var_init(struct cumulative_mean_and_var *cmv) {
|
||||
*cmv = (struct cumulative_mean_and_var){0};
|
||||
}
|
||||
|
||||
static inline attr_unused void
|
||||
cumulative_mean_and_var_update(struct cumulative_mean_and_var *cmv, double x) {
|
||||
if (cmv->n == UINT_MAX) {
|
||||
// We have too many elements, let's keep the mean and variance.
|
||||
return;
|
||||
}
|
||||
cmv->n++;
|
||||
double delta = x - cmv->mean;
|
||||
cmv->mean += delta / (double)cmv->n;
|
||||
cmv->m2 += delta * (x - cmv->mean);
|
||||
}
|
||||
|
||||
static inline attr_unused double
|
||||
cumulative_mean_and_var_get_var(struct cumulative_mean_and_var *cmv) {
|
||||
if (cmv->n < 2) {
|
||||
return 0;
|
||||
}
|
||||
return cmv->m2 / (double)(cmv->n - 1);
|
||||
}
|
||||
|
||||
// Find the k-th smallest element in an array.
|
||||
int quickselect(int *elems, int nelem, int k);
|
||||
|
||||
/// A naive quantile estimator.
|
||||
///
|
||||
/// Estimates the N-th percentile of a random variable X in a sliding window.
|
||||
struct rolling_quantile {
|
||||
int current_rank;
|
||||
int min_target_rank, max_target_rank;
|
||||
int estimate;
|
||||
int capacity;
|
||||
int *tmp_buffer;
|
||||
};
|
||||
|
||||
void rolling_quantile_init(struct rolling_quantile *rq, int capacity, int mink, int maxk);
|
||||
void rolling_quantile_init_with_tolerance(struct rolling_quantile *rq, int window_size,
|
||||
double target, double tolerance);
|
||||
void rolling_quantile_reset(struct rolling_quantile *rq);
|
||||
void rolling_quantile_destroy(struct rolling_quantile *rq);
|
||||
int rolling_quantile_estimate(struct rolling_quantile *rq, struct rolling_window *elements);
|
||||
void rolling_quantile_push_back(struct rolling_quantile *rq, int x);
|
||||
void rolling_quantile_pop_front(struct rolling_quantile *rq, int x);
|
||||
struct rolling_avg;
|
||||
struct rolling_avg *rolling_avg_new(int window_size);
|
||||
void rolling_avg_free(struct rolling_avg *ra);
|
||||
void rolling_avg_reset(struct rolling_avg *ra);
|
||||
void rolling_avg_push(struct rolling_avg *ra, int val);
|
||||
double rolling_avg_get_avg(struct rolling_avg *ra);
|
||||
|
||||
// Some versions of the Android libc do not have timespec_get(), use
|
||||
// clock_gettime() instead.
|
||||
|
||||
530
src/vblank.c
530
src/vblank.c
@@ -1,530 +0,0 @@
|
||||
#include <assert.h>
|
||||
|
||||
#include <ev.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdatomic.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xproto.h>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
// Enable sgi_video_sync_vblank_scheduler
|
||||
#include <GL/glx.h>
|
||||
#include <X11/X.h>
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "backend/gl/glx.h"
|
||||
#endif
|
||||
|
||||
#include "compiler.h"
|
||||
#include "list.h" // for container_of
|
||||
#include "log.h"
|
||||
#include "vblank.h"
|
||||
#include "x.h"
|
||||
|
||||
struct vblank_callback {
|
||||
vblank_callback_t fn;
|
||||
void *user_data;
|
||||
};
|
||||
|
||||
#define VBLANK_WIND_DOWN 4
|
||||
|
||||
struct vblank_scheduler {
|
||||
struct x_connection *c;
|
||||
size_t callback_capacity, callback_count;
|
||||
struct vblank_callback *callbacks;
|
||||
struct ev_loop *loop;
|
||||
/// Request extra vblank events even when no callbacks are scheduled.
|
||||
/// This is because when callbacks are scheduled too close to a vblank,
|
||||
/// we might send PresentNotifyMsc request too late and miss the vblank event.
|
||||
/// So we request extra vblank events right after the last vblank event
|
||||
/// to make sure this doesn't happen.
|
||||
unsigned int wind_down;
|
||||
xcb_window_t target_window;
|
||||
enum vblank_scheduler_type type;
|
||||
bool vblank_event_requested;
|
||||
};
|
||||
|
||||
struct present_vblank_scheduler {
|
||||
struct vblank_scheduler base;
|
||||
|
||||
uint64_t last_msc;
|
||||
/// The timestamp for the end of last vblank.
|
||||
uint64_t last_ust;
|
||||
ev_timer callback_timer;
|
||||
xcb_present_event_t event_id;
|
||||
xcb_special_event_t *event;
|
||||
};
|
||||
|
||||
struct vblank_scheduler_ops {
|
||||
size_t size;
|
||||
void (*init)(struct vblank_scheduler *self);
|
||||
void (*deinit)(struct vblank_scheduler *self);
|
||||
void (*schedule)(struct vblank_scheduler *self);
|
||||
bool (*handle_x_events)(struct vblank_scheduler *self);
|
||||
};
|
||||
|
||||
static void
|
||||
vblank_scheduler_invoke_callbacks(struct vblank_scheduler *self, struct vblank_event *event);
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
struct sgi_video_sync_vblank_scheduler {
|
||||
struct vblank_scheduler base;
|
||||
|
||||
// Since glXWaitVideoSyncSGI blocks, we need to run it in a separate thread.
|
||||
// ... and all the thread shenanigans that come with it.
|
||||
_Atomic unsigned int last_msc;
|
||||
_Atomic uint64_t last_ust;
|
||||
ev_async notify;
|
||||
pthread_t sync_thread;
|
||||
bool running, error;
|
||||
|
||||
/// Protects `running`, `error` and `base.vblank_event_requested`
|
||||
pthread_mutex_t vblank_requested_mtx;
|
||||
pthread_cond_t vblank_requested_cnd;
|
||||
};
|
||||
|
||||
struct sgi_video_sync_thread_args {
|
||||
struct sgi_video_sync_vblank_scheduler *self;
|
||||
int start_status;
|
||||
pthread_mutex_t start_mtx;
|
||||
pthread_cond_t start_cnd;
|
||||
};
|
||||
|
||||
static bool check_sgi_video_sync_extension(Display *dpy, int screen) {
|
||||
const char *glx_ext = glXQueryExtensionsString(dpy, screen);
|
||||
const char *needle = "GLX_SGI_video_sync";
|
||||
char *found = strstr(glx_ext, needle);
|
||||
if (!found) {
|
||||
return false;
|
||||
}
|
||||
if (found != glx_ext && found[-1] != ' ') {
|
||||
return false;
|
||||
}
|
||||
if (found[strlen(needle)] != ' ' && found[strlen(needle)] != '\0') {
|
||||
return false;
|
||||
}
|
||||
|
||||
glXWaitVideoSyncSGI = (PFNGLXWAITVIDEOSYNCSGIPROC)(void *)glXGetProcAddress(
|
||||
(const GLubyte *)"glXWaitVideoSyncSGI");
|
||||
if (!glXWaitVideoSyncSGI) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void *sgi_video_sync_thread(void *data) {
|
||||
auto args = (struct sgi_video_sync_thread_args *)data;
|
||||
auto self = args->self;
|
||||
Display *dpy = XOpenDisplay(NULL);
|
||||
int error_code = 0;
|
||||
if (!dpy) {
|
||||
error_code = 1;
|
||||
goto start_failed;
|
||||
}
|
||||
Window root = DefaultRootWindow(dpy), dummy = None;
|
||||
int screen = DefaultScreen(dpy);
|
||||
int ncfg = 0;
|
||||
GLXFBConfig *cfg_ = glXChooseFBConfig(
|
||||
dpy, screen,
|
||||
(int[]){GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 0},
|
||||
&ncfg);
|
||||
GLXContext ctx = NULL;
|
||||
GLXDrawable drawable = None;
|
||||
|
||||
if (!cfg_) {
|
||||
error_code = 2;
|
||||
goto start_failed;
|
||||
}
|
||||
GLXFBConfig cfg = cfg_[0];
|
||||
XFree(cfg_);
|
||||
|
||||
XVisualInfo *vi = glXGetVisualFromFBConfig(dpy, cfg);
|
||||
if (!vi) {
|
||||
error_code = 3;
|
||||
goto start_failed;
|
||||
}
|
||||
|
||||
Visual *visual = vi->visual;
|
||||
const int depth = vi->depth;
|
||||
XFree(vi);
|
||||
|
||||
Colormap colormap = XCreateColormap(dpy, root, visual, AllocNone);
|
||||
XSetWindowAttributes attributes;
|
||||
attributes.colormap = colormap;
|
||||
|
||||
dummy = XCreateWindow(dpy, root, 0, 0, 1, 1, 0, depth, InputOutput, visual,
|
||||
CWColormap, &attributes);
|
||||
XFreeColormap(dpy, colormap);
|
||||
if (dummy == None) {
|
||||
error_code = 4;
|
||||
goto start_failed;
|
||||
}
|
||||
|
||||
drawable = glXCreateWindow(dpy, cfg, dummy, NULL);
|
||||
if (drawable == None) {
|
||||
error_code = 5;
|
||||
goto start_failed;
|
||||
}
|
||||
|
||||
ctx = glXCreateNewContext(dpy, cfg, GLX_RGBA_TYPE, 0, true);
|
||||
if (ctx == NULL) {
|
||||
error_code = 6;
|
||||
goto start_failed;
|
||||
}
|
||||
|
||||
if (!glXMakeContextCurrent(dpy, drawable, drawable, ctx)) {
|
||||
error_code = 7;
|
||||
goto start_failed;
|
||||
}
|
||||
|
||||
if (!check_sgi_video_sync_extension(dpy, screen)) {
|
||||
error_code = 8;
|
||||
goto start_failed;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&args->start_mtx);
|
||||
args->start_status = 0;
|
||||
pthread_cond_signal(&args->start_cnd);
|
||||
pthread_mutex_unlock(&args->start_mtx);
|
||||
|
||||
pthread_mutex_lock(&self->vblank_requested_mtx);
|
||||
while (self->running) {
|
||||
if (!self->base.vblank_event_requested) {
|
||||
pthread_cond_wait(&self->vblank_requested_cnd,
|
||||
&self->vblank_requested_mtx);
|
||||
continue;
|
||||
}
|
||||
pthread_mutex_unlock(&self->vblank_requested_mtx);
|
||||
|
||||
unsigned int last_msc;
|
||||
glXWaitVideoSyncSGI(1, 0, &last_msc);
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
atomic_store(&self->last_msc, last_msc);
|
||||
atomic_store(&self->last_ust,
|
||||
(uint64_t)(now.tv_sec * 1000000 + now.tv_nsec / 1000));
|
||||
ev_async_send(self->base.loop, &self->notify);
|
||||
pthread_mutex_lock(&self->vblank_requested_mtx);
|
||||
}
|
||||
pthread_mutex_unlock(&self->vblank_requested_mtx);
|
||||
goto cleanup;
|
||||
|
||||
start_failed:
|
||||
pthread_mutex_lock(&args->start_mtx);
|
||||
args->start_status = error_code;
|
||||
pthread_cond_signal(&args->start_cnd);
|
||||
pthread_mutex_unlock(&args->start_mtx);
|
||||
|
||||
cleanup:
|
||||
if (dpy) {
|
||||
glXMakeCurrent(dpy, None, NULL);
|
||||
if (ctx) {
|
||||
glXDestroyContext(dpy, ctx);
|
||||
}
|
||||
if (drawable) {
|
||||
glXDestroyWindow(dpy, drawable);
|
||||
}
|
||||
if (dummy) {
|
||||
XDestroyWindow(dpy, dummy);
|
||||
}
|
||||
XCloseDisplay(dpy);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void sgi_video_sync_scheduler_schedule(struct vblank_scheduler *base) {
|
||||
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
||||
log_verbose("Requesting vblank event for msc %d", self->last_msc + 1);
|
||||
pthread_mutex_lock(&self->vblank_requested_mtx);
|
||||
assert(!base->vblank_event_requested);
|
||||
base->vblank_event_requested = true;
|
||||
pthread_cond_signal(&self->vblank_requested_cnd);
|
||||
pthread_mutex_unlock(&self->vblank_requested_mtx);
|
||||
}
|
||||
|
||||
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 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) {
|
||||
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
||||
auto args = (struct sgi_video_sync_thread_args){
|
||||
.self = self,
|
||||
.start_status = -1,
|
||||
};
|
||||
pthread_mutex_init(&args.start_mtx, NULL);
|
||||
pthread_cond_init(&args.start_cnd, NULL);
|
||||
|
||||
base->type = VBLANK_SCHEDULER_SGI_VIDEO_SYNC;
|
||||
ev_async_init(&self->notify, sgi_video_sync_scheduler_callback);
|
||||
ev_async_start(base->loop, &self->notify);
|
||||
pthread_mutex_init(&self->vblank_requested_mtx, NULL);
|
||||
pthread_cond_init(&self->vblank_requested_cnd, NULL);
|
||||
|
||||
self->running = true;
|
||||
pthread_create(&self->sync_thread, NULL, sgi_video_sync_thread, &args);
|
||||
|
||||
pthread_mutex_lock(&args.start_mtx);
|
||||
while (args.start_status == -1) {
|
||||
pthread_cond_wait(&args.start_cnd, &args.start_mtx);
|
||||
}
|
||||
if (args.start_status != 0) {
|
||||
log_fatal("Failed to start sgi_video_sync_thread, error code: %d",
|
||||
args.start_status);
|
||||
abort();
|
||||
}
|
||||
pthread_mutex_destroy(&args.start_mtx);
|
||||
pthread_cond_destroy(&args.start_cnd);
|
||||
log_info("Started sgi_video_sync_thread");
|
||||
}
|
||||
|
||||
static void sgi_video_sync_scheduler_deinit(struct vblank_scheduler *base) {
|
||||
auto self = (struct sgi_video_sync_vblank_scheduler *)base;
|
||||
ev_async_stop(base->loop, &self->notify);
|
||||
pthread_mutex_lock(&self->vblank_requested_mtx);
|
||||
self->running = false;
|
||||
pthread_cond_signal(&self->vblank_requested_cnd);
|
||||
pthread_mutex_unlock(&self->vblank_requested_mtx);
|
||||
|
||||
pthread_join(self->sync_thread, NULL);
|
||||
|
||||
pthread_mutex_destroy(&self->vblank_requested_mtx);
|
||||
pthread_cond_destroy(&self->vblank_requested_cnd);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void present_vblank_scheduler_schedule(struct vblank_scheduler *base) {
|
||||
auto self = (struct present_vblank_scheduler *)base;
|
||||
log_verbose("Requesting vblank event for window 0x%08x, msc %" PRIu64,
|
||||
base->target_window, self->last_msc + 1);
|
||||
assert(!base->vblank_event_requested);
|
||||
x_request_vblank_event(base->c, base->target_window, self->last_msc + 1);
|
||||
base->vblank_event_requested = true;
|
||||
}
|
||||
|
||||
static void present_vblank_callback(EV_P attr_unused, ev_timer *w, int attr_unused revents) {
|
||||
auto sched = container_of(w, struct present_vblank_scheduler, callback_timer);
|
||||
auto event = (struct vblank_event){
|
||||
.msc = sched->last_msc,
|
||||
.ust = sched->last_ust,
|
||||
};
|
||||
sched->base.vblank_event_requested = false;
|
||||
vblank_scheduler_invoke_callbacks(&sched->base, &event);
|
||||
}
|
||||
|
||||
static void present_vblank_scheduler_init(struct vblank_scheduler *base) {
|
||||
auto self = (struct present_vblank_scheduler *)base;
|
||||
base->type = VBLANK_SCHEDULER_PRESENT;
|
||||
ev_timer_init(&self->callback_timer, present_vblank_callback, 0, 0);
|
||||
|
||||
self->event_id = x_new_id(base->c);
|
||||
auto select_input =
|
||||
xcb_present_select_input(base->c->c, self->event_id, base->target_window,
|
||||
XCB_PRESENT_EVENT_MASK_COMPLETE_NOTIFY);
|
||||
set_cant_fail_cookie(base->c, select_input);
|
||||
self->event =
|
||||
xcb_register_for_special_xge(base->c->c, &xcb_present_id, self->event_id, NULL);
|
||||
}
|
||||
|
||||
static void present_vblank_scheduler_deinit(struct vblank_scheduler *base) {
|
||||
auto self = (struct present_vblank_scheduler *)base;
|
||||
ev_timer_stop(base->loop, &self->callback_timer);
|
||||
auto select_input =
|
||||
xcb_present_select_input(base->c->c, self->event_id, base->target_window, 0);
|
||||
set_cant_fail_cookie(base->c, select_input);
|
||||
xcb_unregister_for_special_event(base->c->c, self->event);
|
||||
}
|
||||
|
||||
/// Handle PresentCompleteNotify events
|
||||
///
|
||||
/// Schedule the registered callback to be called when the current vblank ends.
|
||||
static void handle_present_complete_notify(struct present_vblank_scheduler *self,
|
||||
xcb_present_complete_notify_event_t *cne) {
|
||||
assert(self->base.type == VBLANK_SCHEDULER_PRESENT);
|
||||
|
||||
if (cne->kind != XCB_PRESENT_COMPLETE_KIND_NOTIFY_MSC) {
|
||||
return;
|
||||
}
|
||||
|
||||
assert(self->base.vblank_event_requested);
|
||||
|
||||
// X sometimes sends duplicate/bogus MSC events, when screen has just been turned
|
||||
// off. Don't use the msc value in these events. We treat this as not receiving a
|
||||
// vblank event at all, and try to get a new one.
|
||||
//
|
||||
// See:
|
||||
// https://gitlab.freedesktop.org/xorg/xserver/-/issues/1418
|
||||
bool event_is_invalid = cne->msc <= self->last_msc || cne->ust == 0;
|
||||
if (event_is_invalid) {
|
||||
log_debug("Invalid PresentCompleteNotify event, %" PRIu64 " %" PRIu64,
|
||||
cne->msc, cne->ust);
|
||||
x_request_vblank_event(self->base.c, cne->window, self->last_msc + 1);
|
||||
return;
|
||||
}
|
||||
|
||||
self->last_ust = cne->ust;
|
||||
self->last_msc = cne->msc;
|
||||
|
||||
struct timespec now;
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
auto now_us = (unsigned long)(now.tv_sec * 1000000L + now.tv_nsec / 1000);
|
||||
double delay_sec = 0.0;
|
||||
if (now_us < cne->ust) {
|
||||
log_trace("The end of this vblank is %lu us into the "
|
||||
"future",
|
||||
cne->ust - now_us);
|
||||
delay_sec = (double)(cne->ust - now_us) / 1000000.0;
|
||||
}
|
||||
// Wait until the end of the current vblank to invoke callbacks. If we
|
||||
// call it too early, it can mistakenly think the render missed the
|
||||
// vblank, and doesn't schedule render for the next vblank, causing frame
|
||||
// drops.
|
||||
assert(!ev_is_active(&self->callback_timer));
|
||||
ev_timer_set(&self->callback_timer, delay_sec, 0);
|
||||
ev_timer_start(self->base.loop, &self->callback_timer);
|
||||
}
|
||||
|
||||
static bool handle_present_events(struct vblank_scheduler *base) {
|
||||
auto self = (struct present_vblank_scheduler *)base;
|
||||
xcb_present_generic_event_t *ev;
|
||||
while ((ev = (void *)xcb_poll_for_special_event(base->c->c, self->event))) {
|
||||
if (ev->event != self->event_id) {
|
||||
// This event doesn't have the right event context, it's not meant
|
||||
// for us.
|
||||
goto next;
|
||||
}
|
||||
|
||||
// We only subscribed to the complete notify event.
|
||||
assert(ev->evtype == XCB_PRESENT_EVENT_COMPLETE_NOTIFY);
|
||||
handle_present_complete_notify(self, (void *)ev);
|
||||
next:
|
||||
free(ev);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const struct vblank_scheduler_ops vblank_scheduler_ops[LAST_VBLANK_SCHEDULER] = {
|
||||
[VBLANK_SCHEDULER_PRESENT] =
|
||||
{
|
||||
.size = sizeof(struct present_vblank_scheduler),
|
||||
.init = present_vblank_scheduler_init,
|
||||
.deinit = present_vblank_scheduler_deinit,
|
||||
.schedule = present_vblank_scheduler_schedule,
|
||||
.handle_x_events = handle_present_events,
|
||||
},
|
||||
#ifdef CONFIG_OPENGL
|
||||
[VBLANK_SCHEDULER_SGI_VIDEO_SYNC] =
|
||||
{
|
||||
.size = sizeof(struct sgi_video_sync_vblank_scheduler),
|
||||
.init = sgi_video_sync_scheduler_init,
|
||||
.deinit = sgi_video_sync_scheduler_deinit,
|
||||
.schedule = sgi_video_sync_scheduler_schedule,
|
||||
.handle_x_events = NULL,
|
||||
},
|
||||
#endif
|
||||
};
|
||||
|
||||
static void vblank_scheduler_schedule_internal(struct vblank_scheduler *self) {
|
||||
assert(self->type < LAST_VBLANK_SCHEDULER);
|
||||
auto fn = vblank_scheduler_ops[self->type].schedule;
|
||||
assert(fn != NULL);
|
||||
fn(self);
|
||||
}
|
||||
|
||||
bool vblank_scheduler_schedule(struct vblank_scheduler *self,
|
||||
vblank_callback_t vblank_callback, void *user_data) {
|
||||
if (self->callback_count == 0 && self->wind_down == 0) {
|
||||
vblank_scheduler_schedule_internal(self);
|
||||
}
|
||||
if (self->callback_count == self->callback_capacity) {
|
||||
size_t new_capacity =
|
||||
self->callback_capacity ? self->callback_capacity * 2 : 1;
|
||||
void *new_buffer =
|
||||
realloc(self->callbacks, new_capacity * sizeof(*self->callbacks));
|
||||
if (!new_buffer) {
|
||||
return false;
|
||||
}
|
||||
self->callbacks = new_buffer;
|
||||
self->callback_capacity = new_capacity;
|
||||
}
|
||||
self->callbacks[self->callback_count++] = (struct vblank_callback){
|
||||
.fn = vblank_callback,
|
||||
.user_data = user_data,
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
vblank_scheduler_invoke_callbacks(struct vblank_scheduler *self, struct vblank_event *event) {
|
||||
// callbacks might be added during callback invocation, so we need to
|
||||
// copy the callback_count.
|
||||
size_t count = self->callback_count;
|
||||
if (count == 0) {
|
||||
self->wind_down--;
|
||||
} else {
|
||||
self->wind_down = VBLANK_WIND_DOWN;
|
||||
}
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
self->callbacks[i].fn(event, self->callbacks[i].user_data);
|
||||
}
|
||||
// remove the callbacks that we have called, keep the newly added ones.
|
||||
memmove(self->callbacks, self->callbacks + count,
|
||||
(self->callback_count - count) * sizeof(*self->callbacks));
|
||||
self->callback_count -= count;
|
||||
if (self->callback_count || self->wind_down) {
|
||||
vblank_scheduler_schedule_internal(self);
|
||||
}
|
||||
}
|
||||
|
||||
void vblank_scheduler_free(struct vblank_scheduler *self) {
|
||||
assert(self->type < LAST_VBLANK_SCHEDULER);
|
||||
auto fn = vblank_scheduler_ops[self->type].deinit;
|
||||
if (fn != NULL) {
|
||||
fn(self);
|
||||
}
|
||||
free(self->callbacks);
|
||||
free(self);
|
||||
}
|
||||
|
||||
struct vblank_scheduler *
|
||||
vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c,
|
||||
xcb_window_t target_window, enum vblank_scheduler_type type) {
|
||||
size_t object_size = vblank_scheduler_ops[type].size;
|
||||
auto init_fn = vblank_scheduler_ops[type].init;
|
||||
if (!object_size || !init_fn) {
|
||||
log_error("Unsupported or invalid vblank scheduler type: %d", type);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
assert(object_size >= sizeof(struct vblank_scheduler));
|
||||
struct vblank_scheduler *self = calloc(1, object_size);
|
||||
self->target_window = target_window;
|
||||
self->c = c;
|
||||
self->loop = loop;
|
||||
init_fn(self);
|
||||
return self;
|
||||
}
|
||||
|
||||
bool vblank_handle_x_events(struct vblank_scheduler *self) {
|
||||
assert(self->type < LAST_VBLANK_SCHEDULER);
|
||||
auto fn = vblank_scheduler_ops[self->type].handle_x_events;
|
||||
if (fn != NULL) {
|
||||
return fn(self);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
39
src/vblank.h
39
src/vblank.h
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <xcb/present.h>
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
#include <ev.h>
|
||||
#include <xcb/xproto.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "x.h"
|
||||
|
||||
/// An object that schedule vblank events.
|
||||
struct vblank_scheduler;
|
||||
|
||||
struct vblank_event {
|
||||
uint64_t msc;
|
||||
uint64_t ust;
|
||||
};
|
||||
|
||||
typedef void (*vblank_callback_t)(struct vblank_event *event, void *user_data);
|
||||
|
||||
/// Schedule a vblank event.
|
||||
///
|
||||
/// Schedule for `cb` to be called when the current vblank ends. If this is called
|
||||
/// from a callback function for the current vblank, the newly scheduled callback
|
||||
/// will be called in the next vblank.
|
||||
///
|
||||
/// Returns whether the scheduling is successful. Scheduling can fail if there
|
||||
/// is not enough memory.
|
||||
bool vblank_scheduler_schedule(struct vblank_scheduler *self, vblank_callback_t cb,
|
||||
void *user_data);
|
||||
struct vblank_scheduler *
|
||||
vblank_scheduler_new(struct ev_loop *loop, struct x_connection *c,
|
||||
xcb_window_t target_window, enum vblank_scheduler_type type);
|
||||
void vblank_scheduler_free(struct vblank_scheduler *);
|
||||
|
||||
bool vblank_handle_x_events(struct vblank_scheduler *self);
|
||||
20
src/vsync.c
20
src/vsync.c
@@ -77,35 +77,31 @@ static bool vsync_drm_init(session_t *ps) {
|
||||
* @return true for success, false otherwise
|
||||
*/
|
||||
static bool vsync_opengl_init(session_t *ps) {
|
||||
if (!ensure_glx_context(ps)) {
|
||||
if (!ensure_glx_context(ps))
|
||||
return false;
|
||||
}
|
||||
|
||||
return glxext.has_GLX_SGI_video_sync;
|
||||
}
|
||||
|
||||
static bool vsync_opengl_oml_init(session_t *ps) {
|
||||
if (!ensure_glx_context(ps)) {
|
||||
if (!ensure_glx_context(ps))
|
||||
return false;
|
||||
}
|
||||
|
||||
return glxext.has_GLX_OML_sync_control;
|
||||
}
|
||||
|
||||
static inline bool vsync_opengl_swc_swap_interval(session_t *ps, int interval) {
|
||||
if (glxext.has_GLX_MESA_swap_control) {
|
||||
if (glxext.has_GLX_MESA_swap_control)
|
||||
return glXSwapIntervalMESA((uint)interval) == 0;
|
||||
}
|
||||
if (glxext.has_GLX_SGI_swap_control) {
|
||||
else if (glxext.has_GLX_SGI_swap_control)
|
||||
return glXSwapIntervalSGI(interval) == 0;
|
||||
}
|
||||
if (glxext.has_GLX_EXT_swap_control) {
|
||||
else if (glxext.has_GLX_EXT_swap_control) {
|
||||
GLXDrawable d = glXGetCurrentDrawable();
|
||||
if (d == None) {
|
||||
// We don't have a context??
|
||||
return false;
|
||||
}
|
||||
glXSwapIntervalEXT(ps->c.dpy, glXGetCurrentDrawable(), interval);
|
||||
glXSwapIntervalEXT(ps->dpy, glXGetCurrentDrawable(), interval);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -144,8 +140,8 @@ static int vsync_opengl_wait(session_t *ps attr_unused) {
|
||||
static int vsync_opengl_oml_wait(session_t *ps) {
|
||||
int64_t ust = 0, msc = 0, sbc = 0;
|
||||
|
||||
glXGetSyncValuesOML(ps->c.dpy, ps->reg_win, &ust, &msc, &sbc);
|
||||
glXWaitForMscOML(ps->c.dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc);
|
||||
glXGetSyncValuesOML(ps->dpy, ps->reg_win, &ust, &msc, &sbc);
|
||||
glXWaitForMscOML(ps->dpy, ps->reg_win, 0, 2, (msc + 1) % 2, &ust, &msc, &sbc);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
306
src/win.c
306
src/win.c
@@ -331,7 +331,7 @@ static inline bool win_bind_pixmap(struct backend_base *b, struct managed_win *w
|
||||
assert(!w->win_image);
|
||||
auto pixmap = x_new_id(b->c);
|
||||
auto e = xcb_request_check(
|
||||
b->c->c, xcb_composite_name_window_pixmap_checked(b->c->c, w->base.id, pixmap));
|
||||
b->c, xcb_composite_name_window_pixmap_checked(b->c, w->base.id, pixmap));
|
||||
if (e) {
|
||||
log_error("Failed to get named pixmap for window %#010x(%s)", w->base.id,
|
||||
w->name);
|
||||
@@ -375,11 +375,7 @@ bool win_bind_shadow(struct backend_base *b, struct managed_win *w, struct color
|
||||
b->ops->shadow_from_mask == NULL) {
|
||||
w->shadow_image = b->ops->render_shadow(b, w->widthb, w->heightb, sctx, c);
|
||||
} else {
|
||||
if (!w->mask_image) {
|
||||
// It's possible we already allocated a mask because of background
|
||||
// blur
|
||||
win_bind_mask(b, w);
|
||||
}
|
||||
win_bind_mask(b, w);
|
||||
w->shadow_image = b->ops->shadow_from_mask(b, w->mask_image, sctx, c);
|
||||
}
|
||||
if (!w->shadow_image) {
|
||||
@@ -449,6 +445,10 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
|
||||
}
|
||||
}
|
||||
|
||||
if (win_fetch_and_unset_property_stale(w, ps->atoms->a_NET_WM_DESKTOP)) {
|
||||
win_update_prop_desktop(ps, w);
|
||||
}
|
||||
|
||||
if (win_fetch_and_unset_property_stale(w, ps->atoms->aWM_CLASS)) {
|
||||
if (win_update_class(ps, w)) {
|
||||
win_set_flags(w, WIN_FLAGS_FACTOR_CHANGED);
|
||||
@@ -476,11 +476,17 @@ static void win_update_properties(session_t *ps, struct managed_win *w) {
|
||||
static void init_animation(session_t *ps, struct managed_win *w) {
|
||||
CLEAR_MASK(w->animation_is_tag)
|
||||
static int32_t randr_mon_center_x, randr_mon_center_y;
|
||||
if (w->randr_monitor != -1) {
|
||||
auto e = pixman_region32_extents(&ps->monitors.regions[w->randr_monitor]);
|
||||
randr_mon_center_x = (e->x2 + e->x1) / 2, randr_mon_center_y = (e->y2 + e->y1) / 2;
|
||||
if (w->randr_monitor == -1) {
|
||||
win_update_monitor(ps->randr_nmonitors, ps->randr_monitor_regs, w);
|
||||
if (w->randr_monitor != -1) {
|
||||
auto e = pixman_region32_extents(&ps->randr_monitor_regs[w->randr_monitor]);
|
||||
randr_mon_center_x = (e->x2 + e->x1) / 2, randr_mon_center_y = (e->y2 + e->y1) / 2;
|
||||
} else {
|
||||
randr_mon_center_x = ps->root_width / 2, randr_mon_center_y = ps->root_height / 2;
|
||||
}
|
||||
} else {
|
||||
randr_mon_center_x = ps->root_width / 2, randr_mon_center_y = ps->root_height / 2;
|
||||
auto e = pixman_region32_extents(&ps->randr_monitor_regs[w->randr_monitor]);
|
||||
randr_mon_center_x = (e->x2 + e->x1) / 2, randr_mon_center_y = (e->y2 + e->y1) / 2;
|
||||
}
|
||||
static double *anim_x, *anim_y, *anim_w, *anim_h;
|
||||
enum open_window_animation animation;
|
||||
@@ -683,10 +689,11 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
|
||||
// Determine if a window should animate
|
||||
if (win_should_animate(ps, w)) {
|
||||
if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height)
|
||||
w->dwm_mask = ANIM_PREV_TAG;
|
||||
else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height)
|
||||
w->dwm_mask = ANIM_NEXT_TAG;
|
||||
win_update_bounding_shape(ps, w);
|
||||
// if (w->pending_g.y < 0 && w->g.y > 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height)
|
||||
// w->dwm_mask = ANIM_PREV_TAG;
|
||||
// else if (w->pending_g.y > 0 && w->g.y < 0 && abs(w->pending_g.y - w->g.y) >= w->pending_g.height)
|
||||
// w->dwm_mask = ANIM_NEXT_TAG;
|
||||
|
||||
if (!was_visible || w->dwm_mask) {
|
||||
|
||||
@@ -704,7 +711,6 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
w->g.width = (uint16_t)round(w->animation_w);
|
||||
w->g.height = (uint16_t)round(w->animation_h);
|
||||
}
|
||||
|
||||
} else {
|
||||
w->animation_is_tag = ANIM_IN_TAG;
|
||||
w->animation_dest_center_x =
|
||||
@@ -715,37 +721,37 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
w->animation_dest_h = w->pending_g.height;
|
||||
}
|
||||
|
||||
CLEAR_MASK(w->dwm_mask)
|
||||
w->g.border_width = w->pending_g.border_width;
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_inv_og_distance =
|
||||
1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
CLEAR_MASK(w->dwm_mask)
|
||||
w->g.border_width = w->pending_g.border_width;
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_inv_og_distance =
|
||||
1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
|
||||
if (isinf(w->animation_inv_og_distance))
|
||||
w->animation_inv_og_distance = 0;
|
||||
if (isinf(w->animation_inv_og_distance))
|
||||
w->animation_inv_og_distance = 0;
|
||||
|
||||
// We only grab images if w->reg_ignore_valid is true as
|
||||
// there's an ev_shape_notify() event fired quickly on new windows
|
||||
// for e.g. in case of Firefox main menu and ev_shape_notify()
|
||||
// sets the win_set_flags(w, WIN_FLAGS_SIZE_STALE); which
|
||||
// brakes the new image captured and because this same event
|
||||
// also sets w->reg_ignore_valid = false; too we check for it
|
||||
if (w->reg_ignore_valid) {
|
||||
if (w->old_win_image) {
|
||||
ps->backend_data->ops->release_image(ps->backend_data,
|
||||
w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
// We only grab images if w->reg_ignore_valid is true as
|
||||
// there's an ev_shape_notify() event fired quickly on new windows
|
||||
// for e.g. in case of Firefox main menu and ev_shape_notify()
|
||||
// sets the win_set_flags(w, WIN_FLAGS_SIZE_STALE); which
|
||||
// brakes the new image captured and because this same event
|
||||
// also sets w->reg_ignore_valid = false; too we check for it
|
||||
if (w->reg_ignore_valid) {
|
||||
if (w->old_win_image) {
|
||||
ps->backend_data->ops->release_image(ps->backend_data,
|
||||
w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
|
||||
// We only grab
|
||||
if (w->win_image) {
|
||||
w->old_win_image = ps->backend_data->ops->clone_image(
|
||||
ps->backend_data, w->win_image, &w->bounding_shape);
|
||||
}
|
||||
// We only grab
|
||||
if (w->win_image) {
|
||||
w->old_win_image = ps->backend_data->ops->clone_image(
|
||||
ps->backend_data, w->win_image, &w->bounding_shape);
|
||||
}
|
||||
}
|
||||
|
||||
w->animation_progress = 0.0;
|
||||
@@ -753,19 +759,19 @@ void win_process_update_flags(session_t *ps, struct managed_win *w) {
|
||||
w->g = w->pending_g;
|
||||
}
|
||||
|
||||
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
||||
win_on_win_size_change(ps, w);
|
||||
win_update_bounding_shape(ps, w);
|
||||
damaged = true;
|
||||
win_clear_flags(w, WIN_FLAGS_SIZE_STALE);
|
||||
}
|
||||
if (win_check_flags_all(w, WIN_FLAGS_SIZE_STALE)) {
|
||||
win_on_win_size_change(ps, w);
|
||||
win_update_bounding_shape(ps, w);
|
||||
damaged = true;
|
||||
win_clear_flags(w, WIN_FLAGS_SIZE_STALE);
|
||||
}
|
||||
|
||||
if (win_check_flags_all(w, WIN_FLAGS_POSITION_STALE)) {
|
||||
damaged = true;
|
||||
win_clear_flags(w, WIN_FLAGS_POSITION_STALE);
|
||||
}
|
||||
|
||||
win_update_monitor(&ps->monitors, w);
|
||||
win_update_monitor(ps->randr_nmonitors, ps->randr_monitor_regs, w);
|
||||
}
|
||||
|
||||
if (win_check_flags_all(w, WIN_FLAGS_PROPERTY_STALE)) {
|
||||
@@ -948,7 +954,7 @@ static inline bool win_bounding_shaped(const session_t *ps, xcb_window_t wid) {
|
||||
Bool bounding_shaped;
|
||||
|
||||
reply = xcb_shape_query_extents_reply(
|
||||
ps->c.c, xcb_shape_query_extents(ps->c.c, wid), NULL);
|
||||
ps->c, xcb_shape_query_extents(ps->c, wid), NULL);
|
||||
bounding_shaped = reply && reply->bounding_shaped;
|
||||
free(reply);
|
||||
|
||||
@@ -960,7 +966,7 @@ static inline bool win_bounding_shaped(const session_t *ps, xcb_window_t wid) {
|
||||
|
||||
static wintype_t wid_get_prop_wintype(session_t *ps, xcb_window_t wid) {
|
||||
winprop_t prop =
|
||||
x_get_prop(&ps->c, wid, ps->atoms->a_NET_WM_WINDOW_TYPE, 32L, XCB_ATOM_ATOM, 32);
|
||||
x_get_prop(ps->c, wid, ps->atoms->a_NET_WM_WINDOW_TYPE, 32L, XCB_ATOM_ATOM, 32);
|
||||
|
||||
for (unsigned i = 0; i < prop.nitems; ++i) {
|
||||
for (wintype_t j = 1; j < NUM_WINTYPES; ++j) {
|
||||
@@ -981,7 +987,7 @@ wid_get_opacity_prop(session_t *ps, xcb_window_t wid, opacity_t def, opacity_t *
|
||||
bool ret = false;
|
||||
*out = def;
|
||||
|
||||
winprop_t prop = x_get_prop(&ps->c, wid, ps->atoms->a_NET_WM_WINDOW_OPACITY, 1L,
|
||||
winprop_t prop = x_get_prop(ps->c, wid, ps->atoms->a_NET_WM_WINDOW_OPACITY, 1L,
|
||||
XCB_ATOM_CARDINAL, 32);
|
||||
|
||||
if (prop.nitems) {
|
||||
@@ -1077,12 +1083,11 @@ double win_calc_opacity_target(session_t *ps, const struct managed_win *w) {
|
||||
} else {
|
||||
// Respect active_opacity only when the window is physically
|
||||
// focused
|
||||
if (win_is_focused_raw(ps, w)) {
|
||||
if (win_is_focused_raw(ps, w))
|
||||
opacity = ps->o.active_opacity;
|
||||
} else if (!w->focused) {
|
||||
else if (!w->focused)
|
||||
// Respect inactive_opacity in some cases
|
||||
opacity = ps->o.inactive_opacity;
|
||||
}
|
||||
}
|
||||
|
||||
// respect inactive override
|
||||
@@ -1104,8 +1109,9 @@ bool win_should_dim(session_t *ps, const struct managed_win *w) {
|
||||
|
||||
if (ps->o.inactive_dim > 0 && !(w->focused)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1155,7 +1161,7 @@ bool win_should_animate(session_t *ps, const struct managed_win *w) {
|
||||
* The property must be set on the outermost window, usually the WM frame.
|
||||
*/
|
||||
void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w) {
|
||||
winprop_t prop = x_get_prop(&ps->c, w->base.id, ps->atoms->a_COMPTON_SHADOW, 1,
|
||||
winprop_t prop = x_get_prop(ps->c, w->base.id, ps->atoms->a_COMPTON_SHADOW, 1,
|
||||
XCB_ATOM_CARDINAL, 32);
|
||||
|
||||
if (!prop.nitems) {
|
||||
@@ -1167,6 +1173,17 @@ void win_update_prop_shadow_raw(session_t *ps, struct managed_win *w) {
|
||||
free_winprop(&prop);
|
||||
}
|
||||
|
||||
void win_update_prop_desktop(session_t *ps, struct managed_win *w) {
|
||||
winprop_t prop = x_get_prop(ps->c, w->base.id, ps->atoms->a_NET_WM_DESKTOP, 1,
|
||||
XCB_ATOM_CARDINAL, 32);
|
||||
if (!prop.nitems) {
|
||||
w->cur_desktop = w->cur_desktop;
|
||||
} else {
|
||||
w->cur_desktop = *prop.c32;
|
||||
}
|
||||
free_winprop(&prop);
|
||||
}
|
||||
|
||||
static void win_set_shadow(session_t *ps, struct managed_win *w, bool shadow_new) {
|
||||
if (w->shadow == shadow_new) {
|
||||
return;
|
||||
@@ -1353,9 +1370,8 @@ void win_set_shadow_force(session_t *ps, struct managed_win *w, switch_t val) {
|
||||
|
||||
static void
|
||||
win_set_blur_background(session_t *ps, struct managed_win *w, bool blur_background_new) {
|
||||
if (w->blur_background == blur_background_new) {
|
||||
if (w->blur_background == blur_background_new)
|
||||
return;
|
||||
}
|
||||
|
||||
w->blur_background = blur_background_new;
|
||||
|
||||
@@ -1489,8 +1505,10 @@ void win_on_factor_change(session_t *ps, struct managed_win *w) {
|
||||
// focused state of the window
|
||||
win_update_focused(ps, w);
|
||||
|
||||
win_determine_shadow(ps, w);
|
||||
win_determine_clip_shadow_above(ps, w);
|
||||
if (w->animation_progress > 0.99999999 || (w->animation_progress == 0.0 && ps->animation_time != 0L)) {
|
||||
win_determine_shadow(ps, w);
|
||||
win_determine_clip_shadow_above(ps, w);
|
||||
}
|
||||
win_determine_invert_color(ps, w);
|
||||
win_determine_blur_background(ps, w);
|
||||
win_determine_rounded_corners(ps, w);
|
||||
@@ -1553,11 +1571,10 @@ void win_update_wintype(session_t *ps, struct managed_win *w) {
|
||||
// _NET_WM_WINDOW_TYPE_NORMAL, otherwise as _NET_WM_WINDOW_TYPE_DIALOG.
|
||||
if (WINTYPE_UNKNOWN == w->window_type) {
|
||||
if (w->a.override_redirect ||
|
||||
!wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR)) {
|
||||
!wid_has_prop(ps, w->client_win, ps->atoms->aWM_TRANSIENT_FOR))
|
||||
w->window_type = WINTYPE_NORMAL;
|
||||
} else {
|
||||
else
|
||||
w->window_type = WINTYPE_DIALOG;
|
||||
}
|
||||
}
|
||||
|
||||
if (w->window_type != wtype_old) {
|
||||
@@ -1582,9 +1599,9 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
|
||||
}
|
||||
|
||||
auto e = xcb_request_check(
|
||||
ps->c.c, xcb_change_window_attributes_checked(
|
||||
ps->c.c, client, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)}));
|
||||
ps->c, xcb_change_window_attributes_checked(
|
||||
ps->c, client, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_CLIENT)}));
|
||||
if (e) {
|
||||
log_error("Failed to change event mask of window %#010x", client);
|
||||
free(e);
|
||||
@@ -1609,13 +1626,13 @@ void win_mark_client(session_t *ps, struct managed_win *w, xcb_window_t client)
|
||||
win_on_factor_change(ps, w);
|
||||
|
||||
auto r = xcb_get_window_attributes_reply(
|
||||
ps->c.c, xcb_get_window_attributes(ps->c.c, w->client_win), &e);
|
||||
ps->c, xcb_get_window_attributes(ps->c, w->client_win), &e);
|
||||
if (!r) {
|
||||
log_error_x_error(e, "Failed to get client window attributes");
|
||||
return;
|
||||
}
|
||||
|
||||
w->client_pictfmt = x_get_pictform_for_visual(&ps->c, r->visual);
|
||||
w->client_pictfmt = x_get_pictform_for_visual(ps->c, r->visual);
|
||||
free(r);
|
||||
}
|
||||
|
||||
@@ -1634,7 +1651,7 @@ void win_unmark_client(session_t *ps, struct managed_win *w) {
|
||||
|
||||
// Recheck event mask
|
||||
xcb_change_window_attributes(
|
||||
ps->c.c, client, XCB_CW_EVENT_MASK,
|
||||
ps->c, client, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, client, WIN_EVMODE_UNKNOWN)});
|
||||
}
|
||||
|
||||
@@ -1647,7 +1664,7 @@ static xcb_window_t find_client_win(session_t *ps, xcb_window_t w) {
|
||||
}
|
||||
|
||||
xcb_query_tree_reply_t *reply =
|
||||
xcb_query_tree_reply(ps->c.c, xcb_query_tree(ps->c.c, w), NULL);
|
||||
xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, w), NULL);
|
||||
if (!reply) {
|
||||
return 0;
|
||||
}
|
||||
@@ -1721,7 +1738,7 @@ void free_win_res(session_t *ps, struct managed_win *w) {
|
||||
|
||||
pixman_region32_fini(&w->bounding_shape);
|
||||
// BadDamage may be thrown if the window is destroyed
|
||||
set_ignore_cookie(&ps->c, xcb_damage_destroy(ps->c.c, w->damage));
|
||||
set_ignore_cookie(ps, xcb_damage_destroy(ps->c, w->damage));
|
||||
rc_region_unref(&w->reg_ignore);
|
||||
free(w->name);
|
||||
free(w->class_instance);
|
||||
@@ -1770,11 +1787,12 @@ struct win *add_win_above(session_t *ps, xcb_window_t id, xcb_window_t below) {
|
||||
return NULL;
|
||||
}
|
||||
return add_win_top(ps, id);
|
||||
} else {
|
||||
// we found something from the hash table, so if the stack is
|
||||
// empty, we are in an inconsistent state.
|
||||
assert(!list_is_empty(&ps->window_stack));
|
||||
return add_win(ps, id, w->stack_neighbour.prev);
|
||||
}
|
||||
// we found something from the hash table, so if the stack is
|
||||
// empty, we are in an inconsistent state.
|
||||
assert(!list_is_empty(&ps->window_stack));
|
||||
return add_win(ps, id, w->stack_neighbour.prev);
|
||||
}
|
||||
|
||||
/// Query the Xorg for information about window `win`
|
||||
@@ -1801,7 +1819,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
.animation_velocity_y = 0.0, // updated by window geometry changes
|
||||
.animation_velocity_w = 0.0, // updated by window geometry changes
|
||||
.animation_velocity_h = 0.0, // updated by window geometry changes
|
||||
.animation_progress = 1.0, // updated by window geometry changes
|
||||
.animation_progress = 0.0, // updated by window geometry changes
|
||||
.animation_inv_og_distance = NAN, // updated by window geometry changes
|
||||
.reg_ignore_valid = false, // set to true when damaged
|
||||
.flags = WIN_FLAGS_IMAGES_NONE, // updated by property/attributes/etc
|
||||
@@ -1894,10 +1912,9 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
}
|
||||
|
||||
log_debug("Managing window %#010x", w->id);
|
||||
xcb_get_window_attributes_cookie_t acookie =
|
||||
xcb_get_window_attributes(ps->c.c, w->id);
|
||||
xcb_get_window_attributes_cookie_t acookie = xcb_get_window_attributes(ps->c, w->id);
|
||||
xcb_get_window_attributes_reply_t *a =
|
||||
xcb_get_window_attributes_reply(ps->c.c, acookie, NULL);
|
||||
xcb_get_window_attributes_reply(ps->c, acookie, NULL);
|
||||
if (!a || a->map_state == XCB_MAP_STATE_UNVIEWABLE) {
|
||||
// Failed to get window attributes or geometry probably means
|
||||
// the window is gone already. Unviewable means the window is
|
||||
@@ -1932,7 +1949,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
free(a);
|
||||
|
||||
xcb_generic_error_t *e;
|
||||
auto g = xcb_get_geometry_reply(ps->c.c, xcb_get_geometry(ps->c.c, w->id), &e);
|
||||
auto g = xcb_get_geometry_reply(ps->c, xcb_get_geometry(ps->c, w->id), &e);
|
||||
if (!g) {
|
||||
log_error_x_error(e, "Failed to get geometry of window %#010x", w->id);
|
||||
free(e);
|
||||
@@ -1950,10 +1967,10 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
free(g);
|
||||
|
||||
// Create Damage for window (if not Input Only)
|
||||
new->damage = x_new_id(&ps->c);
|
||||
new->damage = x_new_id(ps->c);
|
||||
e = xcb_request_check(
|
||||
ps->c.c, xcb_damage_create_checked(ps->c.c, new->damage, w->id,
|
||||
XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
|
||||
ps->c, xcb_damage_create_checked(ps->c, new->damage, w->id,
|
||||
XCB_DAMAGE_REPORT_LEVEL_NON_EMPTY));
|
||||
if (e) {
|
||||
log_error_x_error(e, "Failed to create damage");
|
||||
free(e);
|
||||
@@ -1963,15 +1980,15 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
|
||||
// Set window event mask
|
||||
xcb_change_window_attributes(
|
||||
ps->c.c, new->base.id, XCB_CW_EVENT_MASK,
|
||||
ps->c, new->base.id, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){determine_evmask(ps, new->base.id, WIN_EVMODE_FRAME)});
|
||||
|
||||
// Get notification when the shape of a window changes
|
||||
if (ps->shape_exists) {
|
||||
xcb_shape_select_input(ps->c.c, new->base.id, 1);
|
||||
xcb_shape_select_input(ps->c, new->base.id, 1);
|
||||
}
|
||||
|
||||
new->pictfmt = x_get_pictform_for_visual(&ps->c, new->a.visual);
|
||||
new->pictfmt = x_get_pictform_for_visual(ps->c, new->a.visual);
|
||||
new->client_pictfmt = NULL;
|
||||
|
||||
list_replace(&w->stack_neighbour, &new->base.stack_neighbour);
|
||||
@@ -1991,6 +2008,7 @@ struct win *fill_win(session_t *ps, struct win *w) {
|
||||
ps->atoms->a_NET_WM_NAME, ps->atoms->aWM_CLASS,
|
||||
ps->atoms->aWM_WINDOW_ROLE, ps->atoms->a_COMPTON_SHADOW,
|
||||
ps->atoms->aWM_CLIENT_LEADER, ps->atoms->aWM_TRANSIENT_FOR,
|
||||
ps->atoms->a_NET_WM_DESKTOP
|
||||
};
|
||||
win_set_properties_stale(new, init_stale_props, ARR_SIZE(init_stale_props));
|
||||
|
||||
@@ -2041,12 +2059,12 @@ void win_update_leader(session_t *ps, struct managed_win *w) {
|
||||
// Read the leader properties
|
||||
if (ps->o.detect_transient && !leader) {
|
||||
leader =
|
||||
wid_get_prop_window(&ps->c, w->client_win, ps->atoms->aWM_TRANSIENT_FOR);
|
||||
wid_get_prop_window(ps->c, w->client_win, ps->atoms->aWM_TRANSIENT_FOR);
|
||||
}
|
||||
|
||||
if (ps->o.detect_client_leader && !leader) {
|
||||
leader =
|
||||
wid_get_prop_window(&ps->c, w->client_win, ps->atoms->aWM_CLIENT_LEADER);
|
||||
wid_get_prop_window(ps->c, w->client_win, ps->atoms->aWM_CLIENT_LEADER);
|
||||
}
|
||||
|
||||
win_set_leader(ps, w, leader);
|
||||
@@ -2062,9 +2080,8 @@ static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int
|
||||
// Rebuild the cache if needed
|
||||
if (!w->cache_leader && (w->client_win || w->leader)) {
|
||||
// Leader defaults to client window
|
||||
if (!(w->cache_leader = w->leader)) {
|
||||
if (!(w->cache_leader = w->leader))
|
||||
w->cache_leader = w->client_win;
|
||||
}
|
||||
|
||||
// If the leader of this window isn't itself, look for its
|
||||
// ancestors
|
||||
@@ -2072,9 +2089,8 @@ static xcb_window_t win_get_leader_raw(session_t *ps, struct managed_win *w, int
|
||||
auto wp = find_toplevel(ps, w->cache_leader);
|
||||
if (wp) {
|
||||
// Dead loop?
|
||||
if (recursions > WIN_GET_LEADER_MAX_RECURSION) {
|
||||
if (recursions > WIN_GET_LEADER_MAX_RECURSION)
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
w->cache_leader = win_get_leader_raw(ps, wp, recursions + 1);
|
||||
}
|
||||
@@ -2093,9 +2109,8 @@ bool win_update_class(session_t *ps, struct managed_win *w) {
|
||||
int nstr = 0;
|
||||
|
||||
// Can't do anything if there's no client window
|
||||
if (!w->client_win) {
|
||||
if (!w->client_win)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Free and reset old strings
|
||||
free(w->class_instance);
|
||||
@@ -2235,9 +2250,8 @@ void win_update_bounding_shape(session_t *ps, struct managed_win *w) {
|
||||
*/
|
||||
|
||||
xcb_shape_get_rectangles_reply_t *r = xcb_shape_get_rectangles_reply(
|
||||
ps->c.c,
|
||||
xcb_shape_get_rectangles(ps->c.c, w->base.id, XCB_SHAPE_SK_BOUNDING),
|
||||
NULL);
|
||||
ps->c,
|
||||
xcb_shape_get_rectangles(ps->c, w->base.id, XCB_SHAPE_SK_BOUNDING), NULL);
|
||||
|
||||
if (!r) {
|
||||
break;
|
||||
@@ -2310,7 +2324,7 @@ void win_update_opacity_prop(session_t *ps, struct managed_win *w) {
|
||||
* Retrieve frame extents from a window.
|
||||
*/
|
||||
void win_update_frame_extents(session_t *ps, struct managed_win *w, xcb_window_t client) {
|
||||
winprop_t prop = x_get_prop(&ps->c, client, ps->atoms->a_NET_FRAME_EXTENTS, 4L,
|
||||
winprop_t prop = x_get_prop(ps->c, client, ps->atoms->a_NET_FRAME_EXTENTS, 4L,
|
||||
XCB_ATOM_CARDINAL, 32);
|
||||
|
||||
if (prop.nitems == 4) {
|
||||
@@ -2365,7 +2379,7 @@ bool win_is_region_ignore_valid(session_t *ps, const struct managed_win *w) {
|
||||
* Stop listening for events on a particular window.
|
||||
*/
|
||||
void win_ev_stop(session_t *ps, const struct win *w) {
|
||||
xcb_change_window_attributes(ps->c.c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0});
|
||||
xcb_change_window_attributes(ps->c, w->id, XCB_CW_EVENT_MASK, (const uint32_t[]){0});
|
||||
|
||||
if (!w->managed) {
|
||||
return;
|
||||
@@ -2373,12 +2387,12 @@ void win_ev_stop(session_t *ps, const struct win *w) {
|
||||
|
||||
auto mw = (struct managed_win *)w;
|
||||
if (mw->client_win) {
|
||||
xcb_change_window_attributes(ps->c.c, mw->client_win, XCB_CW_EVENT_MASK,
|
||||
xcb_change_window_attributes(ps->c, mw->client_win, XCB_CW_EVENT_MASK,
|
||||
(const uint32_t[]){0});
|
||||
}
|
||||
|
||||
if (ps->shape_exists) {
|
||||
xcb_shape_select_input(ps->c.c, w->id, 0);
|
||||
xcb_shape_select_input(ps->c, w->id, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2687,29 +2701,31 @@ void unmap_win_start(session_t *ps, struct managed_win *w) {
|
||||
w->opacity_target_old = fmax(w->opacity_target, w->opacity_target_old);
|
||||
w->opacity_target = win_calc_opacity_target(ps, w);
|
||||
|
||||
if (ps->o.animations && ps->o.animation_for_unmap_window != OPEN_WINDOW_ANIMATION_NONE && ps->o.wintype_option[w->window_type].animation) {
|
||||
w->dwm_mask = ANIM_UNMAP;
|
||||
init_animation(ps, w);
|
||||
if (ps->o.animations &&
|
||||
ps->o.animation_for_unmap_window != OPEN_WINDOW_ANIMATION_NONE &&
|
||||
ps->o.wintype_option[w->window_type].animation) {
|
||||
w->dwm_mask = ANIM_UNMAP;
|
||||
init_animation(ps, w);
|
||||
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_inv_og_distance =
|
||||
1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
double x_dist = w->animation_dest_center_x - w->animation_center_x;
|
||||
double y_dist = w->animation_dest_center_y - w->animation_center_y;
|
||||
double w_dist = w->animation_dest_w - w->animation_w;
|
||||
double h_dist = w->animation_dest_h - w->animation_h;
|
||||
w->animation_inv_og_distance =
|
||||
1.0 / sqrt(x_dist * x_dist + y_dist * y_dist +
|
||||
w_dist * w_dist + h_dist * h_dist);
|
||||
|
||||
if (isinf(w->animation_inv_og_distance))
|
||||
w->animation_inv_og_distance = 0;
|
||||
if (isinf(w->animation_inv_og_distance))
|
||||
w->animation_inv_og_distance = 0;
|
||||
|
||||
w->animation_progress = 0.0;
|
||||
w->animation_progress = 0.0;
|
||||
|
||||
if (w->old_win_image) {
|
||||
ps->backend_data->ops->release_image(ps->backend_data,
|
||||
w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
}
|
||||
if (w->old_win_image) {
|
||||
ps->backend_data->ops->release_image(ps->backend_data,
|
||||
w->old_win_image);
|
||||
w->old_win_image = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DBUS
|
||||
// Send D-Bus signal
|
||||
@@ -2744,7 +2760,7 @@ bool win_check_fade_finished(session_t *ps, struct managed_win *w) {
|
||||
case WSTATE_DESTROYING: destroy_win_finish(ps, &w->base); return true;
|
||||
case WSTATE_MAPPING: map_win_finish(w); return false;
|
||||
case WSTATE_FADING: w->state = WSTATE_MAPPED; break;
|
||||
default: unreachable();
|
||||
default: unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2767,13 +2783,12 @@ bool win_skip_fading(session_t *ps, struct managed_win *w) {
|
||||
|
||||
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
|
||||
// the x.c.
|
||||
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw) {
|
||||
for (int i = 0; i < monitors->count; i++) {
|
||||
auto e = pixman_region32_extents(&monitors->regions[i]);
|
||||
if (((e->x1 <= mw->g.x || e->x1 <= mw->pending_g.x) &&
|
||||
(e->x2 >= mw->g.x + mw->widthb || e->x2 >= mw->pending_g.x + mw->widthb)) &&
|
||||
(e->y1 <= mw->g.y || e->y1 <= mw->pending_g.y) &&
|
||||
(e->y2 >= mw->g.y + mw->heightb || e->y2 >= mw->pending_g.y + mw->heightb)) {
|
||||
void win_update_monitor(int nmons, region_t *mons, struct managed_win *mw) {
|
||||
for (int i = 0; i < nmons; i++) {
|
||||
auto e = pixman_region32_extents(&mons[i]);
|
||||
// if (e->x1 <= mw->g.x && e->y1 <= mw->g.y &&
|
||||
// e->x2 >= mw->g.x + mw->widthb && e->y2 >= mw->g.y + mw->heightb) {
|
||||
if (e->x1 <= mw->pending_g.x && e->x2 >= mw->pending_g.x + mw->widthb) {
|
||||
mw->randr_monitor = i;
|
||||
log_debug("Window %#010x (%s), %dx%d+%dx%d, is entirely on the "
|
||||
"monitor %d (%dx%d+%dx%d)",
|
||||
@@ -2990,12 +3005,11 @@ struct managed_win *find_managed_window_or_parent(session_t *ps, xcb_window_t wi
|
||||
// We traverse through its ancestors to find out the frame
|
||||
// Using find_win here because if we found a unmanaged window we know
|
||||
// about, we can stop early.
|
||||
while (wid && wid != ps->c.screen_info->root && !(w = find_win(ps, wid))) {
|
||||
while (wid && wid != ps->root && !(w = find_win(ps, wid))) {
|
||||
// xcb_query_tree probably fails if you run picom when X is
|
||||
// somehow initializing (like add it in .xinitrc). In this case
|
||||
// just leave it alone.
|
||||
auto reply =
|
||||
xcb_query_tree_reply(ps->c.c, xcb_query_tree(ps->c.c, wid), NULL);
|
||||
auto reply = xcb_query_tree_reply(ps->c, xcb_query_tree(ps->c, wid), NULL);
|
||||
if (reply == NULL) {
|
||||
break;
|
||||
}
|
||||
@@ -3050,6 +3064,10 @@ win_is_fullscreen_xcb(xcb_connection_t *c, const struct atom *a, const xcb_windo
|
||||
void win_set_flags(struct managed_win *w, uint64_t flags) {
|
||||
log_debug("Set flags %" PRIu64 " to window %#010x (%s)", flags, w->base.id, w->name);
|
||||
if (unlikely(w->state == WSTATE_DESTROYING)) {
|
||||
if (w->animation_progress != 1.0) {
|
||||
// Return because animation will trigger some of the flags
|
||||
return;
|
||||
}
|
||||
log_error("Flags set on a destroyed window %#010x (%s)", w->base.id, w->name);
|
||||
return;
|
||||
}
|
||||
@@ -3062,6 +3080,10 @@ void win_clear_flags(struct managed_win *w, uint64_t flags) {
|
||||
log_debug("Clear flags %" PRIu64 " from window %#010x (%s)", flags, w->base.id,
|
||||
w->name);
|
||||
if (unlikely(w->state == WSTATE_DESTROYING)) {
|
||||
if (w->animation_progress != 1.0) {
|
||||
// Return because animation will trigger some of the flags
|
||||
return;
|
||||
}
|
||||
log_warn("Flags cleared on a destroyed window %#010x (%s)", w->base.id,
|
||||
w->name);
|
||||
return;
|
||||
@@ -3071,7 +3093,7 @@ void win_clear_flags(struct managed_win *w, uint64_t flags) {
|
||||
}
|
||||
|
||||
void win_set_properties_stale(struct managed_win *w, const xcb_atom_t *props, int nprops) {
|
||||
auto const bits_per_element = sizeof(*w->stale_props) * 8;
|
||||
const auto bits_per_element = sizeof(*w->stale_props) * 8;
|
||||
size_t new_capacity = w->stale_props_capacity;
|
||||
|
||||
// Calculate the new capacity of the properties array
|
||||
@@ -3106,12 +3128,12 @@ static void win_clear_all_properties_stale(struct managed_win *w) {
|
||||
}
|
||||
|
||||
static bool win_fetch_and_unset_property_stale(struct managed_win *w, xcb_atom_t prop) {
|
||||
auto const bits_per_element = sizeof(*w->stale_props) * 8;
|
||||
const auto bits_per_element = sizeof(*w->stale_props) * 8;
|
||||
if (prop >= w->stale_props_capacity * bits_per_element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto const mask = 1UL << (prop % bits_per_element);
|
||||
const auto mask = 1UL << (prop % bits_per_element);
|
||||
bool ret = w->stale_props[prop / bits_per_element] & mask;
|
||||
w->stale_props[prop / bits_per_element] &= ~mask;
|
||||
return ret;
|
||||
@@ -3132,7 +3154,7 @@ bool win_check_flags_all(struct managed_win *w, uint64_t flags) {
|
||||
*/
|
||||
bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
|
||||
if (!ps->o.no_ewmh_fullscreen &&
|
||||
win_is_fullscreen_xcb(ps->c.c, ps->atoms, w->client_win)) {
|
||||
win_is_fullscreen_xcb(ps->c, ps->atoms, w->client_win)) {
|
||||
return true;
|
||||
}
|
||||
return rect_is_fullscreen(ps, w->g.x, w->g.y, w->widthb, w->heightb) &&
|
||||
@@ -3147,7 +3169,7 @@ bool win_is_fullscreen(const session_t *ps, const struct managed_win *w) {
|
||||
bool win_is_bypassing_compositor(const session_t *ps, const struct managed_win *w) {
|
||||
bool ret = false;
|
||||
|
||||
auto prop = x_get_prop(&ps->c, w->client_win, ps->atoms->a_NET_WM_BYPASS_COMPOSITOR,
|
||||
auto prop = x_get_prop(ps->c, w->client_win, ps->atoms->a_NET_WM_BYPASS_COMPOSITOR,
|
||||
1L, XCB_ATOM_CARDINAL, 32);
|
||||
|
||||
if (prop.nitems && *prop.c32 == 1) {
|
||||
@@ -3168,13 +3190,13 @@ bool win_is_focused_raw(const session_t *ps, const struct managed_win *w) {
|
||||
|
||||
// Find the managed window immediately below `i` in the window stack
|
||||
struct managed_win *
|
||||
win_stack_find_next_managed(const session_t *ps, const struct list_node *w) {
|
||||
while (!list_node_is_last(&ps->window_stack, w)) {
|
||||
auto next = list_entry(w->next, struct win, stack_neighbour);
|
||||
win_stack_find_next_managed(const session_t *ps, const struct list_node *i) {
|
||||
while (!list_node_is_last(&ps->window_stack, i)) {
|
||||
auto next = list_entry(i->next, struct win, stack_neighbour);
|
||||
if (next->managed) {
|
||||
return (struct managed_win *)next;
|
||||
}
|
||||
w = &next->stack_neighbour;
|
||||
i = &next->stack_neighbour;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
19
src/win.h
19
src/win.h
@@ -11,6 +11,11 @@
|
||||
|
||||
#include "uthash_extra.h"
|
||||
|
||||
// FIXME shouldn't need this
|
||||
#ifdef CONFIG_OPENGL
|
||||
#include <GL/gl.h>
|
||||
#endif
|
||||
|
||||
#include "c2.h"
|
||||
#include "compiler.h"
|
||||
#include "list.h"
|
||||
@@ -26,12 +31,11 @@ typedef struct session session_t;
|
||||
typedef struct _glx_texture glx_texture_t;
|
||||
|
||||
#define win_stack_foreach_managed(w, win_stack) \
|
||||
list_foreach(struct managed_win, w, win_stack, \
|
||||
base.stack_neighbour) if ((w)->base.managed)
|
||||
list_foreach(struct managed_win, w, win_stack, base.stack_neighbour) if (w->base.managed)
|
||||
|
||||
#define win_stack_foreach_managed_safe(w, win_stack) \
|
||||
list_foreach_safe(struct managed_win, w, win_stack, \
|
||||
base.stack_neighbour) if ((w)->base.managed)
|
||||
base.stack_neighbour) if (w->base.managed)
|
||||
|
||||
#ifdef CONFIG_OPENGL
|
||||
// FIXME this type should be in opengl.h
|
||||
@@ -121,6 +125,7 @@ struct managed_win {
|
||||
struct managed_win *prev_trans;
|
||||
/// Number of windows above this window
|
||||
int stacking_rank;
|
||||
uint32_t cur_desktop;
|
||||
// TODO(yshui) rethink reg_ignore
|
||||
|
||||
// Core members
|
||||
@@ -166,7 +171,7 @@ struct managed_win {
|
||||
/// opacity state, window geometry, window mapped/unmapped state,
|
||||
/// window mode of the windows above. DOES NOT INCLUDE the body of THIS WINDOW.
|
||||
/// NULL means reg_ignore has not been calculated for this window.
|
||||
/// 1 = tag prev , 2 = tag next, 4 = unmap
|
||||
/// 1 = tag prev , 2 = tag next, 4 = unmap
|
||||
uint32_t dwm_mask;
|
||||
rc_region_t *reg_ignore;
|
||||
/// Whether the reg_ignore of all windows beneath this window are valid
|
||||
@@ -376,7 +381,9 @@ void win_recheck_client(session_t *ps, struct managed_win *w);
|
||||
double attr_pure win_calc_opacity_target(session_t *ps, const struct managed_win *w);
|
||||
bool attr_pure win_should_dim(session_t *ps, const struct managed_win *w);
|
||||
|
||||
void win_update_monitor(struct x_monitors *monitors, struct managed_win *mw);
|
||||
// TODO(absolutelynothelix): rename to x_update_win_(randr_?)monitor and move to
|
||||
// the x.h.
|
||||
void win_update_monitor(int nmons, region_t *mons, struct managed_win *mw);
|
||||
|
||||
/**
|
||||
* Retrieve the bounding shape of a window.
|
||||
@@ -559,3 +566,5 @@ static inline bool attr_pure attr_unused win_has_frame(const struct managed_win
|
||||
return w->g.border_width || w->frame_extents.top || w->frame_extents.left ||
|
||||
w->frame_extents.right || w->frame_extents.bottom;
|
||||
}
|
||||
|
||||
void win_update_prop_desktop(session_t *ps, struct managed_win *w);
|
||||
|
||||
291
src/x.c
291
src/x.c
@@ -4,14 +4,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <X11/Xlib-xcb.h>
|
||||
#include <X11/Xutil.h>
|
||||
#include <pixman.h>
|
||||
#include <xcb/composite.h>
|
||||
#include <xcb/damage.h>
|
||||
#include <xcb/dpms.h>
|
||||
#include <xcb/glx.h>
|
||||
#include <xcb/present.h>
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/render.h>
|
||||
#include <xcb/sync.h>
|
||||
@@ -31,72 +28,6 @@
|
||||
#include "utils.h"
|
||||
#include "x.h"
|
||||
|
||||
// === Error handling ===
|
||||
|
||||
/**
|
||||
* Xlib error handler function.
|
||||
*/
|
||||
static int xerror(Display attr_unused *dpy, XErrorEvent *ev) {
|
||||
if (!ps_g) {
|
||||
// Do not ignore errors until the session has been initialized
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Fake a xcb error, fill in just enough information
|
||||
xcb_generic_error_t xcb_err;
|
||||
xcb_err.full_sequence = (uint32_t)ev->serial;
|
||||
xcb_err.major_code = ev->request_code;
|
||||
xcb_err.minor_code = ev->minor_code;
|
||||
xcb_err.error_code = ev->error_code;
|
||||
x_handle_error(&ps_g->c, &xcb_err);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void x_discard_pending(struct x_connection *c, uint32_t sequence) {
|
||||
while (c->pending_reply_head && sequence > c->pending_reply_head->sequence) {
|
||||
auto next = c->pending_reply_head->next;
|
||||
free(c->pending_reply_head);
|
||||
c->pending_reply_head = next;
|
||||
}
|
||||
if (!c->pending_reply_head) {
|
||||
c->pending_reply_tail = &c->pending_reply_head;
|
||||
}
|
||||
}
|
||||
|
||||
void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev) {
|
||||
x_discard_pending(c, ev->full_sequence);
|
||||
if (c->pending_reply_head && c->pending_reply_head->sequence == ev->full_sequence) {
|
||||
if (c->pending_reply_head->action != PENDING_REPLY_ACTION_IGNORE) {
|
||||
x_log_error(LOG_LEVEL_ERROR, ev->full_sequence, ev->major_code,
|
||||
ev->minor_code, ev->error_code);
|
||||
}
|
||||
switch (c->pending_reply_head->action) {
|
||||
case PENDING_REPLY_ACTION_ABORT:
|
||||
log_fatal("An unrecoverable X error occurred, aborting...");
|
||||
abort();
|
||||
case PENDING_REPLY_ACTION_DEBUG_ABORT: assert(false); break;
|
||||
case PENDING_REPLY_ACTION_IGNORE: break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
x_log_error(LOG_LEVEL_WARN, ev->full_sequence, ev->major_code, ev->minor_code,
|
||||
ev->error_code);
|
||||
}
|
||||
|
||||
/// Initialize x_connection struct from an Xlib Display.
|
||||
///
|
||||
/// Note this function doesn't take ownership of the Display, the caller is still
|
||||
/// responsible for closing it after `free_x_connection` is called.
|
||||
void x_connection_init(struct x_connection *c, Display *dpy) {
|
||||
c->dpy = dpy;
|
||||
c->c = XGetXCBConnection(dpy);
|
||||
c->pending_reply_tail = &c->pending_reply_head;
|
||||
c->previous_xerror_handler = XSetErrorHandler(xerror);
|
||||
|
||||
c->screen = DefaultScreen(dpy);
|
||||
c->screen_info = x_screen_of_display(c, c->screen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific attribute of a window.
|
||||
*
|
||||
@@ -112,11 +43,11 @@ void x_connection_init(struct x_connection *c, Display *dpy) {
|
||||
* @return a <code>winprop_t</code> structure containing the attribute
|
||||
* and number of items. A blank one on failure.
|
||||
*/
|
||||
winprop_t x_get_prop_with_offset(const struct x_connection *c, xcb_window_t w, xcb_atom_t atom,
|
||||
winprop_t x_get_prop_with_offset(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom,
|
||||
int offset, int length, xcb_atom_t rtype, int rformat) {
|
||||
xcb_get_property_reply_t *r = xcb_get_property_reply(
|
||||
c->c,
|
||||
xcb_get_property(c->c, 0, w, atom, rtype, to_u32_checked(offset),
|
||||
c,
|
||||
xcb_get_property(c, 0, w, atom, rtype, to_u32_checked(offset),
|
||||
to_u32_checked(length)),
|
||||
NULL);
|
||||
|
||||
@@ -140,10 +71,10 @@ winprop_t x_get_prop_with_offset(const struct x_connection *c, xcb_window_t w, x
|
||||
}
|
||||
|
||||
/// Get the type, format and size in bytes of a window's specific attribute.
|
||||
winprop_info_t x_get_prop_info(const struct x_connection *c, xcb_window_t w, xcb_atom_t atom) {
|
||||
winprop_info_t x_get_prop_info(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom) {
|
||||
xcb_generic_error_t *e = NULL;
|
||||
auto r = xcb_get_property_reply(
|
||||
c->c, xcb_get_property(c->c, 0, w, atom, XCB_ATOM_ANY, 0, 0), &e);
|
||||
c, xcb_get_property(c, 0, w, atom, XCB_ATOM_ANY, 0, 0), &e);
|
||||
if (!r) {
|
||||
log_debug_x_error(e, "Failed to get property info for window %#010x", w);
|
||||
free(e);
|
||||
@@ -163,7 +94,7 @@ winprop_info_t x_get_prop_info(const struct x_connection *c, xcb_window_t w, xcb
|
||||
*
|
||||
* @return the value if successful, 0 otherwise
|
||||
*/
|
||||
xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_atom_t aprop) {
|
||||
xcb_window_t wid_get_prop_window(xcb_connection_t *c, xcb_window_t wid, xcb_atom_t aprop) {
|
||||
// Get the attribute
|
||||
xcb_window_t p = XCB_NONE;
|
||||
winprop_t prop = x_get_prop(c, wid, aprop, 1L, XCB_ATOM_WINDOW, 32);
|
||||
@@ -184,7 +115,7 @@ xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_a
|
||||
bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char ***pstrlst,
|
||||
int *pnstr) {
|
||||
assert(ps->server_grabbed);
|
||||
auto prop_info = x_get_prop_info(&ps->c, wid, prop);
|
||||
auto prop_info = x_get_prop_info(ps->c, wid, prop);
|
||||
auto type = prop_info.type;
|
||||
auto format = prop_info.format;
|
||||
auto length = prop_info.length;
|
||||
@@ -209,7 +140,7 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
||||
xcb_generic_error_t *e = NULL;
|
||||
auto word_count = (length + 4 - 1) / 4;
|
||||
auto r = xcb_get_property_reply(
|
||||
ps->c.c, xcb_get_property(ps->c.c, 0, wid, prop, type, 0, word_count), &e);
|
||||
ps->c, xcb_get_property(ps->c, 0, wid, prop, type, 0, word_count), &e);
|
||||
if (!r) {
|
||||
log_debug_x_error(e, "Failed to get window property for %#010x", wid);
|
||||
free(e);
|
||||
@@ -267,14 +198,14 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
||||
// of this program
|
||||
static thread_local xcb_render_query_pict_formats_reply_t *g_pictfmts = NULL;
|
||||
|
||||
static inline void x_get_server_pictfmts(struct x_connection *c) {
|
||||
static inline void x_get_server_pictfmts(xcb_connection_t *c) {
|
||||
if (g_pictfmts) {
|
||||
return;
|
||||
}
|
||||
xcb_generic_error_t *e = NULL;
|
||||
// Get window picture format
|
||||
g_pictfmts = xcb_render_query_pict_formats_reply(
|
||||
c->c, xcb_render_query_pict_formats(c->c), &e);
|
||||
g_pictfmts =
|
||||
xcb_render_query_pict_formats_reply(c, xcb_render_query_pict_formats(c), &e);
|
||||
if (e || !g_pictfmts) {
|
||||
log_fatal("failed to get pict formats\n");
|
||||
abort();
|
||||
@@ -282,7 +213,7 @@ static inline void x_get_server_pictfmts(struct x_connection *c) {
|
||||
}
|
||||
|
||||
const xcb_render_pictforminfo_t *
|
||||
x_get_pictform_for_visual(struct x_connection *c, xcb_visualid_t visual) {
|
||||
x_get_pictform_for_visual(xcb_connection_t *c, xcb_visualid_t visual) {
|
||||
x_get_server_pictfmts(c);
|
||||
|
||||
xcb_render_pictvisual_t *pv = xcb_render_util_find_visual_format(g_pictfmts, visual);
|
||||
@@ -313,7 +244,7 @@ static xcb_visualid_t attr_pure x_get_visual_for_pictfmt(xcb_render_query_pict_f
|
||||
return XCB_NONE;
|
||||
}
|
||||
|
||||
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(xcb_connection_t *c, xcb_pict_standard_t std) {
|
||||
x_get_server_pictfmts(c);
|
||||
|
||||
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
|
||||
@@ -322,7 +253,7 @@ xcb_visualid_t x_get_visual_for_standard(struct x_connection *c, xcb_pict_standa
|
||||
}
|
||||
|
||||
xcb_render_pictformat_t
|
||||
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
||||
x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std) {
|
||||
x_get_server_pictfmts(c);
|
||||
|
||||
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, std);
|
||||
@@ -330,8 +261,8 @@ x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std) {
|
||||
return pictfmt->id;
|
||||
}
|
||||
|
||||
int x_get_visual_depth(struct x_connection *c, xcb_visualid_t visual) {
|
||||
auto setup = xcb_get_setup(c->c);
|
||||
int x_get_visual_depth(xcb_connection_t *c, xcb_visualid_t visual) {
|
||||
auto setup = xcb_get_setup(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);
|
||||
@@ -349,7 +280,7 @@ int x_get_visual_depth(struct x_connection *c, xcb_visualid_t visual) {
|
||||
}
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
|
||||
x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *c,
|
||||
const xcb_render_pictforminfo_t *pictfmt,
|
||||
xcb_pixmap_t pixmap, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr) {
|
||||
@@ -363,9 +294,9 @@ x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
|
||||
}
|
||||
|
||||
xcb_render_picture_t tmp_picture = x_new_id(c);
|
||||
xcb_generic_error_t *e = xcb_request_check(
|
||||
c->c, xcb_render_create_picture_checked(c->c, tmp_picture, pixmap,
|
||||
pictfmt->id, valuemask, buf));
|
||||
xcb_generic_error_t *e =
|
||||
xcb_request_check(c, xcb_render_create_picture_checked(
|
||||
c, tmp_picture, pixmap, pictfmt->id, valuemask, buf));
|
||||
free(buf);
|
||||
if (e) {
|
||||
log_error_x_error(e, "failed to create picture");
|
||||
@@ -376,7 +307,7 @@ x_create_picture_with_pictfmt_and_pixmap(struct x_connection *c,
|
||||
}
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_visual_and_pixmap(struct x_connection *c, xcb_visualid_t visual,
|
||||
x_create_picture_with_visual_and_pixmap(xcb_connection_t *c, xcb_visualid_t visual,
|
||||
xcb_pixmap_t pixmap, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr) {
|
||||
const xcb_render_pictforminfo_t *pictfmt = x_get_pictform_for_visual(c, visual);
|
||||
@@ -384,7 +315,7 @@ x_create_picture_with_visual_and_pixmap(struct x_connection *c, xcb_visualid_t v
|
||||
}
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_standard_and_pixmap(struct x_connection *c, xcb_pict_standard_t standard,
|
||||
x_create_picture_with_standard_and_pixmap(xcb_connection_t *c, xcb_pict_standard_t standard,
|
||||
xcb_pixmap_t pixmap, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr) {
|
||||
x_get_server_pictfmts(c);
|
||||
@@ -395,26 +326,26 @@ x_create_picture_with_standard_and_pixmap(struct x_connection *c, xcb_pict_stand
|
||||
}
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_standard(struct x_connection *c, int w, int h,
|
||||
x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
|
||||
xcb_pict_standard_t standard, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr) {
|
||||
x_get_server_pictfmts(c);
|
||||
|
||||
auto pictfmt = xcb_render_util_find_standard_format(g_pictfmts, standard);
|
||||
assert(pictfmt);
|
||||
return x_create_picture_with_pictfmt(c, w, h, pictfmt, valuemask, attr);
|
||||
return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an picture.
|
||||
*/
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_pictfmt(struct x_connection *c, int w, int h,
|
||||
x_create_picture_with_pictfmt(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
|
||||
const xcb_render_pictforminfo_t *pictfmt, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr) {
|
||||
uint8_t depth = pictfmt->depth;
|
||||
|
||||
xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, w, h);
|
||||
xcb_pixmap_t tmp_pixmap = x_create_pixmap(c, depth, d, w, h);
|
||||
if (!tmp_pixmap) {
|
||||
return XCB_NONE;
|
||||
}
|
||||
@@ -422,23 +353,23 @@ x_create_picture_with_pictfmt(struct x_connection *c, int w, int h,
|
||||
xcb_render_picture_t picture = x_create_picture_with_pictfmt_and_pixmap(
|
||||
c, pictfmt, tmp_pixmap, valuemask, attr);
|
||||
|
||||
set_cant_fail_cookie(c, xcb_free_pixmap(c->c, tmp_pixmap));
|
||||
xcb_free_pixmap(c, tmp_pixmap);
|
||||
|
||||
return picture;
|
||||
}
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_visual(struct x_connection *c, int w, int h, xcb_visualid_t visual,
|
||||
uint32_t valuemask,
|
||||
x_create_picture_with_visual(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
|
||||
xcb_visualid_t visual, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr) {
|
||||
auto pictfmt = x_get_pictform_for_visual(c, visual);
|
||||
return x_create_picture_with_pictfmt(c, w, h, pictfmt, valuemask, attr);
|
||||
return x_create_picture_with_pictfmt(c, d, w, h, pictfmt, valuemask, attr);
|
||||
}
|
||||
|
||||
bool x_fetch_region(struct x_connection *c, xcb_xfixes_region_t r, pixman_region32_t *res) {
|
||||
bool x_fetch_region(xcb_connection_t *c, xcb_xfixes_region_t r, pixman_region32_t *res) {
|
||||
xcb_generic_error_t *e = NULL;
|
||||
xcb_xfixes_fetch_region_reply_t *xr =
|
||||
xcb_xfixes_fetch_region_reply(c->c, xcb_xfixes_fetch_region(c->c, r), &e);
|
||||
xcb_xfixes_fetch_region_reply(c, xcb_xfixes_fetch_region(c, r), &e);
|
||||
if (!xr) {
|
||||
log_error_x_error(e, "Failed to fetch rectangles");
|
||||
return false;
|
||||
@@ -459,7 +390,7 @@ bool x_fetch_region(struct x_connection *c, xcb_xfixes_region_t r, pixman_region
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
|
||||
uint32_t x_create_region(xcb_connection_t *c, const region_t *reg) {
|
||||
if (!reg) {
|
||||
return XCB_NONE;
|
||||
}
|
||||
@@ -479,8 +410,8 @@ uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
|
||||
}
|
||||
|
||||
xcb_xfixes_region_t ret = x_new_id(c);
|
||||
bool success = XCB_AWAIT_VOID(xcb_xfixes_create_region, c->c, ret,
|
||||
to_u32_checked(nrects), xrects);
|
||||
bool success =
|
||||
XCB_AWAIT_VOID(xcb_xfixes_create_region, c, ret, to_u32_checked(nrects), xrects);
|
||||
free(xrects);
|
||||
if (!success) {
|
||||
return XCB_NONE;
|
||||
@@ -488,13 +419,13 @@ uint32_t x_create_region(struct x_connection *c, const region_t *reg) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void x_destroy_region(struct x_connection *c, xcb_xfixes_region_t r) {
|
||||
void x_destroy_region(xcb_connection_t *c, xcb_xfixes_region_t r) {
|
||||
if (r != XCB_NONE) {
|
||||
set_debug_cant_fail_cookie(c, xcb_xfixes_destroy_region(c->c, r));
|
||||
xcb_xfixes_destroy_region(c, r);
|
||||
}
|
||||
}
|
||||
|
||||
void x_set_picture_clip_region(struct x_connection *c, xcb_render_picture_t pict,
|
||||
void x_set_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict,
|
||||
int16_t clip_x_origin, int16_t clip_y_origin,
|
||||
const region_t *reg) {
|
||||
int nrects;
|
||||
@@ -509,10 +440,9 @@ void x_set_picture_clip_region(struct x_connection *c, xcb_render_picture_t pict
|
||||
};
|
||||
}
|
||||
|
||||
xcb_generic_error_t *e =
|
||||
xcb_request_check(c->c, xcb_render_set_picture_clip_rectangles_checked(
|
||||
c->c, pict, clip_x_origin, clip_y_origin,
|
||||
to_u32_checked(nrects), xrects));
|
||||
xcb_generic_error_t *e = xcb_request_check(
|
||||
c, xcb_render_set_picture_clip_rectangles_checked(
|
||||
c, pict, clip_x_origin, clip_y_origin, to_u32_checked(nrects), xrects));
|
||||
if (e) {
|
||||
log_error_x_error(e, "Failed to set clip region");
|
||||
free(e);
|
||||
@@ -520,28 +450,17 @@ void x_set_picture_clip_region(struct x_connection *c, xcb_render_picture_t pict
|
||||
free(xrects);
|
||||
}
|
||||
|
||||
void x_clear_picture_clip_region(struct x_connection *c, xcb_render_picture_t pict) {
|
||||
void x_clear_picture_clip_region(xcb_connection_t *c, xcb_render_picture_t pict) {
|
||||
assert(pict != XCB_NONE);
|
||||
xcb_render_change_picture_value_list_t v = {.clipmask = XCB_NONE};
|
||||
xcb_generic_error_t *e = xcb_request_check(
|
||||
c->c, xcb_render_change_picture_checked(c->c, pict, XCB_RENDER_CP_CLIP_MASK, &v));
|
||||
c, xcb_render_change_picture_checked(c, pict, XCB_RENDER_CP_CLIP_MASK, &v));
|
||||
if (e) {
|
||||
log_error_x_error(e, "failed to clear clip region");
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a <code>Picture</code>.
|
||||
*
|
||||
* Picture must be valid.
|
||||
*/
|
||||
void x_free_picture(struct x_connection *c, xcb_render_picture_t p) {
|
||||
assert(p != XCB_NONE);
|
||||
auto cookie = xcb_render_free_picture(c->c, p);
|
||||
set_debug_cant_fail_cookie(c, cookie);
|
||||
}
|
||||
|
||||
enum {
|
||||
XSyncBadCounter = 0,
|
||||
XSyncBadAlarm = 1,
|
||||
@@ -674,12 +593,12 @@ const char *x_strerror(xcb_generic_error_t *e) {
|
||||
/**
|
||||
* Create a pixmap and check that creation succeeded.
|
||||
*/
|
||||
xcb_pixmap_t x_create_pixmap(struct x_connection *c, uint8_t depth, int width, int height) {
|
||||
xcb_pixmap_t x_create_pixmap(xcb_connection_t *c, uint8_t depth, xcb_drawable_t drawable,
|
||||
int width, int height) {
|
||||
xcb_pixmap_t pix = x_new_id(c);
|
||||
xcb_void_cookie_t cookie =
|
||||
xcb_create_pixmap_checked(c->c, depth, pix, c->screen_info->root,
|
||||
to_u16_checked(width), to_u16_checked(height));
|
||||
xcb_generic_error_t *err = xcb_request_check(c->c, cookie);
|
||||
xcb_void_cookie_t cookie = xcb_create_pixmap_checked(
|
||||
c, depth, pix, drawable, to_u16_checked(width), to_u16_checked(height));
|
||||
xcb_generic_error_t *err = xcb_request_check(c, cookie);
|
||||
if (err == NULL) {
|
||||
return pix;
|
||||
}
|
||||
@@ -695,12 +614,12 @@ xcb_pixmap_t x_create_pixmap(struct x_connection *c, uint8_t depth, int width, i
|
||||
* 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) {
|
||||
bool x_validate_pixmap(xcb_connection_t *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);
|
||||
auto r = xcb_get_geometry_reply(c, xcb_get_geometry(c, pixmap), NULL);
|
||||
if (!r) {
|
||||
return false;
|
||||
}
|
||||
@@ -709,27 +628,22 @@ bool x_validate_pixmap(struct x_connection *c, xcb_pixmap_t pixmap) {
|
||||
free(r);
|
||||
return ret;
|
||||
}
|
||||
/// Names of root window properties that could point to a pixmap of
|
||||
/// background.
|
||||
static const char *background_props_str[] = {
|
||||
"_XROOTPMAP_ID",
|
||||
"_XSETROOT_ID",
|
||||
0,
|
||||
};
|
||||
|
||||
/// 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
|
||||
/// associated with the background pixmap alive but we listen for it's changes and update
|
||||
/// the background pixmap accordingly.
|
||||
///
|
||||
/// For details on the _XSETROOT_ID root window property and it's usage see:
|
||||
/// https://metacpan.org/pod/X11::Protocol::XSetRoot#_XSETROOT_ID
|
||||
/// https://gitlab.freedesktop.org/xorg/app/xsetroot/-/blob/435d35409768de7cbc2c47a6322192dd4b480545/xsetroot.c#L318-352
|
||||
/// https://github.com/ImageMagick/ImageMagick/blob/d04a47227637dbb3af9231b0107ccf9677bf985e/MagickCore/xwindow.c#L9203-L9260
|
||||
/// https://github.com/ImageMagick/ImageMagick/blob/d04a47227637dbb3af9231b0107ccf9677bf985e/MagickCore/xwindow.c#L1853-L1922
|
||||
/// https://www.fvwm.org/Archive/Manpages/fvwm-root.html
|
||||
|
||||
xcb_pixmap_t x_get_root_back_pixmap(struct x_connection *c, struct atom *atoms) {
|
||||
xcb_pixmap_t
|
||||
x_get_root_back_pixmap(xcb_connection_t *c, xcb_window_t root, struct atom *atoms) {
|
||||
xcb_pixmap_t pixmap = XCB_NONE;
|
||||
|
||||
xcb_atom_t root_back_pixmap_atoms[] = {atoms->a_XROOTPMAP_ID, atoms->aESETROOT_PMAP_ID};
|
||||
for (size_t i = 0; i < ARR_SIZE(root_back_pixmap_atoms); i++) {
|
||||
winprop_t prop =
|
||||
x_get_prop(c, c->screen_info->root, root_back_pixmap_atoms[i], 1,
|
||||
XCB_ATOM_PIXMAP, 32);
|
||||
// Get the values of background attributes
|
||||
for (int p = 0; background_props_str[p]; p++) {
|
||||
xcb_atom_t prop_atom = get_atom(atoms, background_props_str[p]);
|
||||
winprop_t prop = x_get_prop(c, root, prop_atom, 1, XCB_ATOM_PIXMAP, 32);
|
||||
if (prop.nitems) {
|
||||
pixmap = (xcb_pixmap_t)*prop.p32;
|
||||
free_winprop(&prop);
|
||||
@@ -742,32 +656,37 @@ xcb_pixmap_t x_get_root_back_pixmap(struct x_connection *c, struct atom *atoms)
|
||||
}
|
||||
|
||||
bool x_is_root_back_pixmap_atom(struct atom *atoms, xcb_atom_t atom) {
|
||||
return atom == atoms->a_XROOTPMAP_ID || atom == atoms->aESETROOT_PMAP_ID ||
|
||||
atom == atoms->a_XSETROOT_ID;
|
||||
for (int p = 0; background_props_str[p]; p++) {
|
||||
xcb_atom_t prop_atom = get_atom(atoms, background_props_str[p]);
|
||||
if (prop_atom == atom) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes a X Render drawable to ensure all pending painting requests
|
||||
* are completed.
|
||||
*/
|
||||
bool x_fence_sync(struct x_connection *c, xcb_sync_fence_t f) {
|
||||
bool x_fence_sync(xcb_connection_t *c, xcb_sync_fence_t f) {
|
||||
// TODO(richardgv): If everybody just follows the rules stated in X Sync
|
||||
// prototype, we need only one fence per screen, but let's stay a bit
|
||||
// cautious right now
|
||||
|
||||
auto e = xcb_request_check(c->c, xcb_sync_trigger_fence_checked(c->c, f));
|
||||
auto e = xcb_request_check(c, xcb_sync_trigger_fence_checked(c, f));
|
||||
if (e) {
|
||||
log_error_x_error(e, "Failed to trigger the fence");
|
||||
goto err;
|
||||
}
|
||||
|
||||
e = xcb_request_check(c->c, xcb_sync_await_fence_checked(c->c, 1, &f));
|
||||
e = xcb_request_check(c, xcb_sync_await_fence_checked(c, 1, &f));
|
||||
if (e) {
|
||||
log_error_x_error(e, "Failed to await on a fence");
|
||||
goto err;
|
||||
}
|
||||
|
||||
e = xcb_request_check(c->c, xcb_sync_reset_fence_checked(c->c, f));
|
||||
e = xcb_request_check(c, xcb_sync_reset_fence_checked(c, f));
|
||||
if (e) {
|
||||
log_error_x_error(e, "Failed to reset the fence");
|
||||
goto err;
|
||||
@@ -779,31 +698,6 @@ err:
|
||||
return false;
|
||||
}
|
||||
|
||||
void x_request_vblank_event(struct x_connection *c, xcb_window_t window, uint64_t msc) {
|
||||
auto cookie = xcb_present_notify_msc(c->c, window, 0, msc, 1, 0);
|
||||
set_cant_fail_cookie(c, cookie);
|
||||
}
|
||||
|
||||
static inline bool dpms_screen_is_off(xcb_dpms_info_reply_t *info) {
|
||||
// state is a bool indicating whether dpms is enabled
|
||||
return info->state && (info->power_level != XCB_DPMS_DPMS_MODE_ON);
|
||||
}
|
||||
|
||||
bool x_check_dpms_status(struct x_connection *c, bool *screen_is_off) {
|
||||
auto r = xcb_dpms_info_reply(c->c, xcb_dpms_info(c->c), NULL);
|
||||
if (!r) {
|
||||
log_error("Failed to query DPMS status.");
|
||||
return false;
|
||||
}
|
||||
auto now_screen_is_off = dpms_screen_is_off(r);
|
||||
if (*screen_is_off != now_screen_is_off) {
|
||||
log_debug("Screen is now %s", now_screen_is_off ? "off" : "on");
|
||||
*screen_is_off = now_screen_is_off;
|
||||
}
|
||||
free(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a struct conv to a X picture convolution filter, normalizing the kernel
|
||||
* in the process. Allow the caller to specify the element at the center of the kernel,
|
||||
@@ -854,7 +748,7 @@ void x_create_convolution_kernel(const conv *kernel, double center,
|
||||
|
||||
/// Generate a search criteria for fbconfig from a X visual.
|
||||
/// 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(xcb_connection_t *c, xcb_visualid_t visual) {
|
||||
auto pictfmt = x_get_pictform_for_visual(c, visual);
|
||||
auto depth = x_get_visual_depth(c, visual);
|
||||
if (!pictfmt || depth == -1) {
|
||||
@@ -882,10 +776,10 @@ 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_t *x_screen_of_display(xcb_connection_t *c, int screen) {
|
||||
xcb_screen_iterator_t iter;
|
||||
|
||||
iter = xcb_setup_roots_iterator(xcb_get_setup(c->c));
|
||||
iter = xcb_setup_roots_iterator(xcb_get_setup(c));
|
||||
for (; iter.rem; --screen, xcb_screen_next(&iter)) {
|
||||
if (screen == 0) {
|
||||
return iter.data;
|
||||
@@ -895,34 +789,39 @@ xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void x_update_monitors(struct x_connection *c, struct x_monitors *m) {
|
||||
x_free_monitor_info(m);
|
||||
void x_update_randr_monitors(session_t *ps) {
|
||||
x_free_randr_info(ps);
|
||||
|
||||
if (!(ps->o.crop_shadow_to_monitor || ps->o.animations) || !ps->randr_exists) {
|
||||
return;
|
||||
}
|
||||
|
||||
xcb_randr_get_monitors_reply_t *r = xcb_randr_get_monitors_reply(
|
||||
c->c, xcb_randr_get_monitors(c->c, c->screen_info->root, true), NULL);
|
||||
ps->c, xcb_randr_get_monitors(ps->c, ps->root, true), NULL);
|
||||
if (!r) {
|
||||
return;
|
||||
}
|
||||
|
||||
m->count = xcb_randr_get_monitors_monitors_length(r);
|
||||
m->regions = ccalloc(m->count, region_t);
|
||||
ps->randr_nmonitors = xcb_randr_get_monitors_monitors_length(r);
|
||||
ps->randr_monitor_regs = ccalloc(ps->randr_nmonitors, region_t);
|
||||
xcb_randr_monitor_info_iterator_t monitor_info_it =
|
||||
xcb_randr_get_monitors_monitors_iterator(r);
|
||||
for (int i = 0; monitor_info_it.rem; xcb_randr_monitor_info_next(&monitor_info_it)) {
|
||||
xcb_randr_monitor_info_t *mi = monitor_info_it.data;
|
||||
pixman_region32_init_rect(&m->regions[i++], mi->x, mi->y, mi->width, mi->height);
|
||||
pixman_region32_init_rect(&ps->randr_monitor_regs[i++], mi->x, mi->y,
|
||||
mi->width, mi->height);
|
||||
}
|
||||
|
||||
free(r);
|
||||
}
|
||||
|
||||
void x_free_monitor_info(struct x_monitors *m) {
|
||||
if (m->regions) {
|
||||
for (int i = 0; i < m->count; i++) {
|
||||
pixman_region32_fini(&m->regions[i]);
|
||||
void x_free_randr_info(session_t *ps) {
|
||||
if (ps->randr_monitor_regs) {
|
||||
for (int i = 0; i < ps->randr_nmonitors; i++) {
|
||||
pixman_region32_fini(&ps->randr_monitor_regs[i]);
|
||||
}
|
||||
free(m->regions);
|
||||
m->regions = NULL;
|
||||
free(ps->randr_monitor_regs);
|
||||
ps->randr_monitor_regs = NULL;
|
||||
}
|
||||
m->count = 0;
|
||||
ps->randr_nmonitors = 0;
|
||||
}
|
||||
|
||||
208
src/x.h
208
src/x.h
@@ -1,7 +1,6 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright (c) 2018 Yuxuan Shui <yshuiv7@gmail.com>
|
||||
#pragma once
|
||||
#include <X11/Xlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
@@ -56,42 +55,6 @@ struct xvisual_info {
|
||||
xcb_visualid_t visual;
|
||||
};
|
||||
|
||||
enum pending_reply_action {
|
||||
PENDING_REPLY_ACTION_IGNORE,
|
||||
PENDING_REPLY_ACTION_ABORT,
|
||||
PENDING_REPLY_ACTION_DEBUG_ABORT,
|
||||
};
|
||||
|
||||
typedef struct pending_reply {
|
||||
struct pending_reply *next;
|
||||
unsigned long sequence;
|
||||
enum pending_reply_action action;
|
||||
} pending_reply_t;
|
||||
|
||||
struct x_connection {
|
||||
/// XCB connection.
|
||||
xcb_connection_t *c;
|
||||
/// Display in use.
|
||||
Display *dpy;
|
||||
/// Head pointer of the error ignore linked list.
|
||||
pending_reply_t *pending_reply_head;
|
||||
/// Pointer to the <code>next</code> member of tail element of the error
|
||||
/// ignore linked list.
|
||||
pending_reply_t **pending_reply_tail;
|
||||
/// Previous handler of X errors
|
||||
XErrorHandler previous_xerror_handler;
|
||||
/// Default screen
|
||||
int screen;
|
||||
/// Information about the default screen
|
||||
xcb_screen_t *screen_info;
|
||||
};
|
||||
|
||||
/// Monitor info
|
||||
struct x_monitors {
|
||||
int count;
|
||||
region_t *regions;
|
||||
};
|
||||
|
||||
#define XCB_AWAIT_VOID(func, c, ...) \
|
||||
({ \
|
||||
bool __success = true; \
|
||||
@@ -129,8 +92,8 @@ struct x_monitors {
|
||||
#define DOUBLE_TO_XFIXED(value) ((xcb_render_fixed_t)(((double)(value)) * 65536))
|
||||
|
||||
/// Wraps x_new_id. abort the program if x_new_id returns error
|
||||
static inline uint32_t x_new_id(struct x_connection *c) {
|
||||
auto ret = xcb_generate_id(c->c);
|
||||
static inline uint32_t x_new_id(xcb_connection_t *c) {
|
||||
auto ret = xcb_generate_id(c);
|
||||
if (ret == (uint32_t)-1) {
|
||||
log_fatal("We seems to have run of XIDs. This is either a bug in the X "
|
||||
"server, or a resource leakage in the compositor. Please open "
|
||||
@@ -140,73 +103,6 @@ static inline uint32_t x_new_id(struct x_connection *c) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void set_reply_action(struct x_connection *c, uint32_t sequence,
|
||||
enum pending_reply_action action) {
|
||||
auto i = cmalloc(pending_reply_t);
|
||||
|
||||
i->sequence = sequence;
|
||||
i->next = 0;
|
||||
i->action = action;
|
||||
*c->pending_reply_tail = i;
|
||||
c->pending_reply_tail = &i->next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignore X errors caused by given X request.
|
||||
*/
|
||||
static inline void attr_unused set_ignore_cookie(struct x_connection *c,
|
||||
xcb_void_cookie_t cookie) {
|
||||
set_reply_action(c, cookie.sequence, PENDING_REPLY_ACTION_IGNORE);
|
||||
}
|
||||
|
||||
static inline void attr_unused set_cant_fail_cookie(struct x_connection *c,
|
||||
xcb_void_cookie_t cookie) {
|
||||
set_reply_action(c, cookie.sequence, PENDING_REPLY_ACTION_ABORT);
|
||||
}
|
||||
|
||||
static inline void attr_unused set_debug_cant_fail_cookie(struct x_connection *c,
|
||||
xcb_void_cookie_t cookie) {
|
||||
#ifndef NDEBUG
|
||||
set_reply_action(c, cookie.sequence, PENDING_REPLY_ACTION_DEBUG_ABORT);
|
||||
#else
|
||||
(void)c;
|
||||
(void)cookie;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void attr_unused free_x_connection(struct x_connection *c) {
|
||||
pending_reply_t *next = NULL;
|
||||
for (auto ign = c->pending_reply_head; ign; ign = next) {
|
||||
next = ign->next;
|
||||
|
||||
free(ign);
|
||||
}
|
||||
|
||||
// Reset head and tail
|
||||
c->pending_reply_head = NULL;
|
||||
c->pending_reply_tail = &c->pending_reply_head;
|
||||
|
||||
XSetErrorHandler(c->previous_xerror_handler);
|
||||
}
|
||||
|
||||
/// Initialize x_connection struct from an Xlib Display.
|
||||
///
|
||||
/// Note this function doesn't take ownership of the Display, the caller is still
|
||||
/// responsible for closing it after `free_x_connection` is called.
|
||||
void x_connection_init(struct x_connection *c, Display *dpy);
|
||||
|
||||
/// Discard queued pending replies.
|
||||
///
|
||||
/// We have received reply with sequence number `sequence`, which means all pending
|
||||
/// replies with sequence number less than `sequence` will never be received. So discard
|
||||
/// them.
|
||||
void x_discard_pending(struct x_connection *c, uint32_t sequence);
|
||||
|
||||
/// Handle X errors.
|
||||
///
|
||||
/// 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);
|
||||
|
||||
/**
|
||||
* Send a request to X server and get the reply to make sure all previous
|
||||
* requests are processed, and their replies received
|
||||
@@ -214,8 +110,8 @@ void x_handle_error(struct x_connection *c, xcb_generic_error_t *ev);
|
||||
* 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));
|
||||
static inline void x_sync(xcb_connection_t *c) {
|
||||
free(xcb_get_input_focus_reply(c, xcb_get_input_focus(c), NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,26 +129,25 @@ static inline void x_sync(struct x_connection *c) {
|
||||
* @return a <code>winprop_t</code> structure containing the attribute
|
||||
* and number of items. A blank one on failure.
|
||||
*/
|
||||
winprop_t x_get_prop_with_offset(const struct x_connection *c, xcb_window_t w, xcb_atom_t atom,
|
||||
winprop_t x_get_prop_with_offset(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom,
|
||||
int offset, int length, xcb_atom_t rtype, int rformat);
|
||||
|
||||
/**
|
||||
* Wrapper of wid_get_prop_adv().
|
||||
*/
|
||||
static inline winprop_t
|
||||
x_get_prop(const struct x_connection *c, xcb_window_t wid, xcb_atom_t atom, int length,
|
||||
xcb_atom_t rtype, int rformat) {
|
||||
static inline winprop_t x_get_prop(xcb_connection_t *c, xcb_window_t wid, xcb_atom_t atom,
|
||||
int length, xcb_atom_t rtype, int rformat) {
|
||||
return x_get_prop_with_offset(c, wid, atom, 0L, length, rtype, rformat);
|
||||
}
|
||||
|
||||
/// Get the type, format and size in bytes of a window's specific attribute.
|
||||
winprop_info_t x_get_prop_info(const struct x_connection *c, xcb_window_t w, xcb_atom_t atom);
|
||||
winprop_info_t x_get_prop_info(xcb_connection_t *c, xcb_window_t w, xcb_atom_t atom);
|
||||
|
||||
/// Discard all X events in queue or in flight. Should only be used when the server is
|
||||
/// grabbed
|
||||
static inline void x_discard_events(struct x_connection *c) {
|
||||
static inline void x_discard_events(xcb_connection_t *c) {
|
||||
xcb_generic_event_t *e;
|
||||
while ((e = xcb_poll_for_event(c->c))) {
|
||||
while ((e = xcb_poll_for_event(c))) {
|
||||
free(e);
|
||||
}
|
||||
}
|
||||
@@ -262,7 +157,7 @@ static inline void x_discard_events(struct x_connection *c) {
|
||||
*
|
||||
* @return the value if successful, 0 otherwise
|
||||
*/
|
||||
xcb_window_t wid_get_prop_window(struct x_connection *c, xcb_window_t wid, xcb_atom_t aprop);
|
||||
xcb_window_t wid_get_prop_window(xcb_connection_t *c, xcb_window_t wid, xcb_atom_t aprop);
|
||||
|
||||
/**
|
||||
* Get the value of a text property of a window.
|
||||
@@ -275,30 +170,30 @@ bool wid_get_text_prop(session_t *ps, xcb_window_t wid, xcb_atom_t prop, char **
|
||||
int *pnstr);
|
||||
|
||||
const xcb_render_pictforminfo_t *
|
||||
x_get_pictform_for_visual(struct x_connection *, xcb_visualid_t);
|
||||
int x_get_visual_depth(struct x_connection *, xcb_visualid_t);
|
||||
x_get_pictform_for_visual(xcb_connection_t *, xcb_visualid_t);
|
||||
int x_get_visual_depth(xcb_connection_t *, xcb_visualid_t);
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_pictfmt_and_pixmap(struct x_connection *,
|
||||
x_create_picture_with_pictfmt_and_pixmap(xcb_connection_t *,
|
||||
const xcb_render_pictforminfo_t *pictfmt,
|
||||
xcb_pixmap_t pixmap, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr)
|
||||
attr_nonnull(1, 2);
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_visual_and_pixmap(struct x_connection *, xcb_visualid_t visual,
|
||||
x_create_picture_with_visual_and_pixmap(xcb_connection_t *, xcb_visualid_t visual,
|
||||
xcb_pixmap_t pixmap, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr)
|
||||
attr_nonnull(1);
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_standard_and_pixmap(struct x_connection *, xcb_pict_standard_t standard,
|
||||
x_create_picture_with_standard_and_pixmap(xcb_connection_t *, xcb_pict_standard_t standard,
|
||||
xcb_pixmap_t pixmap, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr)
|
||||
attr_nonnull(1);
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_standard(struct x_connection *c, int w, int h,
|
||||
x_create_picture_with_standard(xcb_connection_t *c, xcb_drawable_t d, int w, int h,
|
||||
xcb_pict_standard_t standard, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr)
|
||||
attr_nonnull(1);
|
||||
@@ -307,37 +202,30 @@ x_create_picture_with_standard(struct x_connection *c, int w, int h,
|
||||
* Create an picture.
|
||||
*/
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_pictfmt(struct x_connection *, int w, int h,
|
||||
x_create_picture_with_pictfmt(xcb_connection_t *, xcb_drawable_t, int w, int h,
|
||||
const xcb_render_pictforminfo_t *pictfmt, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr)
|
||||
attr_nonnull(1, 4);
|
||||
attr_nonnull(1, 5);
|
||||
|
||||
xcb_render_picture_t
|
||||
x_create_picture_with_visual(struct x_connection *, int w, int h, xcb_visualid_t visual,
|
||||
uint32_t valuemask,
|
||||
x_create_picture_with_visual(xcb_connection_t *, xcb_drawable_t, int w, int h,
|
||||
xcb_visualid_t visual, uint32_t valuemask,
|
||||
const xcb_render_create_picture_value_list_t *attr)
|
||||
attr_nonnull(1);
|
||||
|
||||
/// 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(xcb_connection_t *, xcb_xfixes_region_t r, region_t *res);
|
||||
|
||||
/// 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(xcb_connection_t *c, const region_t *reg);
|
||||
|
||||
/// Destroy a X region
|
||||
void x_destroy_region(struct x_connection *c, uint32_t region);
|
||||
void x_destroy_region(xcb_connection_t *c, uint32_t region);
|
||||
|
||||
void x_set_picture_clip_region(struct x_connection *, xcb_render_picture_t,
|
||||
int16_t clip_x_origin, int16_t clip_y_origin, const region_t *);
|
||||
void x_set_picture_clip_region(xcb_connection_t *, xcb_render_picture_t, int16_t clip_x_origin,
|
||||
int16_t clip_y_origin, const region_t *);
|
||||
|
||||
void x_clear_picture_clip_region(struct x_connection *, xcb_render_picture_t pict);
|
||||
|
||||
/**
|
||||
* Destroy a <code>Picture</code>.
|
||||
*
|
||||
* Picture must be valid.
|
||||
*/
|
||||
void x_free_picture(struct x_connection *c, xcb_render_picture_t p);
|
||||
void x_clear_picture_clip_region(xcb_connection_t *, xcb_render_picture_t pict);
|
||||
|
||||
/**
|
||||
* Log a X11 error
|
||||
@@ -354,9 +242,10 @@ void x_log_error(enum log_level level, unsigned long serial, uint8_t major,
|
||||
*/
|
||||
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(xcb_connection_t *, uint8_t depth, xcb_drawable_t drawable,
|
||||
int width, int height);
|
||||
|
||||
bool x_validate_pixmap(struct x_connection *, xcb_pixmap_t pxmap);
|
||||
bool x_validate_pixmap(xcb_connection_t *, xcb_pixmap_t pxmap);
|
||||
|
||||
/**
|
||||
* Free a <code>winprop_t</code>.
|
||||
@@ -365,22 +254,22 @@ bool x_validate_pixmap(struct x_connection *, xcb_pixmap_t pxmap);
|
||||
*/
|
||||
static inline void free_winprop(winprop_t *pprop) {
|
||||
// Empty the whole structure to avoid possible issues
|
||||
if (pprop->r) {
|
||||
if (pprop->r)
|
||||
free(pprop->r);
|
||||
}
|
||||
pprop->ptr = NULL;
|
||||
pprop->r = NULL;
|
||||
pprop->nitems = 0;
|
||||
}
|
||||
|
||||
/// Get the back pixmap of the root window
|
||||
xcb_pixmap_t x_get_root_back_pixmap(struct x_connection *c, struct atom *atoms);
|
||||
xcb_pixmap_t
|
||||
x_get_root_back_pixmap(xcb_connection_t *c, xcb_window_t root, struct atom *atoms);
|
||||
|
||||
/// Return true if the atom refers to a property name that is used for the
|
||||
/// root window background pixmap
|
||||
bool x_is_root_back_pixmap_atom(struct atom *atoms, xcb_atom_t atom);
|
||||
|
||||
bool x_fence_sync(struct x_connection *, xcb_sync_fence_t);
|
||||
bool x_fence_sync(xcb_connection_t *, xcb_sync_fence_t);
|
||||
|
||||
struct x_convolution_kernel {
|
||||
int size;
|
||||
@@ -404,26 +293,23 @@ void attr_nonnull(1, 3) x_create_convolution_kernel(const conv *kernel, double c
|
||||
|
||||
/// Generate a search criteria for fbconfig from a X visual.
|
||||
/// Returns {-1, -1, -1, -1, -1, -1} on failure
|
||||
struct xvisual_info x_get_visual_info(struct x_connection *c, xcb_visualid_t visual);
|
||||
struct xvisual_info x_get_visual_info(xcb_connection_t *c, xcb_visualid_t visual);
|
||||
|
||||
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(xcb_connection_t *c, xcb_pict_standard_t std);
|
||||
|
||||
xcb_render_pictformat_t
|
||||
x_get_pictfmt_for_standard(struct x_connection *c, xcb_pict_standard_t std);
|
||||
x_get_pictfmt_for_standard(xcb_connection_t *c, xcb_pict_standard_t std);
|
||||
|
||||
xcb_screen_t *x_screen_of_display(struct x_connection *c, int screen);
|
||||
xcb_screen_t *x_screen_of_display(xcb_connection_t *c, int screen);
|
||||
|
||||
/// Populates a `struct x_monitors` with the current monitor configuration.
|
||||
void x_update_monitors(struct x_connection *, struct x_monitors *);
|
||||
/// Free memory allocated for a `struct x_monitors`.
|
||||
void x_free_monitor_info(struct x_monitors *);
|
||||
/**
|
||||
* X RandR-related functions.
|
||||
*
|
||||
* The x_update_randr_monitors function populates ps->randr_nmonitors and
|
||||
* ps->randr_monitor_regs with the data X RandR provided and the
|
||||
* x_free_randr_info function frees them.
|
||||
*/
|
||||
void x_update_randr_monitors(session_t *ps);
|
||||
void x_free_randr_info(session_t *ps);
|
||||
|
||||
uint32_t attr_deprecated xcb_generate_id(xcb_connection_t *c);
|
||||
|
||||
/// Ask X server to send us a notification for the next end of vblank.
|
||||
void x_request_vblank_event(struct x_connection *c, xcb_window_t window, uint64_t msc);
|
||||
|
||||
/// Update screen_is_off to reflect the current DPMS state.
|
||||
///
|
||||
/// Returns true if the DPMS state was successfully queried, false otherwise.
|
||||
bool x_check_dpms_status(struct x_connection *c, bool *screen_is_off);
|
||||
|
||||
Reference in New Issue
Block a user