Compare commits

..

No commits in common. 'master' and 'v0.4.0' have entirely different histories.

@ -16,11 +16,12 @@ trim_trailing_whitespace = true
[src/overlay*{cpp,h}]
indent_size = 3
[src/{keybinds,vulkan}.{cpp,h}]
indent_size = 3
[src/mesa/**]
indent_size = 3
[meson.build]
indent_size = 2
[Makefile]
indent_style = tab
indent_size = 4

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://www.paypal.me/flightlessmango

@ -1,32 +0,0 @@
---
name: "[Bug report] - Issue"
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**List relevant hardware/software information**
- Linux Distribution
- MangoHud version
- GPU
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.

@ -1,6 +0,0 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

@ -1,42 +0,0 @@
name: arch package
on:
workflow_dispatch:
jobs:
build-arch-pkg:
runs-on: ubuntu-latest
container:
image: archlinux:latest
steps:
- name: set git global safe directory
run: |
pacman -Syu git --noconfirm
git config --global --add safe.directory $(realpath .)
- uses: actions/checkout@v3
- name: Install prerequisites
run: |
echo "ParallelDownloads = 10" >> /etc/pacman.conf
echo "\n" && echo "[multilib]" >> /etc/pacman.conf
echo "Include = /etc/pacman.d/mirrorlist" >> /etc/pacman.conf
pacman -Syu base-devel sudo meson python-mako glslang hub python-numpy python-matplotlib --noconfirm
- name: makepkg
run: |
echo "nobody ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
chown nobody:nobody pkgbuild
cd pkgbuild
pkgver=$(git describe --tags | sed -r 's/^v//;s/([^-]*-g)/r\1/;s/-/./g')
sed -i "s/pkgver=.*/pkgver=$pkgver/g" PKGBUILD
sudo -u nobody -- sh -c "makepkg -fsCc --noconfirm"
- name: Edit release and add files
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -x
assets=()
for pkg in ./pkgbuild/*mangohud*.tar.zst;
do echo $pkg;
assets+=("-a" "$pkg")
done;
tag_name="${GITHUB_REF##*/}"
hub release edit "${assets[@]}" -m "" "$tag_name"

@ -1,59 +0,0 @@
name: Build release package
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v3
- name: Install build tools
run: |
set -x
sudo dpkg --add-architecture i386
curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages focal main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null
sudo apt update
sudo apt -y install gcc-multilib g++-multilib ninja-build python3-setuptools \
python3-wheel mesa-common-dev libxnvctrl-dev libdbus-1-dev \
python3-numpy python3-matplotlib unzip hub libxkbcommon-dev libwayland-dev wget unzip \
libxkbcommon-dev:i386 libwayland-dev:i386 gh
sudo pip3 --no-input install 'meson>=0.60' mako
wget https://github.com/KhronosGroup/glslang/releases/download/SDK-candidate-26-Jul-2020/glslang-master-linux-Release.zip
unzip glslang-master-linux-Release.zip bin/glslangValidator
sudo install -m755 bin/glslangValidator /usr/local/bin/
- name: Prepare Artifact Git Info
shell: bash
run: |
echo "##[set-output name=branch;]${GITHUB_REF#refs/heads/}"
ARTIFACT_NAME="commit-$(git rev-parse --short "$GITHUB_SHA")"
if [ ${{ github.event_name == 'pull_request' }} ]; then
echo "##[set-output name=short-sha;]$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")"
if [ ! -z "${{ github.event.pull_request.number }}" ]; then
ARTIFACT_NAME="pr-${{ github.event.pull_request.number }}-commit-$(git rev-parse --short "${{ github.event.pull_request.head.sha }}")"
fi
else
echo "##[set-output name=short-sha;]$(git rev-parse --short "$GITHUB_SHA")"
fi
echo "##[set-output name=artifact-metadata;]$ARTIFACT_NAME"
- name: Build and package
run: |
./build-source.sh
./build.sh build -Dwerror=true package release
- name: Upload assets to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag_name="${GITHUB_REF##*/}"
for pkg in ./build/*.tar.*; do
gh release upload "$tag_name" "$pkg" --clobber
done
- name: Upload artifact
uses: actions/upload-artifact@v3
continue-on-error: true
with:
name: MangoHud-${{steps.git-vars.outputs.artifact-metadata}}
path: ${{runner.workspace}}/MangoHud/build/MangoHud-*tar.gz
retention-days: 30

@ -1,33 +0,0 @@
name: Build source tars
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run build-source.sh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -x
sudo apt update
sudo apt -y install gcc g++ ninja-build python3-pip python3-setuptools python3-wheel pkg-config mesa-common-dev libx11-dev libxnvctrl-dev libdbus-1-dev glslang-tools hub libxkbcommon-dev libwayland-dev wget unzip
sudo pip3 --no-input install 'meson>=0.60' mako
./build-source.sh
assets=()
for asset in ./MangoHud-*-Source*.tar.*; do
assets+=("-a" "$asset")
done
tag_name="${GITHUB_REF##*/}"
hub release edit "${assets[@]}" -m "" "$tag_name"
#hub release create "${assets[@]}" -m "$tag_name" "$tag_name"
- name: Upload artifact
uses: actions/upload-artifact@v3
continue-on-error: true
with:
name: MangoHud-${{steps.git-vars.outputs.artifact-metadata}}
path: ${{runner.workspace}}/MangoHud/build/MangoHud-*tar.gz
retention-days: 30

@ -1,17 +0,0 @@
name: Mingw build testing
on: [push, pull_request]
jobs:
build-mingw:
runs-on: ubuntu-latest
container:
image: archlinux:latest
steps:
- uses: actions/checkout@v3
- name: Install prerequisites
run: |
pacman -Syu mingw-w64-gcc meson python-mako glslang mingw-w64-headers git --noconfirm
- name: configure
run: meson setup --cross-file mingw64.txt build64
- name: build
run: ninja -C build64

@ -1,23 +0,0 @@
name: param check
on:
push:
paths:
- 'src/overlay_params.h'
- 'README.md'
- 'data/MangoHud.conf'
jobs:
param-check:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11.2' # Replace with the version of Python you want to use
- name: Run Python script
run: |
cd tests
python params.py

@ -1,60 +0,0 @@
name: Ubuntu build testing
on: [push, pull_request]
jobs:
build-test:
strategy:
matrix:
compiler: [clang, gcc]
os: [ubuntu-22.04, ubuntu-20.04]
runs-on: ${{ matrix.os }}
steps:
- name: 'Checkout'
uses: actions/checkout@v3
- name: 'Install prerequisites'
run: |
sudo apt-get update
sudo apt-get install -y \
appstream \
glslang-tools \
ninja-build \
python3-mako \
python3-setuptools \
python3-wheel \
mesa-common-dev \
libcmocka-dev \
libdbus-1-dev \
libglew-dev \
libglfw3-dev \
libwayland-dev \
libxnvctrl-dev \
libxkbcommon-dev
sudo pip3 install 'meson>=0.60'
- name: 'Install clang'
if: ${{ (matrix.compiler == 'clang') }}
run: |
sudo apt-get install -y clang
echo "CC=clang" >> "$GITHUB_ENV"
echo "CXX=clang++" >> "$GITHUB_ENV"
- name: 'Install gcc'
if: ${{ (matrix.compiler == 'gcc') }}
run: |
sudo apt-get install -y gcc g++
echo "CC=gcc" >> "$GITHUB_ENV"
echo "CXX=g++" >> "$GITHUB_ENV"
- name: 'Configure'
run: meson setup ./builddir --prefix=/usr
-D include_doc=true
-D with_xnvctrl=enabled
-D with_x11=enabled
-D with_wayland=enabled
-D with_dbus=enabled
-D mangoapp=true
-D mangohudctl=true
-D mangoapp_layer=true
-D tests=enabled
- name: 'Build'
run: meson compile -C ./builddir || ninja -C ./builddir
- name: 'Install'
run: sudo meson install -C ./builddir

@ -1,70 +0,0 @@
name: Update Meson Version and Recreate Release
on:
release:
types: [published]
jobs:
update-version:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Install GitHub CLI
run: sudo apt-get install -y gh
- name: Extract version from release
id: extract_version
run: echo "version=${{ github.event.release.tag_name }}" >> $GITHUB_ENV
- name: Fetch release description
id: fetch_description
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME=${{ env.version }}
DESCRIPTION=$(gh release view $TAG_NAME --json body -q .body)
echo "description=$DESCRIPTION" >> $GITHUB_ENV
- name: Update meson.build
run: |
VERSION=${{ env.version }}
sed -i "s/^\(\s*version\s*:\s*'\)[^']*'/\1${VERSION}'/" meson.build
- name: Commit changes
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git add meson.build
git commit -m "Update version to ${{ env.version }}"
git push origin HEAD:refs/heads/master
- name: Force-update tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME=${{ env.version }}
git tag -fa $TAG_NAME -m "Update tag to include version update"
git push origin --force $TAG_NAME
- name: Recreate release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME=${{ env.version }}
DESCRIPTION=${{ env.description }}
gh release delete $TAG_NAME --yes
gh release create $TAG_NAME --target $(git rev-parse HEAD) --title "$TAG_NAME" --notes "$DESCRIPTION"
- name: Trigger other workflows
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
TAG_NAME=${{ env.version }}
gh workflow run .github/workflows/build-source.yml --ref $TAG_NAME
gh workflow run .github/workflows/build-package.yml --ref $TAG_NAME

9
.gitignore vendored

@ -2,7 +2,7 @@ build
builddir
__pycache__
.vscode
MangoHud*.tar.*
MangoHud*.tar.gz
pkg
mangohud*.tar.*
lib32-mangohud*.tar.*
@ -38,13 +38,10 @@ lib32-mangohud*.tar.*
# subprojects
subprojects/packagecache/
subprojects/Vulkan-Headers-*/
subprojects/imgui-*/
subprojects/spdlog-*/
subprojects/nlohmann_json-*/
subprojects/implot-*/
subprojects/Vulkan-Headers-*/
#GNU Global Metadata
**/GPATH
**/GRTAGS
**/GTAGS
**/GTAGS

6
.gitmodules vendored

@ -1,3 +1,3 @@
[submodule "modules/minhook"]
path = modules/minhook
url = https://github.com/flightlessmango/minhook
[submodule "modules/ImGui/src"]
path = modules/ImGui/src
url = https://github.com/flightlessmango/imgui.git

@ -0,0 +1,36 @@
pkgname=('mangohud' 'lib32-mangohud')
pkgver=r24.b67a2aa
pkgrel=1
pkgdesc="Vulkan overlay layer to display information about the application"
arch=('x86_64')
makedepends=('gcc' 'meson' 'python-mako' 'libx11' 'lib32-libx11' 'git')
depends=('glslang' 'libglvnd' 'lib32-libglvnd' 'vulkan-headers')
replaces=('vulkan-mesa-layer-mango' 'lib32-vulkan-mesa-layer-mango')
url="https://github.com/flightlessmango/MangoHud"
pkgver() {
cd $startdir
printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
}
prepare() {
git submodule update --init --depth 50
}
build() {
cd $startdir
# ./build.sh clean
./build.sh build
}
package_mangohud() {
provides=("mangohud=${pkgver}")
cd $startdir
install -Dm664 "build/release/usr/lib64/libMangoHud.so" "${pkgdir}/usr/lib/libMangoHud.so"
install -Dm664 "build/release/usr/share/vulkan/implicit_layer.d/mangohud.json" "${pkgdir}/usr/share/vulkan/implicit_layer.d/mangohud.json"
}
package_lib32-mangohud() {
provides=("lib32-mangohud=${pkgver}")
cd $startdir
install -Dm664 "build/release/usr/lib32/libMangoHud.so" "${pkgdir}/usr/lib32/libMangoHud.so"
}

@ -2,38 +2,12 @@
A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more.
![Example gif showing a standard performance readout with frametimes](assets/overlay_example.gif)
#### Example:
![](assets/overlay_example.gif)
---
- [MangoHud](#mangohud)
- [Installation - Build From Source](#installation---build-from-source)
- [Dependencies](#dependencies)
- [Building with build script](#building-with-build-script)
- [Installation - Pre-packaged Binaries](#installation---pre-packaged-binaries)
- [GitHub releases](#github-releases)
- [Arch-based distributions](#arch-based-distributions)
- [Debian, Ubuntu](#debian-ubuntu)
- [Fedora](#fedora)
- [Solus](#solus)
- [openSUSE](#opensuse)
- [Flatpak](#flatpak)
- [Normal usage](#normal-usage)
- [OpenGL](#opengl)
- [Hud configuration](#hud-configuration)
- [Environment Variables: **`MANGOHUD_CONFIG`**, **`MANGOHUD_CONFIGFILE`**, and **`MANGOHUD_PRESETSFILE`**](#environment-variables)
- [Vsync](#vsync)
- [OpenGL Vsync](#opengl-vsync)
- [Vulkan Vsync](#vulkan-vsync)
- [Keybindings](#keybindings)
- [Workarounds](#workarounds)
- [FPS logging](#fps-logging)
- [Online visualization: FlightlessMango.com](#online-visualization-flightlessmangocom)
- [Local visualization: `mangoplot`](#local-visualization-mangoplot)
## Installation - Build From Source
# Installation
---
## Build
If you wish to compile MangoHud to keep up to date with any changes - first clone this repository and cd into it:
@ -42,66 +16,20 @@ git clone --recurse-submodules https://github.com/flightlessmango/MangoHud.git
cd MangoHud
```
Using `meson` to install "manually":
```
meson build
ninja -C build install
```
By default, meson should install MangoHud to `/usr/local`. Specify install prefix with `--prefix=/usr` if desired.
Add `-Dappend_libdir_mangohud=false` option to meson to not append `mangohud` to libdir if desired (e.g. /usr/local/lib/mangohud).
To install 32-bit build on 64-bit distro, specify proper `libdir`: `lib32` for Arch, `lib/i386-linux-gnu` on Debian-based distros. RPM-based distros usually install 32-bit libraries to `/usr/lib` and 64-bit to `/usr/lib64`.
You may have to change `PKG_CONFIG_PATH` to point to correct folders for your distro.
```
CC="gcc -m32" \
CXX="g++ -m32" \
PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig" \
meson build32 --libdir lib32
ninja -C build32 install
```
### Dependencies
Install necessary development packages.
- gcc, g++
- or gcc-multilib, g++-multilib for 32-bit support
- meson >=0.54
- ninja (ninja-build)
- glslang
- libGL/libEGL (libglvnd, mesa-common-dev, mesa-libGL-devel etc)
- X11 (libx11-dev)
- XNVCtrl (libxnvctrl-dev), optional, use `-Dwith_xnvctrl=disabled` option with `meson` to disable
- D-Bus (libdbus-1-dev), optional, use `-Dwith_dbus=disabled` option with `meson` to disable
- wayland-client
- xcbcommon
Python 3 libraries:
- Mako (python3-mako or install with `pip`)
If distro's packaged `meson` is too old and gives build errors, install newer version with `pip` (`python3-pip`).
### Building with build script
You can also use `build.sh` script to do some things automatically like install dependencies, if distro is supported but it usually assumes you are running on x86_64 architecture.
To just build it, execute:
To build it, execute:
```
./build.sh build
./build.sh package
```
You can also pass arguments to meson:
**NOTE: If you are running an Ubuntu-based, Arch-based, Fedora-based, or openSUSE-based distro, the build script will automatically detect and prompt you to install missing build dependencies. If you run into any issues with this please report them!**
```
./build.sh build -Dwith_xnvctrl=disabled
```
Once done, proceed to the [installation](#source).
## Install
Resulting files will be install to `./build/release` folder.
### Source
If you have compiled MangoHud from source, to install it, execute:
@ -109,84 +37,17 @@ If you have compiled MangoHud from source, to install it, execute:
./build.sh install
```
You can then subsequently uninstall MangoHud via the following command
```
./build.sh uninstall
```
To tar up the resulting binaries into a package and create a release tar with installer script, execute:
```
./build.sh package release
```
or combine the commands, although `package` should also call `build` if it doesn't find the built libs:
```
./build.sh build package release
```
If you have built MangoHud before and suddenly it fails, you can try cleaning the `build` folder, execute:
```
./build.sh clean
```
Currently it just does `rm -fr build` and clears subprojects.
__NOTE: If you are running an Ubuntu-based, Arch-based, Fedora-based, or openSUSE-based distro, the build script will automatically detect and prompt you to install missing build dependencies. If you run into any issues with this please report them!__
## Installation - Pre-packaged Binaries
---
### GitHub releases
If you do not wish to compile anything, simply download the file under [Releases](https://github.com/flightlessmango/MangoHud/releases), extract it, and from within the extracted folder in terminal, execute:
```
./mangohud-setup.sh install
```
### Arch-based distributions
If you are using an Arch-based distribution, install [`mangohud`](https://archlinux.org/packages/extra/x86_64/mangohud/) and [`lib32-mangohud`](https://archlinux.org/packages/multilib/x86_64/lib32-mangohud/) from the `extra`/`multilib` repository. [`mangohud-git`](https://aur.archlinux.org/packages/mangohud-git/) and [`lib32-mangohud-git`](https://aur.archlinux.org/packages/lib32-mangohud-git/) are available on the AUR to be installed via your favourite AUR helper. These can help fix issues with the hud not activating when using stable releases from pacman!
If you are building it by yourself, you need to enable multilib repository, by editing pacman config:
```
sudo nano /etc/pacman.conf
```
and uncomment:
```txt
#[multilib]
#Include = /etc/pacman.d/mirrorlist
```
then save the file and execute:
```
sudo pacman -Syy
```
### Debian, Ubuntu
### Pre-packaged binaries
If you are using Debian 11 (Bullseye) or later, Ubuntu 21.10 (Impish) or later, or distro derived from them, to install the [MangoHud](https://tracker.debian.org/pkg/mangohud) package, execute:
#### GitHub releases
```
sudo apt install mangohud
```
If you do not wish to compile anything, simply download the file under [Releases](https://github.com/flightlessmango/MangoHud/releases), extract it, and run `./mangohud-setup.sh install` from within the extracted folder.
Optionally, if you also need MangoHud for 32-bit applications, execute:
#### Arch-based distributions
```
sudo apt install mangohud:i386
```
If you are using an Arch-based distribution, install [`mangohud`](https://aur.archlinux.org/packages/mangohud/) and [`lib32-mangohud`](https://aur.archlinux.org/packages/lib32-mangohud/) with your favourite AUR helper. [`mangohud-git`](https://aur.archlinux.org/packages/mangohud-git/) and [`lib32-mangohud-git`](https://aur.archlinux.org/packages/lib32-mangohud-git/) are also available on the AUR if you want the up-to-date version of MangoHud.
### Fedora
#### Fedora
If you are using Fedora, to install the [MangoHud](https://src.fedoraproject.org/rpms/mangohud) package, execute:
@ -194,64 +55,29 @@ If you are using Fedora, to install the [MangoHud](https://src.fedoraproject.org
sudo dnf install mangohud
```
### Solus
If you are using Solus, to install [MangoHud](https://dev.getsol.us/source/mangohud/) simply execute:
```
sudo eopkg it mangohud
```
### openSUSE
If you run openSUSE Leap or Tumbleweed you can get Mangohud from the official repositories.
There are two packages, [mangohud](https://software.opensuse.org/package/mangohud) for 64bit and [mangohud-32bit](https://software.opensuse.org/package/mangohud-32bit) for 32bit application support.
To have Mangohud working for both 32bit and 64bit applications you need to install both packages even on a 64bit operating system.
```
sudo zypper in mangohud mangohud-32bit
```
Leap doesn't seem to have the 32bit package.
Leap 15.2
```
sudo zypper addrepo -f https://download.opensuse.org/repositories/games:tools/openSUSE_Leap_15.2/games:tools.repo
sudo zypper install mangohud
```
Leap 15.3
```
sudo zypper addrepo -f https://download.opensuse.org/repositories/games:tools/openSUSE_Leap_15.3/games:tools.repo
sudo zypper install mangohud
```
### Flatpak
#### Flatpak
If you are using Flatpaks, you will have to add the [Flathub repository](https://flatpak.org/setup/) for your specific distribution, and then, to install it, execute:
For flatpak:
##### For Steam flatpak
```
flatpak install org.freedesktop.Platform.VulkanLayer.MangoHud
flatpak install com.valvesoftware.Steam.Utility.MangoHud
```
To enable MangoHud for all Steam games:
```
flatpak override --user --env=MANGOHUD=1 com.valvesoftware.Steam
```
## Normal usage
# Normal usage
---
To enable the MangoHud overlay layer for Vulkan and OpenGL, run :
To enable the MangoHud overlay layer for 64bit Vulkan and OpenGL, run :
`mangohud /path/to/app`
Or
`mangohud.x86 /path/to/app` for 32bit OpenGL
For Lutris games, go to the System options in Lutris (make sure that advanced options are enabled) and add this to the `Command prefix` setting:
`mangohud`
@ -264,33 +90,18 @@ Or alternatively, add `MANGOHUD=1` to your shell profile (Vulkan only).
## OpenGL
OpenGL games may also need `dlsym` hooking. Add `--dlsym` to your command like `mangohud --dlsym %command%` for Steam.
OpenGL games may also need `dlsym` hooking. Add `MANGOHUD_DLSYM=1` to your command like `MANGOHUD_DLSYM=1 mangohud %command%` for Steam.
Some Linux native OpenGL games overrides LD_PRELOAD and stops MangoHud from working. You can sometimes fix this by editing LD_PRELOAD in the start script
`LD_PRELOAD=/path/to/mangohud/lib/`
## gamescope
To enable mangohud with gamescope you need to install mangoapp.
`gamescope --mangoapp %command%`
Using normal mangohud with gamescope is not support.
## Hud configuration
MangoHud comes with a config file which can be used to set configuration options globally or per application. Usually it is installed as `/usr/share/doc/mangohud/MangoHud.conf.example` or [get a copy from here](https://raw.githubusercontent.com/flightlessmango/MangoHud/master/data/MangoHud.conf).
The priorities of different config files are:
MangoHud comes with a config file which can be used to set configuration options globally or per application. The priorities of different config files are:
1. `/path/to/application/dir/MangoHud.conf`
2. Per-application configuration in ~/.config/MangoHud:
1. `~/.config/MangoHud/<application_name>.conf` for native applications, where `<application_name>` is the case sensitive name of the executable
2. `~/.config/MangoHud/wine-<application_name>.conf` for wine/proton apps, where `<application_name>` is the case sensitive name of the executable without the `.exe` ending
3. `~/.config/MangoHud/MangoHud.conf`
Example: For Overwatch, this would be `wine-Overwatch.conf` (even though the executable you run from Lutris is `Battle.net.exe`, the actual game executable name is `Overwatch.exe`).
If you start the game from the terminal with MangoHud enabled (for example by starting Lutris from the terminal), MangoHud will print the config file names it is looking for.
2. `$HOME/.config/MangoHud/{application_name}.conf`
3. `$HOME/.config/MangoHud/MangoHud.conf`
You can find an example config in /usr/share/doc/mangohud
@ -298,210 +109,88 @@ You can find an example config in /usr/share/doc/mangohud
---
### Environment Variables
### `MANGOHUD_CONFIG` and `MANGOHUD_CONFIGFILE` environment variables
You can also customize the hud by using the `MANGOHUD_CONFIG` environment variable while separating different options with a comma. This takes priority over any config file.
You can also specify configuration file with `MANGOHUD_CONFIGFILE=/path/to/config` for applications whose names are hard to guess (java, python etc).
You can also specify presets file with `MANGOHUD_PRESETSFILE=/path/to/config`. This is especially useful when running mangohud in a sandbox such as flatpak.
A partial list of parameters are below. See the config file for a complete list.
Parameters that are enabled by default have to be explicitly disabled. These (currently) are `fps`, `frame_timing`, `cpu_stats` (cpu load), `gpu_stats` (gpu load), and each can be disabled by setting the corresponding variable to 0 (e.g., fps=0).
| Variable | Description |
|------------------------------------|---------------------------------------------------------------------------------------|
| `af` | Anisotropic filtering level. Improves sharpness of textures viewed at an angle `0`-`16` |
| `alpha` | Set the opacity of all text and frametime graph `0.0`-`1.0` |
| `arch` | Show if the application is 32- or 64-bit |
| `autostart_log=` | Starts the log after X seconds from mangohud init |
| `background_alpha` | Set the opacity of the background `0.0`-`1.0` |
| `battery_color` | Change the battery text color |
| `battery_icon` | Display battery icon instead of percent |
| `battery_watt` | Display wattage for the battery option |
| `battery_time` | Display remaining time for battery option |
| `battery` | Display current battery percent and energy consumption |
| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97,AVG,1,0.1` |
| `bicubic` | Force bicubic filtering |
| `blacklist` | Add a program to the blacklist. e.g `blacklist=vkcube,WatchDogs2.exe` |
| `cellpadding_y` | Set the vertical cellpadding, default is `-0.085` |
| `control=` | Sets up a unix socket with a specific name that can be connected to with mangohud-control.<br>I.e. `control=mangohud` or `control=mangohud-%p` (`%p` will be replaced by process id) |
| `core_load_change` | Change the colors of cpu core loads, uses the same data from `cpu_load_value` and `cpu_load_change` |
| `core_load` | Display load & frequency per core |
| `core_bars` | Change the display of `core_load` from numbers to vertical bars |
| `cpu_load_change` | Change the color of the CPU load depending on load |
| `cpu_load_color` | Set the colors for the gpu load change low, medium and high. e.g `cpu_load_color=0000FF,00FFFF,FF00FF` |
| `cpu_load_value` | Set the values for medium and high load e.g `cpu_load_value=50,90` |
| `cpu_mhz` | Show the CPUs current MHz |
| `cpu_power`<br>`gpu_power` | Display CPU/GPU draw in watts |
| `cpu_temp`<br>`gpu_temp`<br>`gpu_junction_temp`<br>`gpu_mem_temp` | Display current CPU/GPU temperature |
| `cpu_text`<br>`gpu_text` | Override CPU and GPU text |
| `custom_text_center` | Display a custom text centered useful for a header e.g `custom_text_center=FlightLessMango Benchmarks` |
| `custom_text` | Display a custom text e.g `custom_text=Fsync enabled` |
| `debug` | Shows the graph of gamescope app frametimes and latency (only on gamescope obviously) |
| `device_battery_icon` | Display wirless device battery icon. |
| `device_battery` | Display wireless device battery percent. Currently supported arguments `gamepad` and `mouse` e.g `device_battery=gamepad,mouse` |
| `dynamic_frame_timing` | This changes frame_timing y-axis to correspond with the current maximum and minimum frametime instead of being a static 0-50 |
| `engine_short_names` | Display a short version of the used engine (e.g. `OGL` instead of `OpenGL`) |
| `engine_version` | Display OpenGL or vulkan and vulkan-based render engine's version |
| `exec` | Display output of bash command in next column, e.g `custom_text=/home` , `exec=df -h /home \| tail -n 1`. Only works with `legacy_layout=0` |
| `exec_name` | Display current exec name |
| `fan` | Shows the Steam Deck fan rpm |
| `fcat` | Enables frame capture analysis |
| `fcat_overlay_width=` | Sets the width of fcat. Default is `24` |
| `fcat_screen_edge=` | Decides the edge fcat is displayed on. A value between `1` and `4` |
| `font_file_text` | Change text font. Otherwise `font_file` is used |
| `font_file` | Change default font (set location to .TTF/.OTF file) |
| `font_glyph_ranges` | Specify extra font glyph ranges, comma separated: `korean`, `chinese`, `chinese_simplified`, `japanese`, `cyrillic`, `thai`, `vietnamese`, `latin_ext_a`, `latin_ext_b`. If you experience crashes or text is just squares, reduce font size or glyph ranges |
| `font_scale=` | Set global font scale. Default is `1.0` |
| `font_scale_media_player` | Change size of media player text relative to `font_size` |
| `font_size=` | Customizeable font size. Default is `24` |
| `font_size_text=` | Customizeable font size for other text like media metadata. Default is `24` |
| `fps_color_change` | Change the FPS text color depepending on the FPS value |
| `fps_color=` | Choose the colors that the fps changes to when `fps_color_change` is enabled. Corresponds with fps_value. Default is `b22222,fdfd09,39f900` |
| `fps_limit_method` | If FPS limiter should wait before or after presenting a frame. Choose `late` (default) for the lowest latency or `early` for the smoothest frametimes |
| `fps_limit` | Limit the apps framerate. Comma-separated list of one or more FPS values. `0` means unlimited |
| `fps_only` | Show FPS only. ***Not meant to be used with other display params*** |
| `fps_sampling_period=` | Time interval between two sampling points for gathering the FPS in milliseconds. Default is `500` |
| `fps_value` | Choose the break points where `fps_color_change` changes colors between. E.g `60,144`, default is `30,60` |
| `fps_metrics` | Takes a list of decimal values or the value avg, e.g `avg,0.001` |
| `frame_count` | Display frame count |
| `frametime` | Display frametime next to FPS text |
| `fsr` | Display the status of FSR (only works in gamescope) |
| `hdr` | Display the status of HDR (only works in gamescope) |
| `refresh_rate` | Display the current refresh rate (only works in gamescope) |
| `full` | Enable most of the toggleable parameters (currently excludes `histogram`) |
| `gamemode` | Show if GameMode is on |
| `gpu_color`<br>`cpu_color`<br>`vram_color`<br>`ram_color`<br>`io_color`<br>`engine_color`<br>`frametime_color`<br>`background_color`<br>`text_color`<br>`media_player_color` | Change default colors: `gpu_color=RRGGBB` |
| `gpu_core_clock`<br>`gpu_mem_clock`| Display GPU core/memory frequency |
| `gpu_fan` | GPU fan in rpm on AMD, FAN in percent on NVIDIA |
| `gpu_load_change` | Change the color of the GPU load depending on load |
| `gpu_load_color` | Set the colors for the gpu load change low,medium and high. e.g `gpu_load_color=0000FF,00FFFF,FF00FF` |
| `gpu_load_value` | Set the values for medium and high load e.g `gpu_load_value=50,90` |
| `gpu_name` | Display GPU name from pci.ids |
| `gpu_voltage` | Display GPU voltage (only works on AMD GPUs) |
| `hide_fsr_sharpness` | Hides the sharpness info for the `fsr` option (only available in gamescope) |
| `histogram` | Change FPS graph to histogram |
| `horizontal` | Display Mangohud in a horizontal position |
| `horizontal_stretch` | Stretches the background to the screens width in `horizontal` mode |
| `hud_compact` | Display compact version of MangoHud |
| `hud_no_margin` | Remove margins around MangoHud |
| `io_read`<br> `io_write` | Show non-cached IO read/write, in MiB/s |
| `cpu_temp`<br>`gpu_temp` | Displays current CPU/GPU temperature |
| `core_load` | Displays load & frequency per core |
| `gpu_core_clock`<br>`gpu_mem_clock`| Displays GPU core/memory frequency |
| `ram`<br>`vram` | Displays system RAM/VRAM usage |
| `full` | Enables all of the above config options |
| `font_size=` | Customizeable font size (default=24) |
| `width=`<br>`height=` | Customizeable hud dimensions (in pixels) |
| `position=` | Location of the hud: `top-left` (default), `top-right`, `bottom-left`, `bottom-right`, `top-center` |
| `offset_x` `offset_y` | Hud position offsets |
| `no_display` | Hide the hud by default |
| `toggle_hud=`<br>`toggle_logging=` | Modifiable toggle hotkeys. Default are `Shift_R+F12` and `Shift_L+F2`, respectively. |
| `reload_cfg=` | Change keybind for reloading the config. Default = `Shift_L+F4` |
| `time`<br>`time_format=%T` | Displays local time. See [std::put_time](https://en.cppreference.com/w/cpp/io/manip/put_time) for formatting help. NOTE: Sometimes apps (or AMDVLK) may set `TZ` environment variable to `GMT+0` |
| `gpu_color`<br>`gpu_color`<br>`vram_color`<br>`ram_color`<br>`io_color`<br>`engine_color`<br>`frametime_color`<br>`background_color`<br>`text_color`<br>`media_player_color` | Change default colors: `gpu_color=RRGGBB`|
| `alpha` | Set the opacity of all text and frametime graph `0.0-1.0` |
| `background_alpha` | Set the opacity of the background `0.0-1.0` |
| `read_cfg` | Add to MANGOHUD_CONFIG as first parameter to also load config file. Otherwise only MANGOHUD_CONFIG parameters are used. |
| `output_file` | Define name and location of the output file (Required for logging) |
| `font_file` | Change default font (set location to .TTF/.OTF file ) |
| `log_duration` | Set amount of time the logging will run for (in seconds) |
| `log_interval` | Change the default log interval in milliseconds. Default is `0` |
| `log_versioning` | Adds more headers and information such as versioning to the log. This format is not supported on flightlessmango.com (yet) |
| `media_player_format` | Format media player metadata. Add extra text etc. Semi-colon breaks to new line. Defaults to `{title};{artist};{album}` |
| `media_player_name` | Force media player DBus service name without the `org.mpris.MediaPlayer2` part, like `spotify`, `vlc`, `audacious` or `cantata`. If none is set, MangoHud tries to switch between currently playing players |
| `vsync`<br> `gl_vsync` | Set vsync for OpenGL or Vulkan |
| `media_player` | Show media player metadata |
| `no_display` | Hide the HUD by default |
| `no_small_font` | Use primary font size for smaller text like units |
| `offset_x` `offset_y` | HUD position offsets |
| `output_file` | Set location and name of the log file |
| `output_folder` | Set location of the output files (Required for logging) |
| `media_player_name` | Set main media player DBus service name without the `org.mpris.MediaPlayer2` part, like `spotify`, `vlc`, `audacious` or `cantata`. Defaults to `spotify`. |
| `font_scale_media_player` | Change size of media player text relative to font_size |
| `io_read`<br> `io_write` | Show non-cached IO read/write, in MiB/s |
| `pci_dev` | Select GPU device in multi-gpu setups |
| `permit_upload` | Allow uploading of logs to Flightlessmango.com |
| `picmip` | Mip-map LoD bias. Negative values will increase texture sharpness (and aliasing). Positive values will increase texture blurriness `-16`-`16` |
| `position=` | Location of the HUD: `top-left` (default), `top-right`, `middle-left`, `middle-right`, `bottom-left`, `bottom-right`, `top-center`, `bottom-center` |
| `preset=` | Comma separated list of one or more presets. Default is `-1,0,1,2,3,4`. Available presets:<br>`0` (No Hud)<br> `1` (FPS Only)<br> `2` (Horizontal)<br> `3` (Extended)<br> `4` (Detailed)<br>User defined presets can be created by using a [presets.conf](data/presets.conf) file in `~/.config/MangoHud/`. |
| `procmem`<br>`procmem_shared`, `procmem_virt`| Displays process' memory usage: resident, shared and/or virtual. `procmem` (resident) also toggles others off if disabled |
| `ram`<br>`vram` | Display system RAM/VRAM usage |
| `read_cfg` | Add to MANGOHUD_CONFIG as first parameter to also load config file. Otherwise only `MANGOHUD_CONFIG` parameters are used |
| `reload_cfg=` | Change keybind for reloading the config. Default = `Shift_L+F4` |
| `resolution` | Display the current resolution |
| `retro` | Disable linear texture filtering. Makes textures look blocky |
| `round_corners` | Change the amount of roundness of the corners have e.g `round_corners=10.0` |
| `show_fps_limit` | Display the current FPS limit |
| `swap` | Display swap space usage next to system RAM usage |
| `table_columns` | Set the number of table columns for ImGui, defaults to 3 |
| `temp_fahrenheit` | Show temperature in Fahrenheit |
| `text_outline` | Draw an outline around text for better readability. Enabled by default. |
| `text_outline_color=` | Set the color of `text_outline`. Default = `000000` |
| `text_outline_thickness=` | Set the thickness of `text_outline`. Default = `1.5` |
| `throttling_status` | Show if GPU is throttling based on Power, current, temp or "other" (Only shows if throttling is currently happening). Currently disabled by default for Nvidia as it causes lag on 3000 series |
| `throttling_status_graph` | Same as `throttling_status` but displays throttling in the frametime graph and only power and temp throttling |
| `time`<br>`time_format=%T` | Display local time. See [std::put_time](https://en.cppreference.com/w/cpp/io/manip/put_time) for formatting help. NOTE: Sometimes apps may set `TZ` (timezone) environment variable to UTC/GMT |
| `time_no_label` | Remove the label before time |
| `toggle_fps_limit` | Cycle between FPS limits (needs at least two values set with `fps_limit`). Defaults to `Shift_L+F1` |
| `toggle_preset` | Cycle between Presets. Defaults to `Shift_R+F10` |
| `toggle_hud=`<br>`toggle_logging=` | Modifiable toggle hotkeys. Default are `Shift_R+F12` and `Shift_L+F2`, respectively |
| `toggle_hud_position` | Toggle MangoHud postion. Default is `R_Shift+F11` |
| `trilinear` | Force trilinear filtering |
| `upload_log` | Change keybind for uploading log |
| `upload_logs` | Enables automatic uploads of logs to flightlessmango.com |
| `version` | Show current MangoHud version |
| `vkbasalt` | Show if vkBasalt is on |
| `vsync`<br> `gl_vsync` | Set Vsync for OpenGL or Vulkan |
| `vulkan_driver` | Display used Vulkan driver (radv/amdgpu-pro/amdvlk) |
| `width=`<br>`height=` | Customizeable HUD dimensions (in pixels) |
| `wine_color` | Change color of the wine/proton text |
| `wine` | Show current Wine or Proton version in use |
| `winesync` | Show wine sync method in use |
| `present_mode` | Shows current vulkan [present mode](https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPresentModeKHR.html) or vsync status in opengl |
| `network` | Show network interfaces tx and rx kb/s. You can specify interface with `network=eth0` |
| `version` | Shows current mangohud version |
| `fps_limit` | Limit the apps framerate |
| `arch` | Show if the application is 32 or 64 bit |
| `histogram` | Change fps graph to histogram |
| `cpu_text`<br>`gpu_text` | Override CPU and GPU text |
| `log_interval` | Change the default log interval, `100` is default |
| `vulkan_driver` | Displays used vulkan driver, radv/amdgpu-pro/amdvlk |
| `gpu_name` | Displays GPU name from pci.ids |
| `gpu_power` | Display GPU draw in watts |
Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32`
Because comma is also used as option delimiter and needs to be escaped for values with a backslash, you can use `+` like `MANGOHUD_CONFIG=fps_limit=60+30+0` instead.
*Note: Width and Height are set automatically based on the font_size, but can be overridden.*
*Note: RAPL is currently used for Intel CPUs to show power draw with `cpu_power` which may be unreadable for non-root users due to [vulnerability](https://platypusattack.com/). The corresponding `energy_uj` file has to be readable by corresponding user, e.g. by running `chmod o+r /sys/class/powercap/intel-rapl\:0/energy_uj` as root, else the power shown will be **0 W**, though having the file readable may potentially be a security vulnerability persisting until system reboots.*
*Note: The [zenpower3](https://git.exozy.me/a/zenpower3) or [zenergy](https://github.com/boukehaarsma23/zenergy) kernel driver must be installed to show the power draw of Ryzen CPUs.*
Note: Width and Height are set automatically based on the font_size, but can be overridden.
## Vsync
### OpenGL Vsync
- `-1` = Adaptive sync
- `0` = Off
- `1` = On
- `n` = Sync to refresh rate / n.
### Vulkan Vsync
- `0` = Adaptive VSync (FIFO_RELAXED_KHR)
- `1` = Off (IMMEDIATE_KHR)
- `2` = Mailbox (VSync with uncapped FPS) (MAILBOX_KHR)
- `3` = On (FIFO_KHR)
Not all vulkan vsync options may be supported on your device, you can check what your device supports here [vulkan.gpuinfo.org](https://vulkan.gpuinfo.org/listsurfacepresentmodes.php?platform=linux)
All vulkan vsync options might not be supported on your device, you can check what your device supports here [vulkan.gpuinfo.org](https://vulkan.gpuinfo.org/listsurfacepresentmodes.php?platform=linux)
## Keybindings
- `Shift_L+F2` : Toggle Logging
- `Shift_L+F4` : Reload Config
- `Shift_R+F12` : Toggle Hud
## Workarounds
Options starting with "gl_*" are for OpenGL.
- `gl_size_query = viewport` : Specify what to use for getting display size. Options are "viewport", "scissorbox" or disabled. Defaults to using glXQueryDrawable.
- `gl_bind_framebuffer = 0..N` : (Re)bind given framebuffer before MangoHud gets drawn. Helps with Crusader Kings III.
- `gl_dont_flip = 1` : Don't swap origin if using GL_UPPER_LEFT. Helps with Ryujinx.
## FPS logging
You must set a valid path for `output_folder` in your configuration to store logs in.
When you toggle logging (default keybind is `Shift_L+F2`), a file is created with the game name plus a date & timestamp in your `output_folder`.
Log files can be visualized with two different tools: online and locally.
- `Shift_R+F12`: Toggle Hud
### Online visualization: FlightlessMango.com
Log files can be (batch) uploaded to [FlightlessMango.com](https://flightlessmango.com/games/user_benchmarks), which will then take care of creating a frametime graph and a summary with 1% min / average framerate / 97th percentile in a table form and a horizontal bar chart form.
## MangoHud FPS logging
Notes:
- Uploaded benchmarks are public: you can share them with anyone by simply giving them the link.
- Benchmark filenames are used as legend in the produced tables and graphs, they can be renamed after the upload.
When you toggle logging (using the keybind `Shift_L+F2`), a file is created with your chosen name (using `output_file`) plus a date & timestamp.
![Gif illustrating the log uploading process](assets/log_upload_example.gif)
This file can be uploaded to [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks) to create graphs automatically.
you can share the created page with others, just link it.
### Local visualization: `mangoplot`
`mangoplot` is a plotting script that is shipped with `MangoHud`: on a given folder, it takes each log file, makes a 1D heatmap of its framerates, then stacks the heats maps vertically to form a 2D graph for easy visual comparison between benchmarks.
#### Multiple log files
Example output:
It's possible to upload multiple files when using [Flightlessmango.com](https://flightlessmango.com/games/user_benchmarks). You can rename them to your preferred names and upload them in a batch.
These filenames will be used as the legend in the graph.
![Overwatch 2 windows 11 vs linux](assets/Overwatch2-w11-vs-linux.svg)
#### Log uploading walkthrough
<sub><sup>Overwatch 2, 5950X + 5700XT, low graphics preset, FHD, 50% render scale</sup></sub>
![](assets/log_upload_example.gif)

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 56 KiB

@ -0,0 +1,117 @@
### MangoHud configuration file
### Uncomment any options you wish to enable. Default options are left uncommented
### Use some_parameter=0 to disable a parameter (only works with on/off parameters)
### Everything below can be used / overridden with the environment variable MANGOHUD_CONFIG instead
################ PERFORMANCE #################
### Limit the application FPS
# fps_limit=
### VSYNC [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on
# vsync=
### OpenGL VSYNC [0-N] 0 = off; >=1 = wait for N v-blanks, N > 1 acts as a fps limiter (fps = display refresh rate / N)
# gl_vsync=
################### VISUAL ###################
### Display the current CPU information
cpu_stats
# cpu_temp
### Display the current GPU information
gpu_stats
# gpu_temp
# gpu_core_clock
# gpu_mem_clock
### Display loaded MangoHud architecture
# arch
### Display the frametime line graph
frame_timing
#histogram
### Display the current system time
# time
### Time formatting examples
# time_format = %H:%M
# time_format = [ %T %F ]
# time_format = %X # locally formatted time, because of limited glyph range, missing characters may show as '?' (e.g. japanese)
### Change the hud font size (default is 24)
font_size=24
# font_scale_media_player = 0.55
### Change the hud position (default is top-left)
position=top-left
### Display the current CPU load & frequency for each core
# core_load
### IO read and write for the app (not system)
# io_read
# io_write
### Display system ram / vram usage
# ram
# vram
### Disable / hide the hud by deafult
# no_display
### Hud position offset
# offset_x=
# offset_y=
### Hud dimensions
# width=
# height=
### Hud transparency / alpha
background_alpha=0.5
# alpha=
### Color customization
# text_color=FFFFFF
# gpu_color=2E9762
# cpu_color=2E97CB
# vram_color=AD64C1
# ram_color=C26693
# engine_color=EB5B5B
# io_color=A491D3
# frametime_color=00FF00
# background_color=020202
# media_player_color=FFFFFF
### Change default font (set location to .TTF/.OTF file )
# font_file
### Crosshair overlay (default size is 30)
# crosshair
# crosshair_size=
# crosshair_color=RRGGBB
### Show media player metadata
# media_player
# media_player_name = spotify
### Specify gpu with pci bus id for amdgpu and NVML stats.
### Set to 'domain:bus:slot.function'
# pci_dev = 0:0a:0.0
################## INTERACTION #################
### Change toggle keybinds for the hud & logging
#toggle_hud=Shift_R+F12
#toggle_logging=F2
#reload_cfg=Shift_L+F4
################## LOG #################
### Set amount of time in second that the logging will run for
# log_duration
### Define name and location of the output file (Required for logging)
# output_file

@ -25,7 +25,7 @@ from __future__ import print_function
import argparse
import os
import textwrap
import xml.etree.ElementTree as et
import xml.etree.cElementTree as et
from mako.template import Template

@ -1,20 +1,11 @@
#!/usr/bin/env bash
OS_RELEASE_FILES=("/etc/os-release" "/usr/lib/os-release")
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
MANGOHUD_CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
SU_CMD=$(command -v sudo || command -v doas || echo)
SU_CMD=$(command -v sudo || command -v doas)
# doas requires a double dash if the command it runs will include any dashes,
# so append a double dash to the command
[[ $SU_CMD == *doas ]] && SU_CMD="$SU_CMD -- "
# Correctly identify the os-release file.
for os_release in ${OS_RELEASE_FILES[@]} ; do
if [[ ! -e "${os_release}" ]]; then
continue
fi
DISTRO=$(sed -rn 's/^ID(_LIKE)*=(.+)/\2/p' ${os_release} | sed 's/"//g')
done
mangohud_usage() {
echo 'Accepted arguments: "install", "uninstall".'
@ -29,19 +20,6 @@ mangohud_config() {
echo
}
mangohud_uninstall() {
[ "$UID" -eq 0 ] || exec $SU_CMD bash "$0" uninstall
rm -rfv "/usr/lib/mangohud"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.json"
rm -frv "/usr/share/doc/mangohud"
rm -fv "/usr/share/man/man1/mangohud.1"
rm -fv "/usr/bin/mangohud"
rm -fv "/usr/bin/mangoplot"
rm -fv "/usr/bin/mangohud.x86"
}
mangohud_install() {
rm -rf "$HOME/.local/share/MangoHud/"
rm -f "$HOME/.local/share/vulkan/implicit_layer.d/"{mangohud32.json,mangohud64.json}
@ -50,66 +28,30 @@ mangohud_install() {
[ "$UID" -eq 0 ] || tar xf MangoHud-package.tar
[ "$UID" -eq 0 ] || exec $SU_CMD bash "$0" install
mangohud_uninstall
DEFAULTLIB=lib32
for i in $DISTRO; do
case $i in
*arch*)
DEFAULTLIB=lib64
;;
esac
done
echo DEFAULTLIB: $DEFAULTLIB
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib64/libMangoHud_opengl.so /usr/lib/mangohud/lib64/libMangoHud_opengl.so
/usr/bin/install -Dvm644 ./usr/lib/mangohud/lib32/libMangoHud_opengl.so /usr/lib/mangohud/lib32/libMangoHud_opengl.so
/usr/bin/install -Dvm644 ./usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
/usr/bin/install -Dvm644 ./usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
/usr/bin/install -Dvm644 ./usr/share/man/man1/mangohud.1 /usr/share/man/man1/mangohud.1
/usr/bin/install -Dvm644 ./usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
/usr/bin/install -vm755 ./usr/bin/mangohud /usr/bin/mangohud
/usr/bin/install -vm755 ./usr/bin/mangoplot /usr/bin/mangoplot
ln -sv $DEFAULTLIB /usr/lib/mangohud/lib
# FIXME get the triplet somehow
ln -sv lib64 /usr/lib/mangohud/x86_64
ln -sv lib64 /usr/lib/mangohud/x86_64-linux-gnu
ln -sv . /usr/lib/mangohud/lib64/x86_64
ln -sv . /usr/lib/mangohud/lib64/x86_64-linux-gnu
install -vm644 -D ./usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
install -vm644 -D ./usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
install -vm644 -D ./usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
install -vm644 -D ./usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
install -vm644 -D ./usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
install -vm644 -D ./usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
install -vm644 -D ./usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
ln -sv lib32 /usr/lib/mangohud/i686
ln -sv lib32 /usr/lib/mangohud/i386-linux-gnu
ln -sv lib32 /usr/lib/mangohud/i686-linux-gnu
mkdir -p /usr/lib/mangohud/tls
ln -sv ../lib64 /usr/lib/mangohud/tls/x86_64
ln -sv ../lib32 /usr/lib/mangohud/tls/i686
# Some distros search in $prefix/x86_64-linux-gnu/tls/x86_64 etc instead
if [ ! -e /usr/lib/mangohud/lib/i386-linux-gnu ]; then
ln -sv ../lib32 /usr/lib/mangohud/lib/i386-linux-gnu
fi
if [ ! -e /usr/lib/mangohud/lib/i686-linux-gnu ]; then
ln -sv ../lib32 /usr/lib/mangohud/lib/i686-linux-gnu
fi
if [ ! -e /usr/lib/mangohud/lib/x86_64-linux-gnu ]; then
ln -sv ../lib64 /usr/lib/mangohud/lib/x86_64-linux-gnu
fi
# $LIB can be "lib/tls/x86_64"?
ln -sv ../tls /usr/lib/mangohud/lib/tls
rm -rf ./usr
install -vm755 ./usr/bin/mangohud.x86 /usr/bin/mangohud.x86
install -vm755 ./usr/bin/mangohud /usr/bin/mangohud
echo "MangoHud Installed"
}
mangohud_uninstall() {
[ "$UID" -eq 0 ] || exec $SU_CMD bash "$0" uninstall
rm -rfv "/usr/lib/mangohud"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
rm -frv "/usr/share/doc/mangohud"
rm -fv "/usr/bin/mangohud"
rm -fv "/usr/bin/mangohud.x86"
}
for a in $@; do
case $a in
"install") mangohud_install;;

@ -1,58 +1,25 @@
#!/bin/sh
if [ "$#" -eq 0 ]; then
programname=$(basename "$0")
echo "ERROR: No program supplied"
echo
echo "Usage: $programname <program>"
exit 1
MANGOHUD_LIB_NAME="libMangoHud.so"
if [ "$MANGOHUD_DLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
fi
# Add exe names newline separated to the string to disable LD_PRELOAD
DISABLE_LD_PRELOAD="cs2.sh
"
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_opengl.so"
if [ "$1" = "--dlsym" ]; then
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
shift # shift will only be executed if $1 is "--dlsym"
elif [ "$MANGOHUD_DLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="@ld_libdir_mangohud@libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
fi
if [ "$1" = "--version" ]; then
echo @version@
exit 0
if [ "$#" -eq 0 ]; then
programname=`basename "$0"`
echo "ERROR: No program supplied"
echo
echo "Usage: $programname <program>"
exit 1
fi
# grab all arguments from command_line
command_line="$*"
# flag for disable_preload
disable_preload=false
# Execute the program under a clean environment
# pass through the overriden LD_PRELOAD environment variables
LD_PRELOAD="${LD_PRELOAD}:${MANGOHUD_LIB_NAME}"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:@libdir_mangohud@"
# Check if the script name or any of the executables in DISABLE_LD_PRELOAD are in the command line
for exe in $DISABLE_LD_PRELOAD; do
if echo "$command_line" | grep -q "$exe"; then
disable_preload=true
break
fi
done
if [ "$disable_preload" = true ]; then
exec env MANGOHUD=1 "$@"
if hash @mangohud_sh@ 2>/dev/null; then
exec env MANGOHUD=1 LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" LD_PRELOAD="${LD_PRELOAD}" @mangohud_sh@ "$@"
else
# Make sure we don't append mangohud lib multiple times
# otherwise, this could cause issues with the steam runtime
case ":${LD_PRELOAD-}:" in
(*:$MANGOHUD_LIB_NAME:*)
;;
(*)
# Preload using the plain filenames of the libs, the dynamic linker will
# figure out whether the 32 or 64 bit version should be used
LD_PRELOAD="${LD_PRELOAD:+$LD_PRELOAD:}${MANGOHUD_LIB_NAME}"
esac
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"
exec env MANGOHUD=1 LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" LD_PRELOAD="${LD_PRELOAD}" "$@"
fi

@ -1,447 +0,0 @@
#!/usr/bin/env python
r"""
Script to plot all the MangoHud benchmarks contained in a given folder.
"""
from pathlib import Path
import argparse
import csv
from typing import List, Union
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Cursor
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.ticker import EngFormatter
plt.rcParams['font.family'] = "Lato,serif"
plt.rcParams['font.weight'] = "600"
background_color = "#1A1C1D"
legend_facecolor = "#585f63"
legend_textcolor = "#cccbc9"
text_color = "#e8e6e3"
mango_color = "#BB770A"
graphbox_linewidth = 1.5
mango_cmap = LinearSegmentedColormap.from_list("mango_heat", [background_color, mango_color])
def identity(val):
r"""
returns the value as-is
"""
return val
def get_integer(val: str) -> int:
r"""
interprets the str 'val' as an integer and returns it
"""
if is_integer(val):
return int(val)
else:
raise ValueError("Casting a non integer value: ", val)
def is_integer(s: str) -> bool:
r"""
tests if 's' is an integer and returns a bool
"""
try:
int(s)
return True
except ValueError:
return False
def get_float(val):
r"""
interprets the str 'val' as a float and returns it
"""
if is_float(val):
return float(val)
else:
return float("nan")
def is_float(s: str) -> bool:
r"""
tests if 's' is an float and returns a bool
"""
try:
float(s)
return True
except ValueError:
return False
class Database:
r"""
A class that contains all the csv files within
the folder that it is instanced with
"""
def __init__(self,
data_folder_path=None,
csv_separator=" ",
filename_var_separator="|"):
self.datafiles = []
self.result_names_col = None
self.result_values_col = None
self.sim_settings_names_col = None
self.sim_settings_values_col = None
if data_folder_path:
self.load_from_folder(
data_folder_path,
csv_separator,
filename_var_separator)
def load_from_folder(self,
data_folder_path,
csv_separator=" ",
filename_var_separator="|"):
r"""
Load all CSV files form the given folder
"""
filepaths = list(Path(data_folder_path).rglob("*.csv"))
self.datafiles = []
N = len(filepaths)
print(f"Loading {N} benchmark files")
for filepath in filepaths:
try:
datafile = BenchmarkFile(
str(filepath),
csv_separator=csv_separator,
filename_var_separator=filename_var_separator)
self.datafiles.append(datafile)
except Exception:
pass
self.datafiles.sort()
class BenchmarkFile:
r"""
A class that represents a single CSV file, can load CSV files
with arbitrary separators. It can return separately any column
of the file and any mathematical combinations of its columns.
"""
def __init__(self,
filepath="",
filename_var_separator="|",
csv_separator=" "):
self.csv_separator = csv_separator
self.filepath = Path(filepath)
self.filename = self.filepath.name
self.filename_var_separator = filename_var_separator
self.variables = dict()
self.skip_lines = None
self.columns = []
self.column_name_to_index = dict()
self._is_data_loaded = False
if not self.filepath.is_file():
raise Exception("CSV file does not exist")
self._read_column_names()
def __lt__(self, other):
stem = self.filename[:-4] # remove the trailing ".csv"
other_stem = other.filename[:-4]
if stem.startswith(other_stem):
return False
elif stem.startswith(other_stem):
return True
else:
return stem < other_stem
def set_variable(self, name, value):
r"""
Saves a variable within the datafile instance
Note: it will not be saved to disk, it's just a helper method to
attach variables to a given data file.
"""
self.variables[name] = value
def get_variable(self, name):
r"""
Retrieves a saved variable in the instance
"""
return self.variables[name]
def _read_column_names(self):
r"""
Read the first few lines of the benchmark file
to look for the row taht contains the benchmark's
column names i.e. "fps", "frametime", "cpu_load"... etc
and save the columns names and their index
Note: we decide that we found the right row by looking if it
contains "fps"
not the best approach, but it works TM
"""
with open(self.filepath) as open_file:
reader = csv.reader(open_file, delimiter=self.csv_separator)
found_fps_column = False
for row_number, row_content in enumerate(reader):
if row_number > 4:
# if we're past the 4th row, break the loop
break
if "fps" in row_content:
self.skip_lines = row_number + 1
found_fps_column = True
for col, col_name in enumerate(row_content):
if col_name in self.column_name_to_index:
raise Exception("Two columns have the same name")
self.column_name_to_index[col_name] = col
if not found_fps_column:
raise Exception("Not a benchmark file")
def _load_data(self):
r"""
Load the benchmark data into memory.
"""
def extend_columns(new_column_num):
current_row_num = 0
if self.columns:
current_row_num = len(self.columns[0])
assert (all([len(column) == current_row_num for column in self.columns]))
current_column_num = len(self.columns)
if new_column_num >= current_column_num:
self.columns += [["" for j in range(current_row_num)] for i in range(new_column_num - current_column_num)]
# no need to load data if it's already loaded
if self._is_data_loaded:
return
with open(self.filepath) as open_file:
reader = csv.reader(open_file, delimiter=self.csv_separator)
self._is_data_loaded = True
for row_number, row_content in enumerate(reader):
if row_number <= self.skip_lines:
continue
extend_columns(len(row_content))
for col, val in enumerate(row_content):
self.columns[col].append(val)
# Delete any eventual empty column
if all([val == "" for val in self.columns[-1]]):
del self.columns[-1]
def get_column_names(self) -> List[str]:
r"""
Returns the list of columns names of the csv file.
"""
return list(self.column_name_to_index.keys())
def get(self, col: str, data_type: str = "float") \
-> Union[List[float], List[str], List[int], List[complex]]:
r"""
Returns the column `col`.
Parameters
----------
col : str
The desired column name to retrieve, or its index
data_type : str
"string", "integer" or "float", the type to cast
the data to before returning it.
Returns
-------
A list of `data_type` containing the column `col`
"""
if not self._is_data_loaded:
self._load_data()
if len(self.columns) == 0:
raise ValueError("Datafile empty, can't return any data")
data_caster_dict = {
"string": identity,
"float": get_float,
"integer": get_integer
}
if data_type not in data_caster_dict:
raise ValueError("the given `data_type' doesn't match any "
"known types. Which are `string', `integer', "
"`float' or `complex'")
if is_integer(col):
# the column's index is given
return [data_caster_dict[data_type](val) for val in self.columns[col]]
if col in self.column_name_to_index:
# a column name has been given
return [data_caster_dict[data_type](val)
for val in self.columns[self.column_name_to_index[col]]]
raise Exception("Column {} does not exist".format(col))
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description='Plot all the MangoHud benchmarks contained in a given folder.')
parser.add_argument('folder', metavar='folder', nargs=1,
help='path the a MangoHud benchmark folder')
args = parser.parse_args()
bench_folder_path = Path(args.folder[0])
if not bench_folder_path.is_dir():
print(f"The path '{bench_folder_path.absolute()}' "
"does not point to an existing folder")
exit(1)
fps_subdivs = 1.0 # one division every fps_subdivs FPS
y_labels = [] # bench files
x_labels = [] # FPS subidivions
database = Database(bench_folder_path, csv_separator=',')
distributions = []
if len(database.datafiles) == 0:
print(f"The folder \n {bench_folder_path.absolute()} \n"
"contains no CSV file "
"(make sure they have the .csv extension)")
exit(1)
for datafile in database.datafiles:
bar_distribution = []
# sort array to get percentiles
fps_array = np.sort(datafile.get("fps"))
# save percentiles
if len(fps_array) < 10000:
print(f"'{datafile.filename}' simulation "
"isn't long enough for precise statistics")
datafile.set_variable("selected", False)
continue
# Save label only if this file has long enough simulation
y_labels.append(datafile.filename[:-4])
datafile.set_variable("selected", True)
# Save percentiles
datafile.set_variable("0.1%", fps_array[int(float(len(fps_array))*0.001)])
datafile.set_variable("1%", fps_array[int(float(len(fps_array))*0.01)])
datafile.set_variable("50%", fps_array[int(float(len(fps_array))*0.5)])
datafile.set_variable("average fps", np.average(fps_array))
for frame_num, fps in enumerate(fps_array):
if fps > 1000:
print("FPS value above 1000, omitting outlier.")
continue
index = int(fps/fps_subdivs)
for i in range(len(bar_distribution), index+1):
bar_distribution.append(0)
bar_distribution[index] += 1
distributions.append(bar_distribution)
if not distributions:
print("Nothing to plot, exiting.")
exit(1)
num_benchs = len(distributions)
max_size = 0
for distrib in distributions:
max_size = max(max_size, len(distrib))
for distrib in distributions:
for i in range(len(distrib), max_size):
distrib.append(0)
for i in range(max_size):
x_labels.append(str(fps_subdivs * i))
fig, ax = plt.subplots()
# change color of the graph box to the same color as the text
for spine in ['left', 'right', 'bottom', 'top']:
ax.spines[spine].set_color(text_color)
ax.spines[spine].set_linewidth(graphbox_linewidth)
im = ax.imshow(distributions,
aspect="auto",
extent=[0, max_size*fps_subdivs, 0, num_benchs],
cmap=mango_cmap)
# draw thick line that separates each benchmark
for i in range(len(y_labels)+1):
ax.axhline(float(i), color=text_color, lw=graphbox_linewidth)
i = 0
for datafile in database.datafiles:
if datafile.get_variable("selected"):
kwargs = dict(ymin=(num_benchs-i-1+0.15)/num_benchs,
ymax=(num_benchs-i-0.15)/num_benchs,
lw=3)
ax.axvline(datafile.get_variable("0.1%"),
color='#35260f',
label=("0.1%" if i == 0 else None), **kwargs)
ax.axvline(datafile.get_variable("1%"),
color='#6E4503',
label=("1%" if i == 0 else None), **kwargs)
ax.axvline(datafile.get_variable("50%"),
color='#0967BA',
label=("50%" if i == 0 else None), **kwargs)
ax.axvline(datafile.get_variable("average fps"),
color='#003A6E',
label=("Average" if i == 0 else None), **kwargs)
i += 1
ax.tick_params(axis='y', colors=text_color)
ax.tick_params(axis='x', colors=text_color)
ax.set_yticks(np.arange(len(y_labels)-0.5, 0, -1), labels=y_labels)
ax.grid(False)
fig.set_facecolor(background_color)
ax.ticklabel_format(axis='x', style='plain')
formatter0 = EngFormatter(unit='FPS')
ax.xaxis.set_major_formatter(formatter0)
plt.tight_layout()
plt.legend(facecolor=legend_facecolor, labelcolor=legend_textcolor)
cursor = Cursor(ax,
horizOn=False,
color='#6c49abff',
linewidth=4,
useblit=True)
plt.show()

@ -1,7 +0,0 @@
# runtime dependencies for `mangoplot`: matplotlib and a GUI backed like PyQt5
install_data(
'mangoplot.py',
install_dir: get_option('bindir'),
rename: 'mangoplot',
install_mode: 'rwxr-xr-x'
)

@ -0,0 +1,20 @@
#!/bin/sh
MANGOHUD_LIB_NAME="libMangoHud.so"
if [ "$MANGOHUD_NODLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="libMangoHud_nodlsym.so"
fi
if [ "$#" -eq 0 ]; then
programname=`basename "$0"`
echo "ERROR: No program supplied"
echo
echo "Usage: $programname <program>"
exit 1
fi
# Execute the program under a clean environment
# pass through the overriden LD_PRELOAD environment variables
LD_PRELOAD="${LD_PRELOAD}:${MANGOHUD_LIB_NAME}"
exec env MANGOHUD=1 LD_PRELOAD="${LD_PRELOAD}" "$@"

@ -0,0 +1,23 @@
#!/bin/sh
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
MANGOHUD_LIB_NAME="libMangoHud.so:libMangoHud32.so"
if [ "$MANGOHUD_NODLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="libMangoHud_nodlsym.so:libMangoHud_nodlsym32.so"
fi
if [ "$#" -eq 0 ]; then
programname=`basename "$0"`
echo "ERROR: No program supplied"
echo
echo "Usage: $programname <program>"
exit 1
fi
# Execute the program under a clean environment
# pass through the overriden LD_PRELOAD environment variables
LD_PRELOAD="${LD_PRELOAD}:${MANGOHUD_LIB_NAME}"
LD_LIBRARY_PATH="${XDG_DATA_HOME}/MangoHud"
echo $LD_LIBRARY_PATH
exec env MANGOHUD=1 LD_LIBRARY_PATH="$LD_LIBRARY_PATH" LD_PRELOAD="${LD_PRELOAD}" "$@"

@ -1,40 +1,10 @@
#!/bin/sh
VERSION=$(git describe --tags --dirty)
NAME=MangoHud-${VERSION}
TAR_NAME=${NAME}-Source.tar.xz
DFSG_TAR_NAME=${NAME}-Source-DFSG.tar.xz
VERSION=$(git describe --tags --dirty=+)
# remove existing files
rm -rf sourcedir
rm -rf ${NAME}
rm -f ${TAR_NAME}
rm -f ${DFSG_TAR_NAME}
FILE_PATTERN="--exclude-vcs --exclude-vcs-ignores ."
# create tarball with meson
meson setup sourcedir
meson dist --formats=xztar --include-subprojects --no-tests -C sourcedir
mv sourcedir/meson-dist/*.tar.xz ${TAR_NAME}
# create DFSG compliant version
# unpack since tarball is compressed
mkdir ${NAME}
tar -xf ${TAR_NAME} --strip 1 -C ${NAME}
# nvml.h is not DFSG compliant
rm ${NAME}/include/nvml.h
# minhook not needed
rm -r ${NAME}/modules/minhook
# spdlog from system
rm -r ${NAME}/subprojects/spdlog-*
# nlohmann_json from system
rm -r ${NAME}/subprojects/nlohmann_json-*
# remove some vulkan clutter
rm -r ${NAME}/subprojects/Vulkan-Headers-*/cmake ${NAME}/subprojects/Vulkan-Headers-*/BUILD.gn
# remove some dear imgui clutter
rm -rf ${NAME}/subprojects/imgui-*/examples
# compress new sources
tar -cJf ${DFSG_TAR_NAME} ${NAME}
# cleanup
rm -r sourcedir
rm -r ${NAME}
# default version
tar -czf MangoHud-$VERSION-Source.tar.gz $FILE_PATTERN
# DFSG compliant version, excludes NVML
tar -czf MangoHud-$VERSION-Source-DFSG.tar.gz --exclude=include/nvml.h $FILE_PATTERN

@ -1,161 +0,0 @@
#!/usr/bin/env bash
# Specialized build script for Steam Runtime SDK docker
set -e
IFS=" " read -ra debian_chroot < /etc/debian_chroot
LOCAL_CC=${CC:-gcc-5}
LOCAL_CXX=${CXX:-g++-5}
RUNTIME=${RUNTIME:-${debian_chroot[1]}}
SRT_VERSION=${SRT_VERSION:-${debian_chroot[2]}}
VERSION=$(git describe --long --tags --always | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//')
dependencies() {
if [[ ! -f build-srt/release/usr/lib/libMangoHud.so ]]; then
install() {
set +e
for i in ${DEPS[@]}; do
dpkg-query -s "$i" &> /dev/null
if [[ $? == 1 ]]; then
INSTALL="$INSTALL""$i "
fi
done
if [[ ! -z "$INSTALL" ]]; then
apt-get update
apt-get -y install $INSTALL
fi
set -e
}
echo "# Checking Dependencies"
DEPS=(${LOCAL_CC}-multilib ${LOCAL_CXX}-multilib unzip)
install
# use py3.5 with scout, otherwise hope python is new enough
set +e
which python3.5 >/dev/null
if [ $? -eq 0 ]; then
# py3.2 is weird
ln -sf python3.5 /usr/bin/python3
fi
set -e
if [[ ! -f ./bin/get-pip.py ]]; then
curl https://bootstrap.pypa.io/pip/3.5/get-pip.py -o bin/get-pip.py
python3 ./bin/get-pip.py
fi
pip3 install 'meson>=0.54' mako
if [[ ! -f /usr/include/NVCtrl/NVCtrl.h ]]; then
curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl0_440.64-0ubuntu1_amd64.deb
curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl-dev_440.64-0ubuntu1_amd64.deb
dpkg -i libxnvctrl0_440.64-0ubuntu1_amd64.deb libxnvctrl-dev_440.64-0ubuntu1_amd64.deb
fi
# preinstalled 7.10.xxxx
#if [[ ! -f /usr/local/bin/glslangValidator ]]; then
# curl -LO https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip
# unzip glslang-master-linux-Release.zip bin/glslangValidator
# /usr/bin/install -m755 bin/glslangValidator /usr/local/bin/
# rm bin/glslangValidator glslang-master-linux-Release.zip
#fi
fi
}
configure() {
dependencies
git submodule update --init
if [[ ! -f "build-srt/meson64/build.ninja" ]]; then
export CC="${LOCAL_CC}"
export CXX="${LOCAL_CXX}"
meson build-srt/meson64 --libdir lib/mangohud/lib --prefix /usr -Dappend_libdir_mangohud=false $@ ${CONFIGURE_OPTS}
fi
if [[ ! -f "build-srt/meson32/build.ninja" ]]; then
export CC="${LOCAL_CC} -m32"
export CXX="${LOCAL_CXX} -m32"
export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
meson build-srt/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false $@ ${CONFIGURE_OPTS}
fi
}
build() {
if [[ ! -f "build-srt/meson64/build.ninja" || ! -f "build-srt/meson32/build.ninja" ]]; then
configure $@
fi
DESTDIR="$PWD/build-srt/release" ninja -C build-srt/meson32 install
DESTDIR="$PWD/build-srt/release" ninja -C build-srt/meson64 install
}
package() {
LIB="build-srt/release/usr/lib/mangohud/lib/libMangoHud.so"
LIB32="build-srt/release/usr/lib/mangohud/lib32/libMangoHud.so"
if [[ ! -f "$LIB" || "$LIB" -ot "build-srt/meson64/src/libMangoHud.so" ]]; then
build
fi
tar --numeric-owner --owner=0 --group=0 \
-C build-srt/release -cvf "build-srt/MangoHud-package.tar" .
}
release() {
rm build-srt/MangoHud-package.tar
mkdir -p build-srt/MangoHud
package
cp --preserve=mode bin/mangohud-setup.sh build-srt/MangoHud/mangohud-setup.sh
cp build-srt/MangoHud-package.tar build-srt/MangoHud/MangoHud-package.tar
tar --numeric-owner --owner=0 --group=0 \
-C build-srt -czvf build-srt/MangoHud-${VERSION}_${RUNTIME}-${SRT_VERSION}.tar.gz MangoHud
}
clean() {
rm -rf "build-srt/"
}
usage() {
if test -z $1; then
echo "Unrecognized command argument: $a"
else
echo "$0 requires one argument"
fi
echo -e "\nUsage: $0 <command>\n"
echo "Available commands:"
echo -e "\tpull\t\tPull latest commits (code) from Git"
echo -e "\tconfigure\tEnsures that dependencies are installed, updates git submodules, and generates files needed for building MangoHud. This is automatically run by the build command"
echo -e "\tbuild\t\tIf needed runs configure and then builds (compiles) MangoHud"
echo -e "\tpackage\t\tRuns build if needed and then builds a tar package from MangoHud"
echo -e "\tclean\t\tRemoves build directory"
echo -e "\trelease\t\tBuilds a MangoHud release tar package"
}
if [[ -z $@ ]]; then
usage no-args
fi
while [ $# -gt 0 ]; do
OPTS=()
arg="$1"
shift
while [ $# -gt 0 ] ; do
case $1 in
-*)
OPTS+=("$1")
shift
;;
*)
break
;;
esac;
done
echo -e "\e[1mCommand:\e[92m" $arg "\e[94m"${OPTS[@]}"\e[39m\e[0m"
case $arg in
"configure") configure ${OPTS[@]};;
"build") build ${OPTS[@]};;
"package") package;;
"clean") clean;;
"release") release;;
*)
usage
esac
done

@ -1,41 +0,0 @@
#!/usr/bin/env bash
# Usage example: $0 master soldier 0.20210618.0
set -u
if [ $# -eq 2 ]; then
echo Specify runtime version too
exit 1
fi
SRCDIR=$PWD
BRANCH="${1:-master}"
# soldier 0.20210618.0 or newer
# scout 0.20210630.0 or newer
RUNTIME="${2:-soldier}"
VERSION="${3:-0.20210618.0}"
IMAGE="steamrt_${RUNTIME}_${VERSION}_amd64:mango-${RUNTIME}"
BASEURL="https://repo.steampowered.com/steamrt-images-${RUNTIME}/snapshots/${VERSION}"
CACHEDIR="./cache/steamrt-images-${RUNTIME}/snapshots/${VERSION}"
mkdir -p "${CACHEDIR}"
echo -e "\e[1mBuilding branch \e[92m${BRANCH}\e[39m using \e[92m${RUNTIME}:${VERSION}\e[39m runtime\e[0m"
if ! docker inspect --type=image ${IMAGE} 2>&1 >/dev/null ; then
rm -fr ./cache/empty
set -e
mkdir -p ./cache/empty
sed "s/%RUNTIME%/${RUNTIME}/g" steamrt.Dockerfile.in > ./cache/steamrt.Dockerfile
wget -P "${CACHEDIR}" -c ${BASEURL}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-${RUNTIME}-sysroot.tar.gz
cp --reflink=always "${CACHEDIR}/com.valvesoftware.SteamRuntime.Sdk-amd64,i386-${RUNTIME}-sysroot.tar.gz" ./cache/empty/
docker build -f ./cache/steamrt.Dockerfile -t ${IMAGE} ./cache/empty
fi
docker run --entrypoint=/bin/sh --rm -i -v "${SRCDIR}/srt-output:/output" ${IMAGE} << EOF
export RUNTIME=${RUNTIME}
export SRT_VERSION=${VERSION}
git clone git://github.com/flightlessmango/MangoHud.git . --branch ${BRANCH} --recurse-submodules --progress
./build-srt.sh clean build package release
cp -v build-srt/MangoHud*tar.gz /output/
EOF

@ -1,16 +1,16 @@
#!/usr/bin/env bash
set -e
# Import the variables for dependencies
source ./build_deps.sh
OS_RELEASE_FILES=("/etc/os-release" "/usr/lib/os-release")
XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
DATA_DIR="$XDG_DATA_HOME/MangoHud"
CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
LAYER="build/release/usr/share/vulkan/implicit_layer.d/mangohud.json"
INSTALL_DIR="build/package/"
IMPLICIT_LAYER_DIR="$XDG_DATA_HOME/vulkan/implicit_layer.d"
VERSION=$(git describe --long --tags --always | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//')
SU_CMD=$(command -v sudo || command -v doas || echo)
MACHINE=$(uname -m || echo)
SU_CMD=$(command -v sudo || command -v doas)
# doas requires a double dash if the command it runs will include any dashes,
# so append a double dash to the command
@ -21,11 +21,11 @@ for os_release in ${OS_RELEASE_FILES[@]} ; do
if [[ ! -e "${os_release}" ]]; then
continue
fi
DISTRO=$(sed -rn 's/^ID(_LIKE)*=(.+)/\L\2/p' ${os_release} | sed 's/"//g')
DISTRO=$(sed -rn 's/^NAME=(.+)/\1/p' ${os_release} | sed 's/"//g')
done
dependencies() {
if [[ ! -f build/release/usr/lib/libMangoHud.so ]]; then
if [[ ! -f build/release/usr/lib64/libMangoHud.so ]]; then
missing_deps() {
echo "# Missing dependencies:$INSTALL"
read -rp "Do you wish the script to install these packages? [y/N]" PERMISSION
@ -50,33 +50,29 @@ dependencies() {
fi
set -e
}
echo "# Checking Dependencies"
for i in $DISTRO; do
echo "# Checking dependencies for \"$i\""
case $i in
*arch*|*manjaro*|*artix*|*SteamOS*)
case $DISTRO in
"Arch Linux"|"Manjaro Linux")
MANAGER_QUERY="pacman -Q"
MANAGER_INSTALL="pacman -S"
DEPS="{${DEPS_ARCH}}"
DEPS="{gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl}"
dep_install
break
;;
*fedora*|*nobara*)
"Fedora")
MANAGER_QUERY="dnf list installed"
MANAGER_INSTALL="dnf install"
DEPS="{${DEPS_FEDORA}}"
DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel}"
dep_install
unset INSTALL
DEPS="{glibc-devel.i686,libstdc++-devel.i686,libX11-devel.i686,wayland-devel.i686,libxkbcommon-devel.i686}"
DEPS="{glibc-devel.i686,libstdc++-devel.i686,libX11-devel.i686}"
dep_install
break
;;
*debian*|*ubuntu*|*deepin*|*pop*)
*"buntu"|"Linux Mint"|"Debian GNU/Linux"|"Zorin OS"|"Pop!_OS"|"elementary OS")
MANAGER_QUERY="dpkg-query -s"
MANAGER_INSTALL="apt install"
DEPS="{${DEPS_DEBIAN}}"
DEPS="{gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev}"
dep_install
if [[ $(pip3 show meson; echo $?) == 1 || $(pip3 show mako; echo $?) == 1 ]]; then
@ -85,32 +81,39 @@ dependencies() {
if [[ ! -f /usr/local/bin/glslangValidator ]]; then
wget https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip
unzip glslang-master-linux-Release.zip bin/glslangValidator
$SU_CMD /usr/bin/install -m755 bin/glslangValidator /usr/local/bin/
$SU_CMD install -m755 bin/glslangValidator /usr/local/bin/
rm bin/glslangValidator glslang-master-linux-Release.zip
fi
break
;;
*suse*)
echo "You may have to enable packman repository for some extra packages: ${DEPS_SUSE_EXTRA}"
echo "Leap: zypper ar -cfp 90 https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Leap_15.1/ packman"
echo "Tumbleweed: zypper ar -cfp 90 http://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/ packman"
"openSUSE Leap"|"openSUSE Tumbleweed")
PACKMAN_PKGS="libXNVCtrl-devel"
case $DISTRO in
"openSUSE Leap")
echo "You may have to enable packman repository for some extra packages: ${PACKMAN_PKGS}"
echo "zypper ar -cfp 90 https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Leap_15.1/ packman"
;;
"openSUSE Tumbleweed")
echo "You may have to enable packman repository for some extra packages: ${PACKMAN_PKGS}"
echo "zypper ar -cfp 90 http://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/ packman"
;;
esac
MANAGER_QUERY="rpm -q"
MANAGER_INSTALL="zypper install"
DEPS="{${DEPS_SUSE},${DEPS_SUSE_EXTRA}}"
DEPS="{gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel,${PACKMAN_PKGS}}"
dep_install
if [[ $(pip3 show meson; echo $?) == 1 ]]; then
$SU_CMD pip3 install 'meson>=0.54'
fi
break
;;
*solus*)
"Solus")
unset MANAGER_QUERY
unset DEPS
MANAGER_INSTALL="eopkg it"
local packages=(${DEPS_SOLUS//,/ })
local packages=("mesalib-32bit-devel" "glslang" "libstdc++-32bit" "glibc-32bit-devel" "mako")
# eopkg doesn't emit exit codes properly, so use the python API to find if a package is installed.
for package in ${packages[@]}; do
@ -127,42 +130,35 @@ dependencies() {
INSTALL="${INSTALL}""-c system.devel "
fi
dep_install
break
;;
*)
echo "# Unable to find distro information!"
echo "# Attempting to build regardless"
esac
done
fi
}
configure() {
dependencies
git submodule update --init --depth 50
CONFIGURE_OPTS="-Dwerror=true"
if [[ ! -f "build/meson64/build.ninja" ]]; then
meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false $@ ${CONFIGURE_OPTS}
meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
fi
if [[ ! -f "build/meson32/build.ninja" && "$MACHINE" = "x86_64" ]]; then
if [[ ! -f "build/meson32/build.ninja" ]]; then
export CC="gcc -m32"
export CXX="g++ -m32"
export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false $@ ${CONFIGURE_OPTS}
export LLVM_CONFIG="/usr/bin/llvm-config32"
meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
fi
}
build() {
if [[ ! -f "build/meson64/build.ninja" ]]; then
configure $@
configure
fi
DESTDIR="$PWD/build/release" ninja -C build/meson32 install
DESTDIR="$PWD/build/release" ninja -C build/meson64 install
if [ "$MACHINE" = "x86_64" ]; then
DESTDIR="$PWD/build/release" ninja -C build/meson32 install
fi
sed -i 's:/usr/\\$LIB:/usr/lib/mangohud/\\$LIB:g' "$PWD/build/release/usr/bin/mangohud"
}
package() {
@ -185,93 +181,23 @@ release() {
-C build -czvf build/MangoHud-$VERSION.tar.gz MangoHud
}
uninstall() {
[ "$UID" -eq 0 ] || exec $SU_CMD bash "$0" uninstall
rm -rfv "/usr/lib/mangohud"
rm -rfv "/usr/share/doc/mangohud"
rm -fv "/usr/share/man/man1/mangohud.1"
rm -fv "/usr/share/vulkan/implicit_layer.d/mangohud.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
rm -fv "/usr/bin/mangohud"
rm -fv "/usr/bin/mangoplot"
rm -fv "/usr/bin/mangohud.x86"
}
install() {
rm -rf "$HOME/.local/share/MangoHud/"
rm -f "$HOME/.local/share/vulkan/implicit_layer.d/"{mangohud32.json,mangohud64.json}
[ "$UID" -eq 0 ] || mkdir -pv "${CONFIG_DIR}"
[ "$UID" -eq 0 ] || build
[ "$UID" -eq 0 ] || exec $SU_CMD bash "$0" install
uninstall
DEFAULTLIB=lib32
for i in $DISTRO; do
case $i in
*arch*)
DEFAULTLIB=lib64
;;
esac
done
if [ "$MACHINE" != "x86_64" ]; then
# Native libs
DEFAULTLIB=lib64
fi
echo DEFAULTLIB: $DEFAULTLIB
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib64/libMangoHud_opengl.so /usr/lib/mangohud/lib64/libMangoHud_opengl.so
if [ "$MACHINE" = "x86_64" ]; then
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
/usr/bin/install -Dvm644 ./build/release/usr/lib/mangohud/lib32/libMangoHud_opengl.so /usr/lib/mangohud/lib32/libMangoHud_opengl.so
fi
/usr/bin/install -Dvm644 ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
/usr/bin/install -Dvm644 ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
/usr/bin/install -Dvm644 ./build/release/usr/share/man/man1/mangohud.1 /usr/share/man/man1/mangohud.1
/usr/bin/install -Dvm644 ./build/release/usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
/usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud
/usr/bin/install -vm755 ./build/release/usr/bin/mangoplot /usr/bin/mangoplot
ln -sv $DEFAULTLIB /usr/lib/mangohud/lib
# FIXME get the triplet somehow
ln -sv lib64 /usr/lib/mangohud/x86_64
ln -sv lib64 /usr/lib/mangohud/x86_64-linux-gnu
ln -sv . /usr/lib/mangohud/lib64/x86_64
ln -sv . /usr/lib/mangohud/lib64/x86_64-linux-gnu
ln -sv lib32 /usr/lib/mangohud/i686
ln -sv lib32 /usr/lib/mangohud/i386-linux-gnu
ln -sv lib32 /usr/lib/mangohud/i686-linux-gnu
mkdir -p /usr/lib/mangohud/tls
ln -sv ../lib64 /usr/lib/mangohud/tls/x86_64
ln -sv ../lib32 /usr/lib/mangohud/tls/i686
/usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
/usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
/usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
/usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
/usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
/usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
/usr/bin/install -vm644 -D ./build/release/usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
# Some distros search in $prefix/x86_64-linux-gnu/tls/x86_64 etc instead
if [ ! -e /usr/lib/mangohud/lib/i386-linux-gnu ]; then
ln -sv ../lib32 /usr/lib/mangohud/lib/i386-linux-gnu
fi
if [ ! -e /usr/lib/mangohud/lib/i686-linux-gnu ]; then
ln -sv ../lib32 /usr/lib/mangohud/lib/i686-linux-gnu
fi
if [ ! -e /usr/lib/mangohud/lib/x86_64-linux-gnu ]; then
ln -sv ../lib64 /usr/lib/mangohud/lib/x86_64-linux-gnu
fi
# $LIB can be "lib/tls/x86_64"?
ln -sv ../tls /usr/lib/mangohud/lib/tls
#ln -sv lib64 /usr/lib/mangohud/aarch64-linux-gnu
#ln -sv lib64 /usr/lib/mangohud/arm-linux-gnueabihf
/usr/bin/install -vm755 ./build/release/usr/bin/mangohud.x86 /usr/bin/mangohud.x86
/usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud
echo "MangoHud Installed"
}
@ -284,12 +210,23 @@ reinstall() {
clean() {
rm -rf "build"
rm -rf subprojects/*/
}
uninstall() {
[ "$UID" -eq 0 ] || exec $SU_CMD bash "$0" uninstall
rm -rfv "/usr/lib/mangohud"
rm -rfv "/usr/share/doc/mangohud"
rm -fv "/usr/share/vulkan/implicit_layer.d/mangohud.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
rm -fv "/usr/bin/mangohud"
rm -fv "/usr/bin/mangohud.x86"
}
usage() {
if test -z $1; then
echo "Unrecognized command argument: $arg"
echo "Unrecognized command argument: $a"
else
echo "$0 requires one argument"
fi
@ -300,39 +237,17 @@ usage() {
echo -e "\tbuild\t\tIf needed runs configure and then builds (compiles) MangoHud"
echo -e "\tpackage\t\tRuns build if needed and then builds a tar package from MangoHud"
echo -e "\tinstall\t\tInstall MangoHud onto your system"
echo -e "\treinstall\tRuns build, then package, and finally install"
echo -e "\tclean\t\tRemoves build directory"
echo -e "\tuninstall\tRemoves installed MangoHud files from your system"
echo -e "\trelease\t\tBuilds a MangoHud release tar package"
}
if [[ -z $@ ]]; then
usage no-args
fi
while [ $# -gt 0 ]; do
OPTS=()
arg="$1"
shift
while [ $# -gt 0 ] ; do
case $1 in
-*)
OPTS+=("$1")
shift
;;
*)
break
;;
esac;
done
echo -e "\e[1mCommand:\e[92m" $arg "\e[94m"${OPTS[@]}"\e[39m\e[0m"
case $arg in
"pull") git pull ${OPTS[@]};;
"configure") configure ${OPTS[@]};;
"build") build ${OPTS[@]};;
"build_dbg") build --buildtype=debug -Dglibcxx_asserts=true ${OPTS[@]};;
for a in $@; do
case $a in
"") build;;
"pull") git pull;;
"configure") configure;;
"build") build;;
"package") package;;
"install") install;;
"reinstall") reinstall;;
@ -343,3 +258,8 @@ while [ $# -gt 0 ]; do
usage
esac
done
if [[ -z $@ ]]; then
usage no-args
fi

@ -1,7 +0,0 @@
DEPS_ARCH="gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl,libdrm,python-numpy,python-matplotlib,libxkbcommon"
DEPS_FEDORA="meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel,python3-numpy,python3-matplotlib,libstdc++-static,libstdc++-static.i686,libxkbcommon-devel,wayland-devel"
DEPS_DEBIAN="gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev,python3-numpy,python3-matplotlib,libxkbcommon-dev,libxkbcommon-dev:i386,libwayland-dev,libwayland-dev:i386"
DEPS_SOLUS="mesalib-32bit-devel,glslang,libstdc++-32bit,glibc-32bit-devel,mako,numpy,matplotlib,libxkbcommon-devel"
DEPS_SUSE="gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel,python-numpy,python-matplotlib,libxkbcommon-devel"
DEPS_SUSE_EXTRA="libXNVCtrl-devel"

@ -1,27 +0,0 @@
[metadata]
name = mangohud_control
version = 0.0.1
author = Simon Hallsten
author_email = flightlessmangoyt@gmail.com
description = control interface for mangohud
classifiers =
Programming Language :: Python :: 3
License :: OSI Approved :: MIT License
Operating System :: Linux
[options]
package_dir =
= src
packages = find:
python_requires = >=3.6
[options.packages.find]
where = src
[options.entry_points]
console_scripts =
mangohud-control = control:main
[pycodestyle]
max-line-length = 160

@ -1,9 +0,0 @@
import site
import sys
from setuptools import setup
if __name__ == "__main__":
setup()
# See https://github.com/pypa/pip/issues/7953
site.ENABLE_USER_SITE = "--user" in sys.argv[1:]

@ -1,223 +0,0 @@
#!/usr/bin/env python3
import os
import socket
import sys
import select
from select import EPOLLIN, EPOLLPRI, EPOLLERR
import time
from collections import namedtuple
import argparse
TIMEOUT = 1.0 # seconds
VERSION_HEADER = bytearray('MangoHudControlVersion', 'utf-8')
DEVICE_NAME_HEADER = bytearray('DeviceName', 'utf-8')
MANGOHUD_VERSION_HEADER = bytearray('MangoHudVersion', 'utf-8')
DEFAULT_SERVER_ADDRESS = "\0mangohud"
class Connection:
def __init__(self, path):
# Create a Unix Domain socket and connect
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
try:
sock.connect(path)
except socket.error as msg:
print(msg)
sys.exit(1)
self.sock = sock
# initialize poll interface and register socket
epoll = select.epoll()
epoll.register(sock, EPOLLIN | EPOLLPRI | EPOLLERR)
self.epoll = epoll
def recv(self, timeout):
'''
timeout as float in seconds
returns:
- None on error or disconnection
- bytes() (empty) on timeout
'''
events = self.epoll.poll(timeout)
for ev in events:
(fd, event) = ev
if fd != self.sock.fileno():
continue
# check for socket error
if event & EPOLLERR:
return None
# EPOLLIN or EPOLLPRI, just read the message
msg = self.sock.recv(4096)
# socket disconnected
if len(msg) == 0:
return None
return msg
return bytes()
def send(self, msg):
self.sock.send(msg)
class MsgParser:
MSGBEGIN = bytes(':', 'utf-8')[0]
MSGEND = bytes(';', 'utf-8')[0]
MSGSEP = bytes('=', 'utf-8')[0]
def __init__(self, conn):
self.cmdpos = 0
self.parampos = 0
self.bufferpos = 0
self.reading_cmd = False
self.reading_param = False
self.buffer = None
self.cmd = bytearray(4096)
self.param = bytearray(4096)
self.conn = conn
def readCmd(self, ncmds, timeout=TIMEOUT):
'''
returns:
- None on error or disconnection
- bytes() (empty) on timeout
'''
parsed = []
remaining = timeout
while remaining > 0 and ncmds > 0:
now = time.monotonic()
if self.buffer == None:
self.buffer = self.conn.recv(remaining)
self.bufferpos = 0
# disconnected or error
if self.buffer == None:
return None
for i in range(self.bufferpos, len(self.buffer)):
c = self.buffer[i]
self.bufferpos += 1
if c == self.MSGBEGIN:
self.cmdpos = 0
self.parampos = 0
self.reading_cmd = True
self.reading_param = False
elif c == self.MSGEND:
if not self.reading_cmd:
continue
self.reading_cmd = False
self.reading_param = False
cmd = self.cmd[0:self.cmdpos]
param = self.param[0:self.parampos]
self.reading_cmd = False
self.reading_param = False
parsed.append((cmd, param))
ncmds -= 1
if ncmds == 0:
break
elif c == self.MSGSEP:
if self.reading_cmd:
self.reading_param = True
else:
if self.reading_param:
self.param[self.parampos] = c
self.parampos += 1
elif self.reading_cmd:
self.cmd[self.cmdpos] = c
self.cmdpos += 1
# if we read the entire buffer and didn't finish the command,
# throw it away
self.buffer = None
# check if we have time for another iteration
elapsed = time.monotonic() - now
remaining = max(0, remaining - elapsed)
# timeout
return parsed
def control(args):
if args.socket:
address = '\0' + args.socket
else:
address = DEFAULT_SERVER_ADDRESS
conn = Connection(address)
msgparser = MsgParser(conn)
version = None
name = None
mangohud_version = None
msgs = msgparser.readCmd(3)
for m in msgs:
cmd, param = m
if cmd == VERSION_HEADER:
version = int(param)
elif cmd == DEVICE_NAME_HEADER:
name = param.decode('utf-8')
elif cmd == MANGOHUD_VERSION_HEADER:
mangohud_version = param.decode('utf-8')
if args.info:
info = "Protocol Version: {}\n"
info += "Device Name: {}\n"
info += "MangoHud Version: {}"
print(info.format(version, name, mangohud_version))
if args.cmd == 'toggle-logging':
conn.send(bytearray(':logging;', 'utf-8'))
elif args.cmd == 'start-logging':
conn.send(bytearray(':logging=1;', 'utf-8'))
elif args.cmd == 'stop-logging':
conn.send(bytearray(':logging=0;', 'utf-8'))
now = time.monotonic()
while True:
msg = str(conn.recv(3))
if "LoggingFinished" in msg:
print("Logging has stopped")
exit(0)
elapsed = time.monotonic() - now
if elapsed > 3:
print("Stop logging timed out")
exit(1)
elif args.cmd == 'toggle-hud':
conn.send(bytearray(':hud;', 'utf-8'))
elif args.cmd == 'toggle-fcat':
conn.send(bytearray(':fcat;', 'utf-8'))
def main():
parser = argparse.ArgumentParser(description='MangoHud control client')
parser.add_argument('--info', action='store_true', help='Print info from socket')
parser.add_argument('--socket', '-s', type=str, help='Path to socket')
commands = parser.add_subparsers(help='commands to run', dest='cmd')
commands.add_parser('toggle-hud')
commands.add_parser('toggle-logging')
commands.add_parser('start-logging')
commands.add_parser('stop-logging')
commands.add_parser('toggle-fcat')
args = parser.parse_args()
control(args)
if __name__ == '__main__':
main()

@ -1,359 +0,0 @@
### MangoHud configuration file
### Uncomment any options you wish to enable. Default options are left uncommented
### Use some_parameter=0 to disable a parameter (only works with on/off parameters)
### Everything below can be used / overridden with the environment variable MANGOHUD_CONFIG instead
################ INFORMATIONAL #################
## prints possible options on stdout
# help
################ PERFORMANCE #################
### Limit the application FPS. Comma-separated list of one or more FPS values (e.g. 0,30,60). 0 means unlimited (unless VSynced)
# fps_limit=0
### early = wait before present, late = wait after present
# fps_limit_method=
### VSync [0-3] 0 = adaptive; 1 = off; 2 = mailbox; 3 = on
# vsync=-1
### OpenGL VSync [0-N] 0 = off; >=1 = wait for N v-blanks, N > 1 acts as a FPS limiter (FPS = display refresh rate / N)
# gl_vsync=-2
### Mip-map LoD bias. Negative values will increase texture sharpness (and aliasing)
## Positive values will increase texture blurriness (-16 to 16)
# picmip=-17
### Anisotropic filtering level. Improves sharpness of textures viewed at an angle (0 to 16)
# af=-1
### Force bicubic filtering
# bicubic
### Force trilinear filtering
# trilinear
### Disable linear texture filtering. Makes textures look blocky
# retro
################### VISUAL ###################
### Legacy layout
# legacy_layout=0
### pre defined presets
# -1 = default
# 0 = no display
# 1 = fps only
# 2 = horizontal view
# 3 = extended
# 4 = high detailed information
# preset=-1
### Enable most of the toggleable parameters (currently excludes `histogram`)
# full
### Show FPS only. ***Not meant to be used with other display params***
# fps_only
### Display custom centered text, useful for a header
# custom_text_center=
### Display the current system time
# time
## removes the time label
# time_no_label
### Time formatting examples
## %H:%M
## [ %T %F ]
## %X # locally formatted time, because of limited glyph range, missing characters may show as '?' (e.g. Japanese)
# time_format="%T"
### Display MangoHud version
# version
### Display the current GPU information
## Note: gpu_mem_clock and gpu_mem_temp also need "vram" to be enabled
gpu_stats
# gpu_temp
# gpu_junction_temp
# gpu_core_clock
# gpu_mem_temp
# gpu_mem_clock
# gpu_power
# gpu_text=
# gpu_load_change
# gpu_load_value=60,90
# gpu_load_color=39F900,FDFD09,B22222
## GPU fan in rpm on AMD, FAN in percent on NVIDIA
# gpu_fan
## gpu_voltage only works on AMD GPUs
# gpu_voltage
### Display the current CPU information
cpu_stats
# cpu_temp
# cpu_power
# cpu_text=
# cpu_mhz
# cpu_load_change
# cpu_load_value=60,90
# cpu_load_color=39F900,FDFD09,B22222
### Display the current CPU load & frequency for each core
# core_load
# core_load_change
### Display IO read and write for the app (not system)
# io_read
# io_write
### Display system vram / ram / swap space usage
# vram
# ram
# swap
### Display per process memory usage
## Show resident memory and other types, if enabled
# procmem
# procmem_shared
# procmem_virt
### Display battery information
# battery
# battery_icon
# device_battery=gamepad,mouse
# device_battery_icon
# battery_watt
# battery_time
### Display FPS and frametime
fps
# fps_sampling_period=500
# fps_color_change
# fps_value=30,60
# fps_color=B22222,FDFD09,39F900
frametime
# frame_count
## fps_metrics takes a list of decimal values or the value avg
# fps_metrics=avg,0.01
### Display GPU throttling status based on Power, current, temp or "other"
## Only shows if throttling is currently happening
throttling_status
## Same as throttling_status but displays throttling on the frametime graph
#throttling_status_graph
### Display miscellaneous information
# engine_version
# engine_short_names
# gpu_name
# vulkan_driver
# wine
# exec_name
# winesync
### Display loaded MangoHud architecture
# arch
### Display the frametime line graph
frame_timing
# histogram
### Display GameMode / vkBasalt running status
# gamemode
# vkbasalt
### Gamescope related options
## Display the status of FSR (only works in gamescope)
# fsr
## Hides the sharpness info for the `fsr` option (only available in gamescope)
# hide_fsr_sharpness
## Shows the graph of gamescope app frametimes and latency (only on gamescope obviously)
# debug
## Display the status of HDR (only works in gamescope)
# hdr
## Display the current refresh rate (only works in gamescope)
# refresh_rate
### graphs displays one or more graphs that you chose
## seperated by ",", available graphs are
## gpu_load,cpu_load,gpu_core_clock,gpu_mem_clock,vram,ram,cpu_temp,gpu_temp
# graphs=
### mangoapp related options
## Enables mangoapp to be displayed above the Steam UI
# mangoapp_steam
### Steam Deck options
## Shows the Steam Deck fan rpm
# fan
### Display current FPS limit
# show_fps_limit
### Display the current resolution
# resolution
### Display custom text
# custom_text=
### Display output of Bash command in next column
# exec=
### Display media player metadata
# media_player
## for example spotify
# media_player_name=
## Format metadata, lines are delimited by ; (wip)
## example: {title};{artist};{album}
## example: Track:;{title};By:;{artist};From:;{album}
# media_player_format=title,artist,album
### Network interface throughput
# network
## Network can take arguments but it's not required.
## without arguments it shows all interfaces
## arguments set which interfaces will be displayed
# network=eth0,wlo1
### Change the hud font size
# font_size=24
# font_scale=1.0
# font_size_text=24
# font_scale_media_player=0.55
# no_small_font
### Change default font (set location to TTF/OTF file)
## Set font for the whole hud
# font_file=
## Set font only for text like media player metadata
# font_file_text=
## Set font glyph ranges. Defaults to Latin-only. Don't forget to set font_file/font_file_text to font that supports these
## Probably don't enable all at once because of memory usage and hardware limits concerns
## If you experience crashes or text is just squares, reduce glyph range or reduce font size
# font_glyph_ranges=korean,chinese,chinese_simplified,japanese,cyrillic,thai,vietnamese,latin_ext_a,latin_ext_b
### Outline text
text_outline
# text_outline_color = 000000
# text_outline_thickness = 1.5
### Change the hud position
# position=top-left
### Change the corner roundness
# round_corners=0
### Remove margins around MangoHud
# hud_no_margin
### Display compact version of MangoHud
# hud_compact
### Display MangoHud in a horizontal position
# horizontal
# horizontal_stretch
### Disable / hide the hud by default
# no_display
### Hud position offset
# offset_x=0
# offset_y=0
### Hud dimensions
# width=0
# height=140
# table_columns=3
# cellpadding_y=-0.085
### Hud transparency / alpha
# background_alpha=0.5
# alpha=1.0
### FCAT overlay
### This enables an FCAT overlay to perform frametime analysis on the final image stream.
### Enable the overlay
# fcat
### Set the width of the FCAT overlay.
### 24 is a performance optimization on AMD GPUs that should not have adverse effects on nVidia GPUs.
### A minimum of 20 pixels is recommended by nVidia.
# fcat_overlay_width=24
### Set the screen edge, this can be useful for special displays that don't update from top edge to bottom. This goes from 0 (left side) to 3 (top edge), counter-clockwise.
# fcat_screen_edge=0
### Color customization
# text_color=FFFFFF
# gpu_color=2E9762
# cpu_color=2E97CB
# vram_color=AD64C1
# ram_color=C26693
# engine_color=EB5B5B
# io_color=A491D3
# frametime_color=00FF00
# background_color=020202
# media_player_color=FFFFFF
# wine_color=EB5B5B
# battery_color=FF9078
### Specify GPU with PCI bus ID for AMDGPU and NVML stats
### Set to 'domain:bus:slot.function'
# pci_dev=0:0a:0.0
### Blacklist
# blacklist=
### Control over socket
### Enable and set socket name, '%p' is replaced with process id
## example: mangohud
## example: mangohud-%p
# control = -1
################ WORKAROUNDS #################
### Options starting with "gl_*" are for OpenGL
### Specify what to use for getting display size. Options are "viewport", "scissorbox" or disabled. Defaults to using glXQueryDrawable
# gl_size_query=viewport
### (Re)bind given framebuffer before MangoHud gets drawn. Helps with Crusader Kings III
# gl_bind_framebuffer=0
### Don't swap origin if using GL_UPPER_LEFT. Helps with Ryujinx
# gl_dont_flip=1
################ INTERACTION #################
### Change toggle keybinds for the hud & logging
# toggle_hud=Shift_R+F12
# toggle_hud_position=Shift_R+F11
# toggle_preset=Shift_R+F10
# toggle_fps_limit=Shift_L+F1
# toggle_logging=Shift_L+F2
# reload_cfg=Shift_L+F4
# upload_log=Shift_L+F3
#################### LOG #####################
### Automatically start the log after X seconds
# autostart_log=
### Set amount of time in seconds that the logging will run for
# log_duration=
### Change the default log interval, 0 is default
# log_interval=0
### Set location of the output files (required for logging)
# output_folder=/home/<USERNAME>/mangologs
### Permit uploading logs directly to FlightlessMango.com
## set to 1 to enable
# permit_upload=0
### Define a '+'-separated list of percentiles shown in the benchmark results
### Use "AVG" to get a mean average. Default percentiles are 97+AVG+1+0.1
## example: ['97', 'AVG', '1', '0.1']
# benchmark_percentiles=97,AVG
## Adds more headers and information such as versioning to the log. This format is not supported on flightlessmango.com (yet)
# log_versioning
## Enable automatic uploads of logs to flightlessmango.com
# upload_logs

@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<component type="generic">
<id>io.github.flightlessmango.mangohud</id>
<name>MangoHud</name>
<summary>Vulkan/OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more</summary>
<developer_name>FlightlessMango</developer_name>
<icon type="stock">io.github.flightlessmango.mangohud</icon>
<metadata_license>CC0-1.0</metadata_license>
<project_license>MIT</project_license>
<description>
<p>A modification of the Mesa Vulkan overlay, including GUI improvements with HUD configuration, temperature reporting, and logging capabilities. Includes a script (mangohud) to start it on any OpenGL or Vulkan application.</p>
</description>
<screenshots>
<screenshot type="default">
<caption>Example</caption>
<image>https://raw.githubusercontent.com/flightlessmango/MangoHud/master/assets/overlay_example.gif</image>
</screenshot>
<screenshot>
<caption>Log uploading walkthrough</caption>
<image>https://raw.githubusercontent.com/flightlessmango/MangoHud/master/assets/log_upload_example.gif</image>
</screenshot>
</screenshots>
<url type="homepage">https://github.com/flightlessmango/MangoHud</url>
<url type="bugtracker">https://github.com/flightlessmango/MangoHud/issues</url>
<url type="donation">https://www.paypal.me/flightlessmango</url>
<provides>
<binary>mangohud</binary>
</provides>
<categories>
<category>Utility</category>
<category>Game</category>
</categories>
<content_rating type="oars-1.1" />
</component>

@ -1,383 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="86.038002mm"
height="86.038002mm"
viewBox="-6.468 0 86.038221 86.038221"
version="1.1"
id="svg3290"
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
sodipodi:docname="io.github.flightlessmango.mangohud.svg">
<title
id="title5515">FlightlessMango Icon</title>
<defs
id="defs3284">
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2486">
<rect
style="opacity:1;stroke-width:0.75"
id="rect2488"
width="207.21774"
height="243.88824"
x="893.77185"
y="-713.32947"
transform="scale(1,-1)" />
</clipPath>
<clipPath
clipPathUnits="userSpaceOnUse"
id="clipPath2344">
<path
d="M 0,2000 H 2000 V 0 H 0 Z"
id="path2342" />
</clipPath>
</defs>
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="2.2547806"
inkscape:cx="98.679223"
inkscape:cy="162.32178"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
inkscape:document-rotation="0"
showgrid="false"
units="mm"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:window-height="1010"
inkscape:window-x="0"
inkscape:window-y="393"
inkscape:window-maximized="1"
inkscape:showpageshadow="2"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1" />
<metadata
id="metadata3287">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title>FlightlessMango Icon</dc:title>
<cc:license
rdf:resource="http://creativecommons.org/licenses/by/4.0/" />
<dc:date>2020-11-14</dc:date>
<dc:creator>
<cc:Agent>
<dc:title>FlightlessMango</dc:title>
</cc:Agent>
</dc:creator>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/licenses/by/4.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Notice" />
<cc:requires
rdf:resource="http://creativecommons.org/ns#Attribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-0.06554034,-0.07012108)">
<rect
style="display:inline;fill:#fffae3;fill-opacity:1;stroke-width:0.265336"
id="rect235"
width="86.038216"
height="86.038216"
x="-6.4024596"
y="0.07012108" />
<g
id="g2338"
clip-path="url(#clipPath2486)"
transform="matrix(0.35277777,0,0,-0.35277777,-315.2373,251.7169)">
<g
id="g2340"
clip-path="url(#clipPath2344)">
<g
id="g2346"
transform="translate(683.6049,1312.4586)">
<path
d="M 0,0 V -4.41 H -11.995 V -14.829 H 0 v -4.41 H -11.995 V -34.09 h -4.411 V 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2348" />
</g>
<path
d="m 715.713,1278.369 h -4.432 v 34.111 h 4.432 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2350" />
<path
d="m 748.695,1278.369 h -4.41 v 21.924 h 4.41 z m -0.022,30.042 c 0.611,-0.597 0.916,-1.328 0.916,-2.195 0,-0.867 -0.305,-1.598 -0.916,-2.195 -0.611,-0.596 -1.342,-0.894 -2.194,-0.894 -0.839,0 -1.563,0.298 -2.173,0.894 -0.611,0.597 -0.916,1.328 -0.916,2.195 0,0.867 0.305,1.598 0.916,2.195 0.61,0.596 1.334,0.895 2.173,0.895 0.852,0 1.583,-0.299 2.194,-0.895"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2352" />
<g
id="g2354"
transform="translate(796.2708,1291.6427)">
<path
d="m 0,0 c -0.27,1.349 -0.951,2.571 -2.045,3.665 -1.478,1.491 -3.267,2.237 -5.369,2.237 -2.103,0 -3.896,-0.746 -5.38,-2.237 -1.485,-1.492 -2.227,-3.281 -2.227,-5.37 0,-2.102 0.742,-3.895 2.227,-5.379 1.484,-1.485 3.277,-2.227 5.38,-2.227 2.102,0 3.891,0.739 5.369,2.216 1.094,1.094 1.775,2.301 2.045,3.622 z M 0.022,7.137 V 8.65 H 4.453 V -7.649 c 0,-4.261 -1.158,-7.549 -3.473,-9.864 -2.372,-2.089 -5.234,-3.133 -8.586,-3.133 -3.352,0 -6.215,1.044 -8.587,3.133 -0.483,0.483 -1.256,1.463 -2.322,2.94 l 4.389,1.576 c 0.568,-0.681 0.952,-1.121 1.15,-1.321 1.492,-1.477 3.282,-2.215 5.37,-2.215 3.508,0 5.937,2.301 7.287,6.903 -1.378,-2.671 -3.743,-4.006 -7.095,-4.006 -3.353,0 -6.211,1.151 -8.576,3.452 -2.365,2.301 -3.547,5.127 -3.547,8.479 0,3.353 1.182,6.211 3.547,8.576 2.365,2.365 5.223,3.548 8.576,3.548 2.826,0 5.305,-1.094 7.436,-3.282"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2356" />
</g>
<g
id="g2358"
transform="translate(829.0182,1312.4586)">
<path
d="m 0,0 h 4.432 v -14.531 c 1.235,2.372 3.025,3.558 5.369,3.558 2.344,0 4.215,-0.841 5.614,-2.524 1.399,-1.683 2.12,-3.796 2.163,-6.339 V -34.09 h -4.411 v 14.084 c 0,1.264 -0.358,2.347 -1.076,3.249 -0.717,0.902 -1.85,1.353 -3.398,1.353 -1.832,0 -3.253,-0.98 -4.261,-2.94 V -34.09 H 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2360" />
</g>
<g
id="g2362"
transform="translate(877.4686,1300.2928)">
<path
d="M 0,0 V 8.224 H 4.41 V 0 H 8.352 V -4.41 H 4.41 V -21.924 H 0 V -4.41 H -3.942 V 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2364" />
</g>
<path
d="m 917.204,1278.369 h -4.432 v 34.111 h 4.432 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2366" />
<g
id="g2368"
transform="translate(959.6033,1296.9692)">
<path
d="m 0,0 c -0.497,0.384 -1.613,0.575 -3.345,0.575 -2.088,0 -3.875,-0.742 -5.359,-2.226 -1.484,-1.485 -2.226,-3.278 -2.226,-5.38 0,-1.719 0.191,-2.834 0.575,-3.345 z m -7.372,-13.636 c 0.738,-0.654 2.08,-0.98 4.027,-0.98 2.102,0 3.899,0.739 5.39,2.216 0.185,0.199 0.561,0.639 1.129,1.321 l 4.411,-1.577 c -1.08,-1.491 -1.861,-2.479 -2.344,-2.961 -2.372,-2.373 -5.234,-3.558 -8.586,-3.558 -3.338,0 -6.193,1.185 -8.565,3.558 -2.373,2.372 -3.558,5.234 -3.558,8.586 0,3.352 1.185,6.214 3.558,8.587 2.372,2.371 5.227,3.557 8.565,3.557 2.869,0 5.383,-0.866 7.542,-2.599 C 4.552,2.216 4.9,1.896 5.241,1.556 5.383,1.413 5.787,0.966 6.456,0.213 L 4.24,-2.024 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2370" />
</g>
<g
id="g2372"
transform="translate(998.5509,1283.0776)">
<path
d="M 0,0 C 0.525,-0.511 1.151,-0.767 1.875,-0.767 2.6,-0.767 3.221,-0.511 3.74,0 4.258,0.512 4.517,1.143 4.517,1.896 4.517,2.621 4.261,3.239 3.75,3.75 3.395,4.105 2.635,4.652 1.47,5.39 c -1.591,0.867 -2.72,1.634 -3.388,2.301 -1.165,1.165 -1.746,2.564 -1.746,4.198 0,1.647 0.581,3.057 1.746,4.229 1.165,1.172 2.571,1.758 4.219,1.758 1.647,0 3.019,-0.554 4.113,-1.662 0.78,-0.781 1.278,-1.655 1.49,-2.621 L 3.622,12.358 c -0.1,0.354 -0.199,0.589 -0.298,0.703 -0.256,0.255 -0.597,0.383 -1.023,0.383 -0.426,0 -0.792,-0.152 -1.097,-0.458 -0.306,-0.305 -0.458,-0.671 -0.458,-1.097 0,-0.426 0.149,-0.788 0.447,-1.087 C 1.463,10.547 2.074,10.113 3.026,9.503 4.787,8.536 6.065,7.656 6.861,6.86 8.238,5.468 8.928,3.814 8.928,1.896 c 0,-1.96 -0.686,-3.632 -2.057,-5.017 -1.37,-1.385 -3.036,-2.078 -4.996,-2.078 -1.946,0 -3.608,0.696 -4.985,2.088 -0.953,0.952 -1.577,2.138 -1.876,3.559 l 4.39,0.937 C -0.468,0.732 -0.27,0.27 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2374" />
</g>
<g
id="g2376"
transform="translate(1039.1595,1283.0776)">
<path
d="m 0,0 c 0.525,-0.511 1.151,-0.767 1.875,-0.767 0.724,0 1.346,0.256 1.865,0.767 0.518,0.512 0.777,1.143 0.777,1.896 0,0.725 -0.256,1.343 -0.767,1.854 -0.355,0.355 -1.116,0.902 -2.28,1.64 -1.591,0.867 -2.72,1.634 -3.388,2.301 -1.165,1.165 -1.746,2.564 -1.746,4.198 0,1.647 0.581,3.057 1.746,4.229 1.165,1.172 2.571,1.758 4.219,1.758 1.647,0 3.019,-0.554 4.113,-1.662 0.78,-0.781 1.278,-1.655 1.49,-2.621 L 3.622,12.358 c -0.1,0.354 -0.199,0.589 -0.298,0.703 -0.256,0.255 -0.597,0.383 -1.023,0.383 -0.426,0 -0.792,-0.152 -1.097,-0.458 -0.306,-0.305 -0.458,-0.671 -0.458,-1.097 0,-0.426 0.149,-0.788 0.447,-1.087 C 1.463,10.547 2.074,10.113 3.026,9.503 4.787,8.536 6.065,7.656 6.861,6.86 8.238,5.468 8.928,3.814 8.928,1.896 c 0,-1.96 -0.686,-3.632 -2.057,-5.017 -1.37,-1.385 -3.036,-2.078 -4.996,-2.078 -1.946,0 -3.608,0.696 -4.985,2.088 -0.953,0.952 -1.577,2.138 -1.876,3.559 l 4.39,0.937 C -0.468,0.732 -0.27,0.27 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2378" />
</g>
<g
id="g2380"
transform="translate(1080.4086,1312.4586)">
<path
d="M 0,0 11.058,-19.111 22.072,0 H 26.1 V -34.09 H 21.69 V -9.46 L 11.058,-27.868 0.384,-9.46 V -34.09 H -4.027 V 0 Z"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2382" />
</g>
<g
id="g2384"
transform="translate(1154.7667,1288.2979)">
<path
d="m 0,0 v 3.239 c -0.256,1.377 -0.938,2.627 -2.046,3.749 -1.491,1.477 -3.281,2.216 -5.369,2.216 -2.102,0 -3.896,-0.739 -5.379,-2.216 -1.485,-1.477 -2.227,-3.267 -2.227,-5.369 0,-2.088 0.742,-3.874 2.227,-5.358 1.483,-1.485 3.277,-2.227 5.379,-2.227 2.088,0 3.878,0.738 5.369,2.216 1.108,1.122 1.79,2.372 2.046,3.75 m 0,10.461 v 1.534 H 4.432 V -9.95 H 0 v 2.748 c -2.131,-2.201 -4.603,-3.302 -7.415,-3.302 -3.352,0 -6.21,1.183 -8.576,3.547 -2.365,2.365 -3.547,5.224 -3.547,8.576 0,3.352 1.182,6.211 3.547,8.576 2.366,2.365 5.224,3.547 8.576,3.547 2.812,0 5.284,-1.094 7.415,-3.281"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2386" />
</g>
<g
id="g2388"
transform="translate(1191.8602,1294.4125)">
<path
d="M 0,0 V -16.043 H -4.432 V 5.88 H 0 V 3.324 c 0.923,2.499 2.777,3.749 5.561,3.749 2.372,0 4.257,-0.841 5.657,-2.524 1.399,-1.683 2.12,-3.796 2.162,-6.339 V -16.043 H 8.97 V -1.96 C 8.97,-0.696 8.508,0.387 7.585,1.289 6.661,2.191 5.561,2.642 4.282,2.642 3.018,2.642 1.925,2.202 1.001,1.321 0.49,0.796 0.156,0.355 0,0"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2390" />
</g>
<g
id="g2392"
transform="translate(1252.5187,1291.6427)">
<path
d="m 0,0 c -0.271,1.349 -0.952,2.571 -2.046,3.665 -1.477,1.491 -3.267,2.237 -5.369,2.237 -2.102,0 -3.896,-0.746 -5.379,-2.237 -1.485,-1.492 -2.227,-3.281 -2.227,-5.37 0,-2.102 0.742,-3.895 2.227,-5.379 1.483,-1.485 3.277,-2.227 5.379,-2.227 2.102,0 3.892,0.739 5.369,2.216 1.094,1.094 1.775,2.301 2.046,3.622 z M 0.021,7.137 V 8.65 H 4.453 V -7.649 c 0,-4.261 -1.158,-7.549 -3.473,-9.864 -2.372,-2.089 -5.235,-3.133 -8.586,-3.133 -3.353,0 -6.215,1.044 -8.587,3.133 -0.483,0.483 -1.257,1.463 -2.322,2.94 l 4.388,1.576 c 0.569,-0.681 0.952,-1.121 1.152,-1.321 1.491,-1.477 3.28,-2.215 5.369,-2.215 3.507,0 5.937,2.301 7.286,6.903 -1.378,-2.671 -3.743,-4.006 -7.095,-4.006 -3.352,0 -6.21,1.151 -8.576,3.452 -2.365,2.301 -3.547,5.127 -3.547,8.479 0,3.353 1.182,6.211 3.547,8.576 2.366,2.365 5.224,3.548 8.576,3.548 2.827,0 5.305,-1.094 7.436,-3.282"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2394" />
</g>
<g
id="g2396"
transform="translate(1290.7312,1295.3074)">
<path
d="m 0,0 c -1.484,-1.492 -2.226,-3.281 -2.226,-5.369 0,-2.102 0.742,-3.892 2.226,-5.369 1.484,-1.477 3.278,-2.216 5.38,-2.216 2.102,0 3.895,0.739 5.38,2.216 1.484,1.477 2.226,3.267 2.226,5.369 0,2.088 -0.742,3.877 -2.226,5.369 C 9.275,1.492 7.482,2.237 5.38,2.237 3.278,2.237 1.484,1.492 0,0 m 5.38,6.775 c 3.352,0 6.211,-1.186 8.575,-3.558 2.365,-2.372 3.548,-5.234 3.548,-8.586 0,-3.353 -1.183,-6.214 -3.548,-8.587 -2.364,-2.372 -5.223,-3.557 -8.575,-3.557 -3.338,0 -6.193,1.185 -8.565,3.557 -2.372,2.373 -3.558,5.234 -3.558,8.587 0,3.352 1.186,6.214 3.558,8.586 2.372,2.372 5.227,3.558 8.565,3.558"
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2398" />
</g>
<path
d="M 2000,0 H 5 v 983 h 1995 z"
style="fill:#fffae3;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2400" />
<g
id="g2402"
transform="translate(757.9102,452.8965)">
<path
d="m 0,0 v -7.977 h 6.042 v -3.947 H 0 V -24.251 H -4.271 V 3.947 H 7.533 V 0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2404" />
</g>
<g
id="g2406"
transform="translate(797.7944,432.6738)">
<path
d="M 0,0 V -4.028 H -11.4 V 24.17 h 4.27 L -7.13,0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2408" />
</g>
<path
d="m 818.666,456.844 h 4.27 v -28.198 h -4.27 z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2410" />
<g
id="g2412"
transform="translate(858.5898,450.4785)">
<path
d="m 0,0 v -1.934 l -4.109,-0.201 v 1.693 c 0,2.255 -0.846,3.262 -2.497,3.262 -1.652,0 -2.498,-1.007 -2.498,-2.86 v -15.388 c 0,-1.853 0.806,-2.86 2.458,-2.86 1.651,0 2.457,1.007 2.457,3.263 v 4.834 h -2.417 v 3.948 H 0 v -9.226 c 0,-4.269 -2.457,-6.767 -6.728,-6.767 -4.229,0 -6.646,2.498 -6.646,6.767 V 0 c 0,4.271 2.417,6.768 6.646,6.768 C -2.457,6.768 0,4.271 0,0"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2414" />
</g>
<g
id="g2416"
transform="translate(890.0957,428.6455)">
<path
d="M 0,0 V 12.286 H -5.075 V 0 h -4.271 v 28.198 h 4.271 V 16.314 H 0 V 28.198 H 4.271 V 0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2418" />
</g>
<g
id="g2420"
transform="translate(924.0195,452.8154)">
<path
d="M 0,0 V -24.17 H -4.271 V 0 H -8.822 V 4.028 H 4.552 V 0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2422" />
</g>
<g
id="g2424"
transform="translate(960.8022,432.6738)">
<path
d="M 0,0 V -4.028 H -11.4 V 24.17 h 4.27 L -7.13,0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2426" />
</g>
<g
id="g2428"
transform="translate(985.9434,452.8965)">
<path
d="m 0,0 v -7.977 h 6.244 v -3.947 H 0 v -8.379 h 7.734 v -3.948 H -4.27 V 3.947 H 7.734 V 0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2430" />
</g>
<g
id="g2432"
transform="translate(1026.7549,450.9219)">
<path
d="m 0,0 v -0.725 l -4.109,-0.403 v 1.048 c 0,1.611 -0.766,2.457 -2.175,2.457 -1.41,0 -2.176,-0.846 -2.176,-2.457 v -0.363 c 0,-1.652 0.726,-3.061 3.585,-5.479 3.908,-3.303 5.358,-5.639 5.358,-9.023 v -1.409 c 0,-4.029 -2.377,-6.326 -6.606,-6.326 -4.231,0 -6.606,2.297 -6.606,6.326 v 1.893 l 4.108,0.402 v -2.013 c 0,-1.774 0.806,-2.659 2.417,-2.659 1.611,0 2.417,0.885 2.417,2.659 v 0.926 c 0,1.772 -0.846,3.182 -3.706,5.599 -3.907,3.303 -5.236,5.64 -5.236,8.943 V 0 c 0,4.028 2.295,6.324 6.364,6.324 C -2.297,6.324 0,4.028 0,0"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2434" />
</g>
<g
id="g2436"
transform="translate(1059.3076,450.9219)">
<path
d="m 0,0 v -0.725 l -4.109,-0.403 v 1.048 c 0,1.611 -0.766,2.457 -2.175,2.457 -1.41,0 -2.176,-0.846 -2.176,-2.457 v -0.363 c 0,-1.652 0.726,-3.061 3.585,-5.479 3.908,-3.303 5.358,-5.639 5.358,-9.023 v -1.409 c 0,-4.029 -2.377,-6.326 -6.606,-6.326 -4.231,0 -6.606,2.297 -6.606,6.326 v 1.893 l 4.108,0.402 v -2.013 c 0,-1.774 0.806,-2.659 2.417,-2.659 1.611,0 2.417,0.885 2.417,2.659 v 0.926 c 0,1.772 -0.846,3.182 -3.706,5.599 -3.907,3.303 -5.236,5.64 -5.236,8.943 V 0 c 0,4.028 2.295,6.324 6.364,6.324 C -2.297,6.324 0,4.028 0,0"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2438" />
</g>
<g
id="g2440"
transform="translate(1094.3184,428.6455)">
<path
d="M 0,0 V 19.577 H -0.081 L -3.626,4.028 H -6.163 L -9.708,19.577 H -9.789 V 0 h -4.028 v 28.198 h 5.599 l 3.344,-15.146 h 0.08 L -1.49,28.198 H 4.271 V 0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2442" />
</g>
<g
id="g2444"
transform="translate(1125.542,438.918)">
<path
d="M 0,0 H 3.586 L 1.854,11.883 H 1.732 Z m 5.076,-10.272 -0.927,6.405 h -4.712 l -0.927,-6.405 h -4.27 l 4.874,28.198 h 5.439 l 4.874,-28.198 z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2446" />
</g>
<g
id="g2448"
transform="translate(1165.8701,428.6455)">
<path
d="M 0,0 -5.599,18.691 H -5.68 V 0 h -4.028 v 28.198 h 4.754 L 0.081,11.4 H 0.162 V 28.198 H 4.189 V 0 Z"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2450" />
</g>
<g
id="g2452"
transform="translate(1205.7158,450.4785)">
<path
d="m 0,0 v -1.934 l -4.108,-0.201 v 1.693 c 0,2.255 -0.846,3.262 -2.497,3.262 -1.653,0 -2.499,-1.007 -2.499,-2.86 v -15.388 c 0,-1.853 0.806,-2.86 2.458,-2.86 1.652,0 2.457,1.007 2.457,3.263 v 4.834 h -2.416 v 3.948 H 0 v -9.226 c 0,-4.269 -2.457,-6.767 -6.727,-6.767 -4.23,0 -6.646,2.498 -6.646,6.767 V 0 c 0,4.271 2.416,6.768 6.646,6.768 C -2.457,6.768 0,4.271 0,0"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2454" />
</g>
<g
id="g2456"
transform="translate(1231.7031,450.0762)">
<path
d="m 0,0 v -14.663 c 0,-2.095 0.927,-3.223 2.618,-3.223 1.693,0 2.619,1.128 2.619,3.223 V 0 C 5.237,2.095 4.311,3.223 2.618,3.223 0.927,3.223 0,2.095 0,0 m 9.507,0.04 v -14.743 c 0,-4.432 -2.618,-7.131 -6.889,-7.131 -4.269,0 -6.889,2.699 -6.889,7.131 V 0.04 c 0,4.432 2.62,7.13 6.889,7.13 4.271,0 6.889,-2.698 6.889,-7.13"
style="fill:#414042;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2458" />
</g>
<g
id="g2460"
transform="translate(1100.9401,661.5807)">
<path
d="m 0,0 c 0,0 -31.4,46.786 -77.244,51.496 0,0 -86.911,9.978 -106.761,-96.712 -3.373,-18.131 -3.024,-34.734 0.124,-49.549 9.467,-10.111 23.255,-20.105 40.687,-27.366 19.155,-7.978 26.834,-21.155 29.838,-31.678 18.644,26.846 1.258,68.715 1.258,68.715 19.468,-6.28 27.318,-35.796 27.318,-35.796 10.362,11.304 19.154,43.332 -0.314,67.196 -14.232,17.446 -13.816,42.076 -11.618,51.496 3.172,13.595 15.085,25.277 29.045,25.277 13.96,0 25.277,-11.317 25.277,-25.277 0,-8.236 -7.629,-15.162 -12.234,-21.266 -21.443,-28.425 6.896,-39.65 8.78,-69.48 4.359,-69.014 -50.868,-64.057 -50.868,-64.057 -1.587,-5.818 -8.659,-12.973 -10.85,-15.085 0.003,0 0.007,-10e-4 0.011,-0.002 0.065,0.043 0.103,0.066 0.103,0.066 l 5.242,-0.849 c 26.536,-3 53.043,7.333 68.922,34.083 C 7.536,-70.022 -91.06,-52.124 0,0"
style="fill:#f7941e;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2462" />
</g>
<g
id="g2464"
transform="translate(1038.7678,651.5327)">
<path
d="m 0,0 c 5.116,0 9.263,4.147 9.263,9.263 0,1.321 -0.281,2.575 -0.779,3.712 C 7.875,12.379 7.042,12.01 6.123,12.01 c -1.864,0 -3.375,1.512 -3.375,3.376 0,0.919 0.368,1.752 0.964,2.361 C 2.575,18.245 1.321,18.526 0,18.526 -5.116,18.526 -9.263,14.379 -9.263,9.263 -9.263,4.147 -5.116,0 0,0"
style="fill:#f7941e;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2466" />
</g>
<g
id="g2468"
transform="translate(993.389,489.493)">
<path
d="m 0,0 c -0.013,-0.008 -0.028,-0.019 -0.043,-0.029 -0.151,-0.1 -0.411,-0.281 -0.75,-0.549 -1.987,-1.572 -6.576,-6.075 -6.361,-14.016 0.273,-10.12 8.456,-4.177 7.895,1.106 -0.561,5.282 5.368,12.581 5.368,12.581 L 5.346,-0.783 0.103,0.066 C 0.103,0.066 0.065,0.043 0,0"
style="fill:#74a643;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2470" />
</g>
<g
id="g2472"
transform="translate(987.5845,507.7722)">
<path
d="m 0,0 c -3.004,10.522 -10.684,23.7 -29.839,31.678 -17.432,7.26 -31.219,17.254 -40.686,27.365 -9.959,10.637 -15.137,21.403 -14.738,29.25 0,0 -26.008,-65.675 13.438,-101.405 34.576,-31.319 71.326,-7.651 71.326,-7.651 0,0 -45.521,6.117 -65.664,49.919 0,0 24.448,-30.526 52.688,-41.624 4.976,-1.956 10.07,-3.313 15.169,-3.789 0,0 0.912,7.13 -1.694,16.257"
style="fill:#74a643;fill-opacity:1;fill-rule:nonzero;stroke:none"
id="path2474" />
</g>
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 22 KiB

@ -1,39 +0,0 @@
.\" Manpage for mangoapp.
.TH mangoapp 1 "" "" "mangoapp"
.SH NAME
mangoapp \- transparent background application with a built in mangohud
.SH SYNOPSIS
\fBmangoapp\fR
.SH DESCRIPTION
MangoHud is a Vulkan/OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more.
.PP
Mangoapp is a transparent background opengl application with a built in MangoHud. It's designed to be run inside a
gamescope instance which will display mangoapp ontop of gamescopes output. It also takes frame information from
gamescope. The purpose of this is to "easily" make MangoHud compatible with any application, or at least any
application that gamescope can run. This solves issues with some OpenGL games and certain ports that have stdc++ issues
as it's no longer directly injected into the game.
.SH USAGE
Create a script (e.g. \fBrun.sh\fR) containing the app you want to run (e.g. \fBvkcube\fR) and \fBmangoapp\fR like so:
.PP
.RS 4
.EX
#!/bin/sh
vkcube&
mangoapp
.EE
.RE
.PP
And then run it with \fBgamescope ./run.sh\fR.
.SH SEE ALSO
mangohud(1)
.SH ABOUT
MangoHud development takes place at \fIhttps://github.com/flightlessmango/MangoHud\fR.
.br
Benchmarks created with MangoHud can be uploaded to \fIhttps://flightlessmango.com\fR.

@ -1,53 +0,0 @@
.\" Manpage for mangohud.
.TH mangohud 1 "" "" "mangohud"
.SH NAME
mangohud \- enable MangoHud on any application
.SH SYNOPSIS
\fBmangohud\fR [--dlsym] COMMAND
.SH DESCRIPTION
MangoHud is a Vulkan/OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more.
.SH USAGE
MangoHud can be enabled for Vulkan applications by setting \fBMANGOHUD=1\fR as environment variable.
.br
To load MangoHud for any application, including OpenGL applications, the \fBmangohud\fR executable can be used. It preloads a library via ld into the application.
.br
Note: some OpenGL applications may also need dlsym hooking. This can be done by passing option \fB--dlsym\fR or by setting \fBMANGOHUD_DLSYM=1\fR as envrionment variable.
.SH CONFIG
MangoHud comes with a config file which can be used to set configuration options globally or per application. The priorities of different config files are:
.LP
.RS 4
/path/to/application/MangoHud.conf
.br
$XDG_CONFIG_HOME/MangoHud/{application_name}.conf
.br
$XDG_CONFIG_HOME/MangoHud/MangoHud.conf
.RS -4
.LP
An example config file is located in /usr/share/doc/mangohud/MangoHud.conf, containing all available options.
.LP
A custom config file location can also be specified with the \fBMANGOHUD_CONFIGFILE\fR environment variable.
.br
Config options can also be set with the \fBMANGOHUD_CONFIG\fR environment variable. This takes priority over any config file.
.SH EXAMPLES
OpenGL: \fBmangohud glxgears\fR
.br
Vulkan: \fBMANGOHUD=1 vkcube\fR
.br
Steam: set your launch option to \fBmangohud %command%\fR
.br
Lutris: add \fBmangohud\fR to the Command prefix setting
.br
OpenGL with dlsym: \fBmangohud --dlsym glxgears\fR
.br
Custom config options: \fBMANGOHUD_CONFIG="gpu_stats=0,font_size=12" mangohud glxgears\fR
.SH ABOUT
MangoHud development takes place at \fIhttps://github.com/flightlessmango/MangoHud\fR.
.br
Benchmarks created with MangoHud can be uploaded to \fIhttps://flightlessmango.com\fR.

@ -1,56 +0,0 @@
man1dir = join_paths(get_option('mandir'), 'man1')
datadir = get_option('datadir')
metainfo_file = files('io.github.flightlessmango.mangohud.metainfo.xml')
icon_file = files('io.github.flightlessmango.mangohud.svg')
# Validate metainfo file
ascli_exe = find_program('appstreamcli', required: get_option('tests'))
if ascli_exe.found()
test('validate metainfo file',
ascli_exe,
args: ['validate',
'--no-net',
'--pedantic',
metainfo_file]
)
endif
# Install metainfo file
install_data(
metainfo_file,
install_dir: join_paths(datadir, 'metainfo'),
install_tag : 'doc',
)
# Install icon for metainfo
install_data(
icon_file,
install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps'),
install_tag : 'doc',
)
# Install man pages
install_man(
files('mangohud.1'),
install_dir: man1dir,
)
if get_option('mangoapp')
install_man(
files('mangoapp.1'),
install_dir: man1dir,
)
endif
install_data(
files('MangoHud.conf'),
install_dir : join_paths(get_option('datadir'), 'doc', 'mangohud'),
rename : ['MangoHud.conf.example'],
install_tag : 'doc',
)
install_data(
files('presets.conf'),
install_dir : join_paths(get_option('datadir'), 'doc', 'mangohud'),
rename : ['presets.conf.example'],
install_tag : 'doc',
)

@ -1,10 +0,0 @@
[preset 1]
no_display
[preset 2]
legacy_layout=0
cpu_stats=0
gpu_stats=0
fps
fps_only=1
frametime=0

@ -1,805 +0,0 @@
// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py for languages C and C++
// from https://raw.githubusercontent.com/ForkAwesome/Fork-Awesome/master/src/icons/icons.yml
// for use with https://github.com/ForkAwesome/Fork-Awesome/blob/master/fonts/forkawesome-webfont.ttf
#pragma once
#define FONT_ICON_FILE_NAME_FK "forkawesome-webfont.ttf"
#define ICON_MIN_FK 0xf000
#define ICON_MAX_FK 0xf35f
#define ICON_FK_GLASS "\xef\x80\x80" // U+f000
#define ICON_FK_MUSIC "\xef\x80\x81" // U+f001
#define ICON_FK_SEARCH "\xef\x80\x82" // U+f002
#define ICON_FK_ENVELOPE_O "\xef\x80\x83" // U+f003
#define ICON_FK_HEART "\xef\x80\x84" // U+f004
#define ICON_FK_STAR "\xef\x80\x85" // U+f005
#define ICON_FK_STAR_O "\xef\x80\x86" // U+f006
#define ICON_FK_USER "\xef\x80\x87" // U+f007
#define ICON_FK_FILM "\xef\x80\x88" // U+f008
#define ICON_FK_TH_LARGE "\xef\x80\x89" // U+f009
#define ICON_FK_TH "\xef\x80\x8a" // U+f00a
#define ICON_FK_TH_LIST "\xef\x80\x8b" // U+f00b
#define ICON_FK_CHECK "\xef\x80\x8c" // U+f00c
#define ICON_FK_TIMES "\xef\x80\x8d" // U+f00d
#define ICON_FK_SEARCH_PLUS "\xef\x80\x8e" // U+f00e
#define ICON_FK_SEARCH_MINUS "\xef\x80\x90" // U+f010
#define ICON_FK_POWER_OFF "\xef\x80\x91" // U+f011
#define ICON_FK_SIGNAL "\xef\x80\x92" // U+f012
#define ICON_FK_COG "\xef\x80\x93" // U+f013
#define ICON_FK_TRASH_O "\xef\x80\x94" // U+f014
#define ICON_FK_HOME "\xef\x80\x95" // U+f015
#define ICON_FK_FILE_O "\xef\x80\x96" // U+f016
#define ICON_FK_CLOCK_O "\xef\x80\x97" // U+f017
#define ICON_FK_ROAD "\xef\x80\x98" // U+f018
#define ICON_FK_DOWNLOAD "\xef\x80\x99" // U+f019
#define ICON_FK_ARROW_CIRCLE_O_DOWN "\xef\x80\x9a" // U+f01a
#define ICON_FK_ARROW_CIRCLE_O_UP "\xef\x80\x9b" // U+f01b
#define ICON_FK_INBOX "\xef\x80\x9c" // U+f01c
#define ICON_FK_PLAY_CIRCLE_O "\xef\x80\x9d" // U+f01d
#define ICON_FK_REPEAT "\xef\x80\x9e" // U+f01e
#define ICON_FK_REFRESH "\xef\x80\xa1" // U+f021
#define ICON_FK_LIST_ALT "\xef\x80\xa2" // U+f022
#define ICON_FK_LOCK "\xef\x80\xa3" // U+f023
#define ICON_FK_FLAG "\xef\x80\xa4" // U+f024
#define ICON_FK_HEADPHONES "\xef\x80\xa5" // U+f025
#define ICON_FK_VOLUME_OFF "\xef\x80\xa6" // U+f026
#define ICON_FK_VOLUME_DOWN "\xef\x80\xa7" // U+f027
#define ICON_FK_VOLUME_UP "\xef\x80\xa8" // U+f028
#define ICON_FK_QRCODE "\xef\x80\xa9" // U+f029
#define ICON_FK_BARCODE "\xef\x80\xaa" // U+f02a
#define ICON_FK_TAG "\xef\x80\xab" // U+f02b
#define ICON_FK_TAGS "\xef\x80\xac" // U+f02c
#define ICON_FK_BOOK "\xef\x80\xad" // U+f02d
#define ICON_FK_BOOKMARK "\xef\x80\xae" // U+f02e
#define ICON_FK_PRINT "\xef\x80\xaf" // U+f02f
#define ICON_FK_CAMERA "\xef\x80\xb0" // U+f030
#define ICON_FK_FONT "\xef\x80\xb1" // U+f031
#define ICON_FK_BOLD "\xef\x80\xb2" // U+f032
#define ICON_FK_ITALIC "\xef\x80\xb3" // U+f033
#define ICON_FK_TEXT_HEIGHT "\xef\x80\xb4" // U+f034
#define ICON_FK_TEXT_WIDTH "\xef\x80\xb5" // U+f035
#define ICON_FK_ALIGN_LEFT "\xef\x80\xb6" // U+f036
#define ICON_FK_ALIGN_CENTER "\xef\x80\xb7" // U+f037
#define ICON_FK_ALIGN_RIGHT "\xef\x80\xb8" // U+f038
#define ICON_FK_ALIGN_JUSTIFY "\xef\x80\xb9" // U+f039
#define ICON_FK_LIST "\xef\x80\xba" // U+f03a
#define ICON_FK_OUTDENT "\xef\x80\xbb" // U+f03b
#define ICON_FK_INDENT "\xef\x80\xbc" // U+f03c
#define ICON_FK_VIDEO_CAMERA "\xef\x80\xbd" // U+f03d
#define ICON_FK_PICTURE_O "\xef\x80\xbe" // U+f03e
#define ICON_FK_PENCIL "\xef\x81\x80" // U+f040
#define ICON_FK_MAP_MARKER "\xef\x81\x81" // U+f041
#define ICON_FK_ADJUST "\xef\x81\x82" // U+f042
#define ICON_FK_TINT "\xef\x81\x83" // U+f043
#define ICON_FK_PENCIL_SQUARE_O "\xef\x81\x84" // U+f044
#define ICON_FK_SHARE_SQUARE_O "\xef\x81\x85" // U+f045
#define ICON_FK_CHECK_SQUARE_O "\xef\x81\x86" // U+f046
#define ICON_FK_ARROWS "\xef\x81\x87" // U+f047
#define ICON_FK_STEP_BACKWARD "\xef\x81\x88" // U+f048
#define ICON_FK_FAST_BACKWARD "\xef\x81\x89" // U+f049
#define ICON_FK_BACKWARD "\xef\x81\x8a" // U+f04a
#define ICON_FK_PLAY "\xef\x81\x8b" // U+f04b
#define ICON_FK_PAUSE "\xef\x81\x8c" // U+f04c
#define ICON_FK_STOP "\xef\x81\x8d" // U+f04d
#define ICON_FK_FORWARD "\xef\x81\x8e" // U+f04e
#define ICON_FK_FAST_FORWARD "\xef\x81\x90" // U+f050
#define ICON_FK_STEP_FORWARD "\xef\x81\x91" // U+f051
#define ICON_FK_EJECT "\xef\x81\x92" // U+f052
#define ICON_FK_CHEVRON_LEFT "\xef\x81\x93" // U+f053
#define ICON_FK_CHEVRON_RIGHT "\xef\x81\x94" // U+f054
#define ICON_FK_PLUS_CIRCLE "\xef\x81\x95" // U+f055
#define ICON_FK_MINUS_CIRCLE "\xef\x81\x96" // U+f056
#define ICON_FK_TIMES_CIRCLE "\xef\x81\x97" // U+f057
#define ICON_FK_CHECK_CIRCLE "\xef\x81\x98" // U+f058
#define ICON_FK_QUESTION_CIRCLE "\xef\x81\x99" // U+f059
#define ICON_FK_INFO_CIRCLE "\xef\x81\x9a" // U+f05a
#define ICON_FK_CROSSHAIRS "\xef\x81\x9b" // U+f05b
#define ICON_FK_TIMES_CIRCLE_O "\xef\x81\x9c" // U+f05c
#define ICON_FK_CHECK_CIRCLE_O "\xef\x81\x9d" // U+f05d
#define ICON_FK_BAN "\xef\x81\x9e" // U+f05e
#define ICON_FK_ARROW_LEFT "\xef\x81\xa0" // U+f060
#define ICON_FK_ARROW_RIGHT "\xef\x81\xa1" // U+f061
#define ICON_FK_ARROW_UP "\xef\x81\xa2" // U+f062
#define ICON_FK_ARROW_DOWN "\xef\x81\xa3" // U+f063
#define ICON_FK_SHARE "\xef\x81\xa4" // U+f064
#define ICON_FK_EXPAND "\xef\x81\xa5" // U+f065
#define ICON_FK_COMPRESS "\xef\x81\xa6" // U+f066
#define ICON_FK_PLUS "\xef\x81\xa7" // U+f067
#define ICON_FK_MINUS "\xef\x81\xa8" // U+f068
#define ICON_FK_ASTERISK "\xef\x81\xa9" // U+f069
#define ICON_FK_EXCLAMATION_CIRCLE "\xef\x81\xaa" // U+f06a
#define ICON_FK_GIFT "\xef\x81\xab" // U+f06b
#define ICON_FK_LEAF "\xef\x81\xac" // U+f06c
#define ICON_FK_FIRE "\xef\x81\xad" // U+f06d
#define ICON_FK_EYE "\xef\x81\xae" // U+f06e
#define ICON_FK_EYE_SLASH "\xef\x81\xb0" // U+f070
#define ICON_FK_EXCLAMATION_TRIANGLE "\xef\x81\xb1" // U+f071
#define ICON_FK_PLANE "\xef\x81\xb2" // U+f072
#define ICON_FK_CALENDAR "\xef\x81\xb3" // U+f073
#define ICON_FK_RANDOM "\xef\x81\xb4" // U+f074
#define ICON_FK_COMMENT "\xef\x81\xb5" // U+f075
#define ICON_FK_MAGNET "\xef\x81\xb6" // U+f076
#define ICON_FK_CHEVRON_UP "\xef\x81\xb7" // U+f077
#define ICON_FK_CHEVRON_DOWN "\xef\x81\xb8" // U+f078
#define ICON_FK_RETWEET "\xef\x81\xb9" // U+f079
#define ICON_FK_SHOPPING_CART "\xef\x81\xba" // U+f07a
#define ICON_FK_FOLDER "\xef\x81\xbb" // U+f07b
#define ICON_FK_FOLDER_OPEN "\xef\x81\xbc" // U+f07c
#define ICON_FK_ARROWS_V "\xef\x81\xbd" // U+f07d
#define ICON_FK_ARROWS_H "\xef\x81\xbe" // U+f07e
#define ICON_FK_BAR_CHART "\xef\x82\x80" // U+f080
#define ICON_FK_TWITTER_SQUARE "\xef\x82\x81" // U+f081
#define ICON_FK_FACEBOOK_SQUARE "\xef\x82\x82" // U+f082
#define ICON_FK_CAMERA_RETRO "\xef\x82\x83" // U+f083
#define ICON_FK_KEY "\xef\x82\x84" // U+f084
#define ICON_FK_COGS "\xef\x82\x85" // U+f085
#define ICON_FK_COMMENTS "\xef\x82\x86" // U+f086
#define ICON_FK_THUMBS_O_UP "\xef\x82\x87" // U+f087
#define ICON_FK_THUMBS_O_DOWN "\xef\x82\x88" // U+f088
#define ICON_FK_STAR_HALF "\xef\x82\x89" // U+f089
#define ICON_FK_HEART_O "\xef\x82\x8a" // U+f08a
#define ICON_FK_SIGN_OUT "\xef\x82\x8b" // U+f08b
#define ICON_FK_LINKEDIN_SQUARE "\xef\x82\x8c" // U+f08c
#define ICON_FK_THUMB_TACK "\xef\x82\x8d" // U+f08d
#define ICON_FK_EXTERNAL_LINK "\xef\x82\x8e" // U+f08e
#define ICON_FK_SIGN_IN "\xef\x82\x90" // U+f090
#define ICON_FK_TROPHY "\xef\x82\x91" // U+f091
#define ICON_FK_GITHUB_SQUARE "\xef\x82\x92" // U+f092
#define ICON_FK_UPLOAD "\xef\x82\x93" // U+f093
#define ICON_FK_LEMON_O "\xef\x82\x94" // U+f094
#define ICON_FK_PHONE "\xef\x82\x95" // U+f095
#define ICON_FK_SQUARE_O "\xef\x82\x96" // U+f096
#define ICON_FK_BOOKMARK_O "\xef\x82\x97" // U+f097
#define ICON_FK_PHONE_SQUARE "\xef\x82\x98" // U+f098
#define ICON_FK_TWITTER "\xef\x82\x99" // U+f099
#define ICON_FK_FACEBOOK "\xef\x82\x9a" // U+f09a
#define ICON_FK_GITHUB "\xef\x82\x9b" // U+f09b
#define ICON_FK_UNLOCK "\xef\x82\x9c" // U+f09c
#define ICON_FK_CREDIT_CARD "\xef\x82\x9d" // U+f09d
#define ICON_FK_RSS "\xef\x82\x9e" // U+f09e
#define ICON_FK_HDD_O "\xef\x82\xa0" // U+f0a0
#define ICON_FK_BULLHORN "\xef\x82\xa1" // U+f0a1
#define ICON_FK_BELL_O "\xef\x83\xb3" // U+f0f3
#define ICON_FK_CERTIFICATE "\xef\x82\xa3" // U+f0a3
#define ICON_FK_HAND_O_RIGHT "\xef\x82\xa4" // U+f0a4
#define ICON_FK_HAND_O_LEFT "\xef\x82\xa5" // U+f0a5
#define ICON_FK_HAND_O_UP "\xef\x82\xa6" // U+f0a6
#define ICON_FK_HAND_O_DOWN "\xef\x82\xa7" // U+f0a7
#define ICON_FK_ARROW_CIRCLE_LEFT "\xef\x82\xa8" // U+f0a8
#define ICON_FK_ARROW_CIRCLE_RIGHT "\xef\x82\xa9" // U+f0a9
#define ICON_FK_ARROW_CIRCLE_UP "\xef\x82\xaa" // U+f0aa
#define ICON_FK_ARROW_CIRCLE_DOWN "\xef\x82\xab" // U+f0ab
#define ICON_FK_GLOBE "\xef\x82\xac" // U+f0ac
#define ICON_FK_GLOBE_E "\xef\x8c\x84" // U+f304
#define ICON_FK_GLOBE_W "\xef\x8c\x85" // U+f305
#define ICON_FK_WRENCH "\xef\x82\xad" // U+f0ad
#define ICON_FK_TASKS "\xef\x82\xae" // U+f0ae
#define ICON_FK_FILTER "\xef\x82\xb0" // U+f0b0
#define ICON_FK_BRIEFCASE "\xef\x82\xb1" // U+f0b1
#define ICON_FK_ARROWS_ALT "\xef\x82\xb2" // U+f0b2
#define ICON_FK_USERS "\xef\x83\x80" // U+f0c0
#define ICON_FK_LINK "\xef\x83\x81" // U+f0c1
#define ICON_FK_CLOUD "\xef\x83\x82" // U+f0c2
#define ICON_FK_FLASK "\xef\x83\x83" // U+f0c3
#define ICON_FK_SCISSORS "\xef\x83\x84" // U+f0c4
#define ICON_FK_FILES_O "\xef\x83\x85" // U+f0c5
#define ICON_FK_PAPERCLIP "\xef\x83\x86" // U+f0c6
#define ICON_FK_FLOPPY_O "\xef\x83\x87" // U+f0c7
#define ICON_FK_SQUARE "\xef\x83\x88" // U+f0c8
#define ICON_FK_BARS "\xef\x83\x89" // U+f0c9
#define ICON_FK_LIST_UL "\xef\x83\x8a" // U+f0ca
#define ICON_FK_LIST_OL "\xef\x83\x8b" // U+f0cb
#define ICON_FK_STRIKETHROUGH "\xef\x83\x8c" // U+f0cc
#define ICON_FK_UNDERLINE "\xef\x83\x8d" // U+f0cd
#define ICON_FK_TABLE "\xef\x83\x8e" // U+f0ce
#define ICON_FK_MAGIC "\xef\x83\x90" // U+f0d0
#define ICON_FK_TRUCK "\xef\x83\x91" // U+f0d1
#define ICON_FK_PINTEREST "\xef\x83\x92" // U+f0d2
#define ICON_FK_PINTEREST_SQUARE "\xef\x83\x93" // U+f0d3
#define ICON_FK_GOOGLE_PLUS_SQUARE "\xef\x83\x94" // U+f0d4
#define ICON_FK_GOOGLE_PLUS "\xef\x83\x95" // U+f0d5
#define ICON_FK_MONEY "\xef\x83\x96" // U+f0d6
#define ICON_FK_CARET_DOWN "\xef\x83\x97" // U+f0d7
#define ICON_FK_CARET_UP "\xef\x83\x98" // U+f0d8
#define ICON_FK_CARET_LEFT "\xef\x83\x99" // U+f0d9
#define ICON_FK_CARET_RIGHT "\xef\x83\x9a" // U+f0da
#define ICON_FK_COLUMNS "\xef\x83\x9b" // U+f0db
#define ICON_FK_SORT "\xef\x83\x9c" // U+f0dc
#define ICON_FK_SORT_DESC "\xef\x83\x9d" // U+f0dd
#define ICON_FK_SORT_ASC "\xef\x83\x9e" // U+f0de
#define ICON_FK_ENVELOPE "\xef\x83\xa0" // U+f0e0
#define ICON_FK_LINKEDIN "\xef\x83\xa1" // U+f0e1
#define ICON_FK_UNDO "\xef\x83\xa2" // U+f0e2
#define ICON_FK_GAVEL "\xef\x83\xa3" // U+f0e3
#define ICON_FK_TACHOMETER "\xef\x83\xa4" // U+f0e4
#define ICON_FK_COMMENT_O "\xef\x83\xa5" // U+f0e5
#define ICON_FK_COMMENTS_O "\xef\x83\xa6" // U+f0e6
#define ICON_FK_BOLT "\xef\x83\xa7" // U+f0e7
#define ICON_FK_SITEMAP "\xef\x83\xa8" // U+f0e8
#define ICON_FK_UMBRELLA "\xef\x83\xa9" // U+f0e9
#define ICON_FK_CLIPBOARD "\xef\x83\xaa" // U+f0ea
#define ICON_FK_LIGHTBULB_O "\xef\x83\xab" // U+f0eb
#define ICON_FK_EXCHANGE "\xef\x83\xac" // U+f0ec
#define ICON_FK_CLOUD_DOWNLOAD "\xef\x83\xad" // U+f0ed
#define ICON_FK_CLOUD_UPLOAD "\xef\x83\xae" // U+f0ee
#define ICON_FK_USER_MD "\xef\x83\xb0" // U+f0f0
#define ICON_FK_STETHOSCOPE "\xef\x83\xb1" // U+f0f1
#define ICON_FK_SUITCASE "\xef\x83\xb2" // U+f0f2
#define ICON_FK_BELL "\xef\x82\xa2" // U+f0a2
#define ICON_FK_COFFEE "\xef\x83\xb4" // U+f0f4
#define ICON_FK_CUTLERY "\xef\x83\xb5" // U+f0f5
#define ICON_FK_FILE_TEXT_O "\xef\x83\xb6" // U+f0f6
#define ICON_FK_BUILDING_O "\xef\x83\xb7" // U+f0f7
#define ICON_FK_HOSPITAL_O "\xef\x83\xb8" // U+f0f8
#define ICON_FK_AMBULANCE "\xef\x83\xb9" // U+f0f9
#define ICON_FK_MEDKIT "\xef\x83\xba" // U+f0fa
#define ICON_FK_FIGHTER_JET "\xef\x83\xbb" // U+f0fb
#define ICON_FK_BEER "\xef\x83\xbc" // U+f0fc
#define ICON_FK_H_SQUARE "\xef\x83\xbd" // U+f0fd
#define ICON_FK_PLUS_SQUARE "\xef\x83\xbe" // U+f0fe
#define ICON_FK_ANGLE_DOUBLE_LEFT "\xef\x84\x80" // U+f100
#define ICON_FK_ANGLE_DOUBLE_RIGHT "\xef\x84\x81" // U+f101
#define ICON_FK_ANGLE_DOUBLE_UP "\xef\x84\x82" // U+f102
#define ICON_FK_ANGLE_DOUBLE_DOWN "\xef\x84\x83" // U+f103
#define ICON_FK_ANGLE_LEFT "\xef\x84\x84" // U+f104
#define ICON_FK_ANGLE_RIGHT "\xef\x84\x85" // U+f105
#define ICON_FK_ANGLE_UP "\xef\x84\x86" // U+f106
#define ICON_FK_ANGLE_DOWN "\xef\x84\x87" // U+f107
#define ICON_FK_DESKTOP "\xef\x84\x88" // U+f108
#define ICON_FK_LAPTOP "\xef\x84\x89" // U+f109
#define ICON_FK_TABLET "\xef\x84\x8a" // U+f10a
#define ICON_FK_MOBILE "\xef\x84\x8b" // U+f10b
#define ICON_FK_CIRCLE_O "\xef\x84\x8c" // U+f10c
#define ICON_FK_QUOTE_LEFT "\xef\x84\x8d" // U+f10d
#define ICON_FK_QUOTE_RIGHT "\xef\x84\x8e" // U+f10e
#define ICON_FK_SPINNER "\xef\x84\x90" // U+f110
#define ICON_FK_CIRCLE "\xef\x84\x91" // U+f111
#define ICON_FK_REPLY "\xef\x84\x92" // U+f112
#define ICON_FK_GITHUB_ALT "\xef\x84\x93" // U+f113
#define ICON_FK_FOLDER_O "\xef\x84\x94" // U+f114
#define ICON_FK_FOLDER_OPEN_O "\xef\x84\x95" // U+f115
#define ICON_FK_SMILE_O "\xef\x84\x98" // U+f118
#define ICON_FK_FROWN_O "\xef\x84\x99" // U+f119
#define ICON_FK_MEH_O "\xef\x84\x9a" // U+f11a
#define ICON_FK_GAMEPAD "\xef\x84\x9b" // U+f11b
#define ICON_FK_KEYBOARD_O "\xef\x84\x9c" // U+f11c
#define ICON_FK_FLAG_O "\xef\x84\x9d" // U+f11d
#define ICON_FK_FLAG_CHECKERED "\xef\x84\x9e" // U+f11e
#define ICON_FK_TERMINAL "\xef\x84\xa0" // U+f120
#define ICON_FK_CODE "\xef\x84\xa1" // U+f121
#define ICON_FK_REPLY_ALL "\xef\x84\xa2" // U+f122
#define ICON_FK_STAR_HALF_O "\xef\x84\xa3" // U+f123
#define ICON_FK_LOCATION_ARROW "\xef\x84\xa4" // U+f124
#define ICON_FK_CROP "\xef\x84\xa5" // U+f125
#define ICON_FK_CODE_FORK "\xef\x84\xa6" // U+f126
#define ICON_FK_CHAIN_BROKEN "\xef\x84\xa7" // U+f127
#define ICON_FK_QUESTION "\xef\x84\xa8" // U+f128
#define ICON_FK_INFO "\xef\x84\xa9" // U+f129
#define ICON_FK_EXCLAMATION "\xef\x84\xaa" // U+f12a
#define ICON_FK_SUPERSCRIPT "\xef\x84\xab" // U+f12b
#define ICON_FK_SUBSCRIPT "\xef\x84\xac" // U+f12c
#define ICON_FK_ERASER "\xef\x84\xad" // U+f12d
#define ICON_FK_PUZZLE_PIECE "\xef\x84\xae" // U+f12e
#define ICON_FK_MICROPHONE "\xef\x84\xb0" // U+f130
#define ICON_FK_MICROPHONE_SLASH "\xef\x84\xb1" // U+f131
#define ICON_FK_SHIELD "\xef\x84\xb2" // U+f132
#define ICON_FK_CALENDAR_O "\xef\x84\xb3" // U+f133
#define ICON_FK_FIRE_EXTINGUISHER "\xef\x84\xb4" // U+f134
#define ICON_FK_ROCKET "\xef\x84\xb5" // U+f135
#define ICON_FK_MAXCDN "\xef\x84\xb6" // U+f136
#define ICON_FK_CHEVRON_CIRCLE_LEFT "\xef\x84\xb7" // U+f137
#define ICON_FK_CHEVRON_CIRCLE_RIGHT "\xef\x84\xb8" // U+f138
#define ICON_FK_CHEVRON_CIRCLE_UP "\xef\x84\xb9" // U+f139
#define ICON_FK_CHEVRON_CIRCLE_DOWN "\xef\x84\xba" // U+f13a
#define ICON_FK_HTML5 "\xef\x84\xbb" // U+f13b
#define ICON_FK_CSS3 "\xef\x84\xbc" // U+f13c
#define ICON_FK_ANCHOR "\xef\x84\xbd" // U+f13d
#define ICON_FK_UNLOCK_ALT "\xef\x84\xbe" // U+f13e
#define ICON_FK_BULLSEYE "\xef\x85\x80" // U+f140
#define ICON_FK_ELLIPSIS_H "\xef\x85\x81" // U+f141
#define ICON_FK_ELLIPSIS_V "\xef\x85\x82" // U+f142
#define ICON_FK_RSS_SQUARE "\xef\x85\x83" // U+f143
#define ICON_FK_PLAY_CIRCLE "\xef\x85\x84" // U+f144
#define ICON_FK_TICKET "\xef\x85\x85" // U+f145
#define ICON_FK_MINUS_SQUARE "\xef\x85\x86" // U+f146
#define ICON_FK_MINUS_SQUARE_O "\xef\x85\x87" // U+f147
#define ICON_FK_LEVEL_UP "\xef\x85\x88" // U+f148
#define ICON_FK_LEVEL_DOWN "\xef\x85\x89" // U+f149
#define ICON_FK_CHECK_SQUARE "\xef\x85\x8a" // U+f14a
#define ICON_FK_PENCIL_SQUARE "\xef\x85\x8b" // U+f14b
#define ICON_FK_EXTERNAL_LINK_SQUARE "\xef\x85\x8c" // U+f14c
#define ICON_FK_SHARE_SQUARE "\xef\x85\x8d" // U+f14d
#define ICON_FK_COMPASS "\xef\x85\x8e" // U+f14e
#define ICON_FK_CARET_SQUARE_O_DOWN "\xef\x85\x90" // U+f150
#define ICON_FK_CARET_SQUARE_O_UP "\xef\x85\x91" // U+f151
#define ICON_FK_CARET_SQUARE_O_RIGHT "\xef\x85\x92" // U+f152
#define ICON_FK_EUR "\xef\x85\x93" // U+f153
#define ICON_FK_GBP "\xef\x85\x94" // U+f154
#define ICON_FK_USD "\xef\x85\x95" // U+f155
#define ICON_FK_INR "\xef\x85\x96" // U+f156
#define ICON_FK_JPY "\xef\x85\x97" // U+f157
#define ICON_FK_RUB "\xef\x85\x98" // U+f158
#define ICON_FK_KRW "\xef\x85\x99" // U+f159
#define ICON_FK_BTC "\xef\x85\x9a" // U+f15a
#define ICON_FK_FILE "\xef\x85\x9b" // U+f15b
#define ICON_FK_FILE_TEXT "\xef\x85\x9c" // U+f15c
#define ICON_FK_SORT_ALPHA_ASC "\xef\x85\x9d" // U+f15d
#define ICON_FK_SORT_ALPHA_DESC "\xef\x85\x9e" // U+f15e
#define ICON_FK_SORT_AMOUNT_ASC "\xef\x85\xa0" // U+f160
#define ICON_FK_SORT_AMOUNT_DESC "\xef\x85\xa1" // U+f161
#define ICON_FK_SORT_NUMERIC_ASC "\xef\x85\xa2" // U+f162
#define ICON_FK_SORT_NUMERIC_DESC "\xef\x85\xa3" // U+f163
#define ICON_FK_THUMBS_UP "\xef\x85\xa4" // U+f164
#define ICON_FK_THUMBS_DOWN "\xef\x85\xa5" // U+f165
#define ICON_FK_YOUTUBE_SQUARE "\xef\x85\xa6" // U+f166
#define ICON_FK_YOUTUBE "\xef\x85\xa7" // U+f167
#define ICON_FK_XING "\xef\x85\xa8" // U+f168
#define ICON_FK_XING_SQUARE "\xef\x85\xa9" // U+f169
#define ICON_FK_YOUTUBE_PLAY "\xef\x85\xaa" // U+f16a
#define ICON_FK_DROPBOX "\xef\x85\xab" // U+f16b
#define ICON_FK_STACK_OVERFLOW "\xef\x85\xac" // U+f16c
#define ICON_FK_INSTAGRAM "\xef\x85\xad" // U+f16d
#define ICON_FK_FLICKR "\xef\x85\xae" // U+f16e
#define ICON_FK_ADN "\xef\x85\xb0" // U+f170
#define ICON_FK_BITBUCKET "\xef\x85\xb1" // U+f171
#define ICON_FK_BITBUCKET_SQUARE "\xef\x85\xb2" // U+f172
#define ICON_FK_TUMBLR "\xef\x85\xb3" // U+f173
#define ICON_FK_TUMBLR_SQUARE "\xef\x85\xb4" // U+f174
#define ICON_FK_LONG_ARROW_DOWN "\xef\x85\xb5" // U+f175
#define ICON_FK_LONG_ARROW_UP "\xef\x85\xb6" // U+f176
#define ICON_FK_LONG_ARROW_LEFT "\xef\x85\xb7" // U+f177
#define ICON_FK_LONG_ARROW_RIGHT "\xef\x85\xb8" // U+f178
#define ICON_FK_APPLE "\xef\x85\xb9" // U+f179
#define ICON_FK_WINDOWS "\xef\x85\xba" // U+f17a
#define ICON_FK_ANDROID "\xef\x85\xbb" // U+f17b
#define ICON_FK_LINUX "\xef\x85\xbc" // U+f17c
#define ICON_FK_DRIBBBLE "\xef\x85\xbd" // U+f17d
#define ICON_FK_SKYPE "\xef\x85\xbe" // U+f17e
#define ICON_FK_FOURSQUARE "\xef\x86\x80" // U+f180
#define ICON_FK_TRELLO "\xef\x86\x81" // U+f181
#define ICON_FK_FEMALE "\xef\x86\x82" // U+f182
#define ICON_FK_MALE "\xef\x86\x83" // U+f183
#define ICON_FK_GRATIPAY "\xef\x86\x84" // U+f184
#define ICON_FK_SUN_O "\xef\x86\x85" // U+f185
#define ICON_FK_MOON_O "\xef\x86\x86" // U+f186
#define ICON_FK_ARCHIVE "\xef\x86\x87" // U+f187
#define ICON_FK_BUG "\xef\x86\x88" // U+f188
#define ICON_FK_VK "\xef\x86\x89" // U+f189
#define ICON_FK_WEIBO "\xef\x86\x8a" // U+f18a
#define ICON_FK_RENREN "\xef\x86\x8b" // U+f18b
#define ICON_FK_PAGELINES "\xef\x86\x8c" // U+f18c
#define ICON_FK_STACK_EXCHANGE "\xef\x86\x8d" // U+f18d
#define ICON_FK_ARROW_CIRCLE_O_RIGHT "\xef\x86\x8e" // U+f18e
#define ICON_FK_ARROW_CIRCLE_O_LEFT "\xef\x86\x90" // U+f190
#define ICON_FK_CARET_SQUARE_O_LEFT "\xef\x86\x91" // U+f191
#define ICON_FK_DOT_CIRCLE_O "\xef\x86\x92" // U+f192
#define ICON_FK_WHEELCHAIR "\xef\x86\x93" // U+f193
#define ICON_FK_VIMEO_SQUARE "\xef\x86\x94" // U+f194
#define ICON_FK_TRY "\xef\x86\x95" // U+f195
#define ICON_FK_PLUS_SQUARE_O "\xef\x86\x96" // U+f196
#define ICON_FK_SPACE_SHUTTLE "\xef\x86\x97" // U+f197
#define ICON_FK_SLACK "\xef\x86\x98" // U+f198
#define ICON_FK_ENVELOPE_SQUARE "\xef\x86\x99" // U+f199
#define ICON_FK_WORDPRESS "\xef\x86\x9a" // U+f19a
#define ICON_FK_OPENID "\xef\x86\x9b" // U+f19b
#define ICON_FK_UNIVERSITY "\xef\x86\x9c" // U+f19c
#define ICON_FK_GRADUATION_CAP "\xef\x86\x9d" // U+f19d
#define ICON_FK_YAHOO "\xef\x86\x9e" // U+f19e
#define ICON_FK_GOOGLE "\xef\x86\xa0" // U+f1a0
#define ICON_FK_REDDIT "\xef\x86\xa1" // U+f1a1
#define ICON_FK_REDDIT_SQUARE "\xef\x86\xa2" // U+f1a2
#define ICON_FK_STUMBLEUPON_CIRCLE "\xef\x86\xa3" // U+f1a3
#define ICON_FK_STUMBLEUPON "\xef\x86\xa4" // U+f1a4
#define ICON_FK_DELICIOUS "\xef\x86\xa5" // U+f1a5
#define ICON_FK_DIGG "\xef\x86\xa6" // U+f1a6
#define ICON_FK_DRUPAL "\xef\x86\xa9" // U+f1a9
#define ICON_FK_JOOMLA "\xef\x86\xaa" // U+f1aa
#define ICON_FK_LANGUAGE "\xef\x86\xab" // U+f1ab
#define ICON_FK_FAX "\xef\x86\xac" // U+f1ac
#define ICON_FK_BUILDING "\xef\x86\xad" // U+f1ad
#define ICON_FK_CHILD "\xef\x86\xae" // U+f1ae
#define ICON_FK_PAW "\xef\x86\xb0" // U+f1b0
#define ICON_FK_SPOON "\xef\x86\xb1" // U+f1b1
#define ICON_FK_CUBE "\xef\x86\xb2" // U+f1b2
#define ICON_FK_CUBES "\xef\x86\xb3" // U+f1b3
#define ICON_FK_BEHANCE "\xef\x86\xb4" // U+f1b4
#define ICON_FK_BEHANCE_SQUARE "\xef\x86\xb5" // U+f1b5
#define ICON_FK_STEAM "\xef\x86\xb6" // U+f1b6
#define ICON_FK_STEAM_SQUARE "\xef\x86\xb7" // U+f1b7
#define ICON_FK_RECYCLE "\xef\x86\xb8" // U+f1b8
#define ICON_FK_CAR "\xef\x86\xb9" // U+f1b9
#define ICON_FK_TAXI "\xef\x86\xba" // U+f1ba
#define ICON_FK_TREE "\xef\x86\xbb" // U+f1bb
#define ICON_FK_SPOTIFY "\xef\x86\xbc" // U+f1bc
#define ICON_FK_DEVIANTART "\xef\x86\xbd" // U+f1bd
#define ICON_FK_SOUNDCLOUD "\xef\x86\xbe" // U+f1be
#define ICON_FK_DATABASE "\xef\x87\x80" // U+f1c0
#define ICON_FK_FILE_PDF_O "\xef\x87\x81" // U+f1c1
#define ICON_FK_FILE_WORD_O "\xef\x87\x82" // U+f1c2
#define ICON_FK_FILE_EXCEL_O "\xef\x87\x83" // U+f1c3
#define ICON_FK_FILE_POWERPOINT_O "\xef\x87\x84" // U+f1c4
#define ICON_FK_FILE_IMAGE_O "\xef\x87\x85" // U+f1c5
#define ICON_FK_FILE_ARCHIVE_O "\xef\x87\x86" // U+f1c6
#define ICON_FK_FILE_AUDIO_O "\xef\x87\x87" // U+f1c7
#define ICON_FK_FILE_VIDEO_O "\xef\x87\x88" // U+f1c8
#define ICON_FK_FILE_CODE_O "\xef\x87\x89" // U+f1c9
#define ICON_FK_VINE "\xef\x87\x8a" // U+f1ca
#define ICON_FK_CODEPEN "\xef\x87\x8b" // U+f1cb
#define ICON_FK_JSFIDDLE "\xef\x87\x8c" // U+f1cc
#define ICON_FK_LIFE_RING "\xef\x87\x8d" // U+f1cd
#define ICON_FK_CIRCLE_O_NOTCH "\xef\x87\x8e" // U+f1ce
#define ICON_FK_REBEL "\xef\x87\x90" // U+f1d0
#define ICON_FK_EMPIRE "\xef\x87\x91" // U+f1d1
#define ICON_FK_GIT_SQUARE "\xef\x87\x92" // U+f1d2
#define ICON_FK_GIT "\xef\x87\x93" // U+f1d3
#define ICON_FK_HACKER_NEWS "\xef\x87\x94" // U+f1d4
#define ICON_FK_TENCENT_WEIBO "\xef\x87\x95" // U+f1d5
#define ICON_FK_QQ "\xef\x87\x96" // U+f1d6
#define ICON_FK_WEIXIN "\xef\x87\x97" // U+f1d7
#define ICON_FK_PAPER_PLANE "\xef\x87\x98" // U+f1d8
#define ICON_FK_PAPER_PLANE_O "\xef\x87\x99" // U+f1d9
#define ICON_FK_HISTORY "\xef\x87\x9a" // U+f1da
#define ICON_FK_CIRCLE_THIN "\xef\x87\x9b" // U+f1db
#define ICON_FK_HEADER "\xef\x87\x9c" // U+f1dc
#define ICON_FK_PARAGRAPH "\xef\x87\x9d" // U+f1dd
#define ICON_FK_SLIDERS "\xef\x87\x9e" // U+f1de
#define ICON_FK_SHARE_ALT "\xef\x87\xa0" // U+f1e0
#define ICON_FK_SHARE_ALT_SQUARE "\xef\x87\xa1" // U+f1e1
#define ICON_FK_BOMB "\xef\x87\xa2" // U+f1e2
#define ICON_FK_FUTBOL_O "\xef\x87\xa3" // U+f1e3
#define ICON_FK_TTY "\xef\x87\xa4" // U+f1e4
#define ICON_FK_BINOCULARS "\xef\x87\xa5" // U+f1e5
#define ICON_FK_PLUG "\xef\x87\xa6" // U+f1e6
#define ICON_FK_SLIDESHARE "\xef\x87\xa7" // U+f1e7
#define ICON_FK_TWITCH "\xef\x87\xa8" // U+f1e8
#define ICON_FK_YELP "\xef\x87\xa9" // U+f1e9
#define ICON_FK_NEWSPAPER_O "\xef\x87\xaa" // U+f1ea
#define ICON_FK_WIFI "\xef\x87\xab" // U+f1eb
#define ICON_FK_CALCULATOR "\xef\x87\xac" // U+f1ec
#define ICON_FK_PAYPAL "\xef\x87\xad" // U+f1ed
#define ICON_FK_GOOGLE_WALLET "\xef\x87\xae" // U+f1ee
#define ICON_FK_CC_VISA "\xef\x87\xb0" // U+f1f0
#define ICON_FK_CC_MASTERCARD "\xef\x87\xb1" // U+f1f1
#define ICON_FK_CC_DISCOVER "\xef\x87\xb2" // U+f1f2
#define ICON_FK_CC_AMEX "\xef\x87\xb3" // U+f1f3
#define ICON_FK_CC_PAYPAL "\xef\x87\xb4" // U+f1f4
#define ICON_FK_CC_STRIPE "\xef\x87\xb5" // U+f1f5
#define ICON_FK_BELL_SLASH "\xef\x87\xb6" // U+f1f6
#define ICON_FK_BELL_SLASH_O "\xef\x87\xb7" // U+f1f7
#define ICON_FK_TRASH "\xef\x87\xb8" // U+f1f8
#define ICON_FK_COPYRIGHT "\xef\x87\xb9" // U+f1f9
#define ICON_FK_AT "\xef\x87\xba" // U+f1fa
#define ICON_FK_EYEDROPPER "\xef\x87\xbb" // U+f1fb
#define ICON_FK_PAINT_BRUSH "\xef\x87\xbc" // U+f1fc
#define ICON_FK_BIRTHDAY_CAKE "\xef\x87\xbd" // U+f1fd
#define ICON_FK_AREA_CHART "\xef\x87\xbe" // U+f1fe
#define ICON_FK_PIE_CHART "\xef\x88\x80" // U+f200
#define ICON_FK_LINE_CHART "\xef\x88\x81" // U+f201
#define ICON_FK_LASTFM "\xef\x88\x82" // U+f202
#define ICON_FK_LASTFM_SQUARE "\xef\x88\x83" // U+f203
#define ICON_FK_TOGGLE_OFF "\xef\x88\x84" // U+f204
#define ICON_FK_TOGGLE_ON "\xef\x88\x85" // U+f205
#define ICON_FK_BICYCLE "\xef\x88\x86" // U+f206
#define ICON_FK_BUS "\xef\x88\x87" // U+f207
#define ICON_FK_IOXHOST "\xef\x88\x88" // U+f208
#define ICON_FK_ANGELLIST "\xef\x88\x89" // U+f209
#define ICON_FK_CC "\xef\x88\x8a" // U+f20a
#define ICON_FK_ILS "\xef\x88\x8b" // U+f20b
#define ICON_FK_MEANPATH "\xef\x88\x8c" // U+f20c
#define ICON_FK_BUYSELLADS "\xef\x88\x8d" // U+f20d
#define ICON_FK_CONNECTDEVELOP "\xef\x88\x8e" // U+f20e
#define ICON_FK_DASHCUBE "\xef\x88\x90" // U+f210
#define ICON_FK_FORUMBEE "\xef\x88\x91" // U+f211
#define ICON_FK_LEANPUB "\xef\x88\x92" // U+f212
#define ICON_FK_SELLSY "\xef\x88\x93" // U+f213
#define ICON_FK_SHIRTSINBULK "\xef\x88\x94" // U+f214
#define ICON_FK_SIMPLYBUILT "\xef\x88\x95" // U+f215
#define ICON_FK_SKYATLAS "\xef\x88\x96" // U+f216
#define ICON_FK_CART_PLUS "\xef\x88\x97" // U+f217
#define ICON_FK_CART_ARROW_DOWN "\xef\x88\x98" // U+f218
#define ICON_FK_DIAMOND "\xef\x88\x99" // U+f219
#define ICON_FK_SHIP "\xef\x88\x9a" // U+f21a
#define ICON_FK_USER_SECRET "\xef\x88\x9b" // U+f21b
#define ICON_FK_MOTORCYCLE "\xef\x88\x9c" // U+f21c
#define ICON_FK_STREET_VIEW "\xef\x88\x9d" // U+f21d
#define ICON_FK_HEARTBEAT "\xef\x88\x9e" // U+f21e
#define ICON_FK_VENUS "\xef\x88\xa1" // U+f221
#define ICON_FK_MARS "\xef\x88\xa2" // U+f222
#define ICON_FK_MERCURY "\xef\x88\xa3" // U+f223
#define ICON_FK_TRANSGENDER "\xef\x88\xa4" // U+f224
#define ICON_FK_TRANSGENDER_ALT "\xef\x88\xa5" // U+f225
#define ICON_FK_VENUS_DOUBLE "\xef\x88\xa6" // U+f226
#define ICON_FK_MARS_DOUBLE "\xef\x88\xa7" // U+f227
#define ICON_FK_VENUS_MARS "\xef\x88\xa8" // U+f228
#define ICON_FK_MARS_STROKE "\xef\x88\xa9" // U+f229
#define ICON_FK_MARS_STROKE_V "\xef\x88\xaa" // U+f22a
#define ICON_FK_MARS_STROKE_H "\xef\x88\xab" // U+f22b
#define ICON_FK_NEUTER "\xef\x88\xac" // U+f22c
#define ICON_FK_GENDERLESS "\xef\x88\xad" // U+f22d
#define ICON_FK_FACEBOOK_OFFICIAL "\xef\x88\xb0" // U+f230
#define ICON_FK_PINTEREST_P "\xef\x88\xb1" // U+f231
#define ICON_FK_WHATSAPP "\xef\x88\xb2" // U+f232
#define ICON_FK_SERVER "\xef\x88\xb3" // U+f233
#define ICON_FK_USER_PLUS "\xef\x88\xb4" // U+f234
#define ICON_FK_USER_TIMES "\xef\x88\xb5" // U+f235
#define ICON_FK_BED "\xef\x88\xb6" // U+f236
#define ICON_FK_VIACOIN "\xef\x88\xb7" // U+f237
#define ICON_FK_TRAIN "\xef\x88\xb8" // U+f238
#define ICON_FK_SUBWAY "\xef\x88\xb9" // U+f239
#define ICON_FK_MEDIUM "\xef\x88\xba" // U+f23a
#define ICON_FK_MEDIUM_SQUARE "\xef\x8b\xb8" // U+f2f8
#define ICON_FK_Y_COMBINATOR "\xef\x88\xbb" // U+f23b
#define ICON_FK_OPTIN_MONSTER "\xef\x88\xbc" // U+f23c
#define ICON_FK_OPENCART "\xef\x88\xbd" // U+f23d
#define ICON_FK_EXPEDITEDSSL "\xef\x88\xbe" // U+f23e
#define ICON_FK_BATTERY_FULL "\xef\x89\x80" // U+f240
#define ICON_FK_BATTERY_THREE_QUARTERS "\xef\x89\x81" // U+f241
#define ICON_FK_BATTERY_HALF "\xef\x89\x82" // U+f242
#define ICON_FK_BATTERY_QUARTER "\xef\x89\x83" // U+f243
#define ICON_FK_BATTERY_EMPTY "\xef\x89\x84" // U+f244
#define ICON_FK_MOUSE_POINTER "\xef\x89\x85" // U+f245
#define ICON_FK_I_CURSOR "\xef\x89\x86" // U+f246
#define ICON_FK_OBJECT_GROUP "\xef\x89\x87" // U+f247
#define ICON_FK_OBJECT_UNGROUP "\xef\x89\x88" // U+f248
#define ICON_FK_STICKY_NOTE "\xef\x89\x89" // U+f249
#define ICON_FK_STICKY_NOTE_O "\xef\x89\x8a" // U+f24a
#define ICON_FK_CC_JCB "\xef\x89\x8b" // U+f24b
#define ICON_FK_CC_DINERS_CLUB "\xef\x89\x8c" // U+f24c
#define ICON_FK_CLONE "\xef\x89\x8d" // U+f24d
#define ICON_FK_BALANCE_SCALE "\xef\x89\x8e" // U+f24e
#define ICON_FK_HOURGLASS_O "\xef\x89\x90" // U+f250
#define ICON_FK_HOURGLASS_START "\xef\x89\x91" // U+f251
#define ICON_FK_HOURGLASS_HALF "\xef\x89\x92" // U+f252
#define ICON_FK_HOURGLASS_END "\xef\x89\x93" // U+f253
#define ICON_FK_HOURGLASS "\xef\x89\x94" // U+f254
#define ICON_FK_HAND_ROCK_O "\xef\x89\x95" // U+f255
#define ICON_FK_HAND_PAPER_O "\xef\x89\x96" // U+f256
#define ICON_FK_HAND_SCISSORS_O "\xef\x89\x97" // U+f257
#define ICON_FK_HAND_LIZARD_O "\xef\x89\x98" // U+f258
#define ICON_FK_HAND_SPOCK_O "\xef\x89\x99" // U+f259
#define ICON_FK_HAND_POINTER_O "\xef\x89\x9a" // U+f25a
#define ICON_FK_HAND_PEACE_O "\xef\x89\x9b" // U+f25b
#define ICON_FK_TRADEMARK "\xef\x89\x9c" // U+f25c
#define ICON_FK_REGISTERED "\xef\x89\x9d" // U+f25d
#define ICON_FK_CREATIVE_COMMONS "\xef\x89\x9e" // U+f25e
#define ICON_FK_GG "\xef\x89\xa0" // U+f260
#define ICON_FK_GG_CIRCLE "\xef\x89\xa1" // U+f261
#define ICON_FK_TRIPADVISOR "\xef\x89\xa2" // U+f262
#define ICON_FK_ODNOKLASSNIKI "\xef\x89\xa3" // U+f263
#define ICON_FK_ODNOKLASSNIKI_SQUARE "\xef\x89\xa4" // U+f264
#define ICON_FK_GET_POCKET "\xef\x89\xa5" // U+f265
#define ICON_FK_WIKIPEDIA_W "\xef\x89\xa6" // U+f266
#define ICON_FK_SAFARI "\xef\x89\xa7" // U+f267
#define ICON_FK_CHROME "\xef\x89\xa8" // U+f268
#define ICON_FK_FIREFOX "\xef\x89\xa9" // U+f269
#define ICON_FK_OPERA "\xef\x89\xaa" // U+f26a
#define ICON_FK_INTERNET_EXPLORER "\xef\x89\xab" // U+f26b
#define ICON_FK_TELEVISION "\xef\x89\xac" // U+f26c
#define ICON_FK_CONTAO "\xef\x89\xad" // U+f26d
#define ICON_FK_500PX "\xef\x89\xae" // U+f26e
#define ICON_FK_AMAZON "\xef\x89\xb0" // U+f270
#define ICON_FK_CALENDAR_PLUS_O "\xef\x89\xb1" // U+f271
#define ICON_FK_CALENDAR_MINUS_O "\xef\x89\xb2" // U+f272
#define ICON_FK_CALENDAR_TIMES_O "\xef\x89\xb3" // U+f273
#define ICON_FK_CALENDAR_CHECK_O "\xef\x89\xb4" // U+f274
#define ICON_FK_INDUSTRY "\xef\x89\xb5" // U+f275
#define ICON_FK_MAP_PIN "\xef\x89\xb6" // U+f276
#define ICON_FK_MAP_SIGNS "\xef\x89\xb7" // U+f277
#define ICON_FK_MAP_O "\xef\x89\xb8" // U+f278
#define ICON_FK_MAP "\xef\x89\xb9" // U+f279
#define ICON_FK_COMMENTING "\xef\x89\xba" // U+f27a
#define ICON_FK_COMMENTING_O "\xef\x89\xbb" // U+f27b
#define ICON_FK_HOUZZ "\xef\x89\xbc" // U+f27c
#define ICON_FK_VIMEO "\xef\x89\xbd" // U+f27d
#define ICON_FK_BLACK_TIE "\xef\x89\xbe" // U+f27e
#define ICON_FK_FONTICONS "\xef\x8a\x80" // U+f280
#define ICON_FK_REDDIT_ALIEN "\xef\x8a\x81" // U+f281
#define ICON_FK_EDGE "\xef\x8a\x82" // U+f282
#define ICON_FK_CREDIT_CARD_ALT "\xef\x8a\x83" // U+f283
#define ICON_FK_CODIEPIE "\xef\x8a\x84" // U+f284
#define ICON_FK_MODX "\xef\x8a\x85" // U+f285
#define ICON_FK_FORT_AWESOME "\xef\x8a\x86" // U+f286
#define ICON_FK_USB "\xef\x8a\x87" // U+f287
#define ICON_FK_PRODUCT_HUNT "\xef\x8a\x88" // U+f288
#define ICON_FK_MIXCLOUD "\xef\x8a\x89" // U+f289
#define ICON_FK_SCRIBD "\xef\x8a\x8a" // U+f28a
#define ICON_FK_PAUSE_CIRCLE "\xef\x8a\x8b" // U+f28b
#define ICON_FK_PAUSE_CIRCLE_O "\xef\x8a\x8c" // U+f28c
#define ICON_FK_STOP_CIRCLE "\xef\x8a\x8d" // U+f28d
#define ICON_FK_STOP_CIRCLE_O "\xef\x8a\x8e" // U+f28e
#define ICON_FK_SHOPPING_BAG "\xef\x8a\x90" // U+f290
#define ICON_FK_SHOPPING_BASKET "\xef\x8a\x91" // U+f291
#define ICON_FK_HASHTAG "\xef\x8a\x92" // U+f292
#define ICON_FK_BLUETOOTH "\xef\x8a\x93" // U+f293
#define ICON_FK_BLUETOOTH_B "\xef\x8a\x94" // U+f294
#define ICON_FK_PERCENT "\xef\x8a\x95" // U+f295
#define ICON_FK_GITLAB "\xef\x8a\x96" // U+f296
#define ICON_FK_WPBEGINNER "\xef\x8a\x97" // U+f297
#define ICON_FK_WPFORMS "\xef\x8a\x98" // U+f298
#define ICON_FK_ENVIRA "\xef\x8a\x99" // U+f299
#define ICON_FK_UNIVERSAL_ACCESS "\xef\x8a\x9a" // U+f29a
#define ICON_FK_WHEELCHAIR_ALT "\xef\x8a\x9b" // U+f29b
#define ICON_FK_QUESTION_CIRCLE_O "\xef\x8a\x9c" // U+f29c
#define ICON_FK_BLIND "\xef\x8a\x9d" // U+f29d
#define ICON_FK_AUDIO_DESCRIPTION "\xef\x8a\x9e" // U+f29e
#define ICON_FK_VOLUME_CONTROL_PHONE "\xef\x8a\xa0" // U+f2a0
#define ICON_FK_BRAILLE "\xef\x8a\xa1" // U+f2a1
#define ICON_FK_ASSISTIVE_LISTENING_SYSTEMS "\xef\x8a\xa2" // U+f2a2
#define ICON_FK_AMERICAN_SIGN_LANGUAGE_INTERPRETING "\xef\x8a\xa3" // U+f2a3
#define ICON_FK_DEAF "\xef\x8a\xa4" // U+f2a4
#define ICON_FK_GLIDE "\xef\x8a\xa5" // U+f2a5
#define ICON_FK_GLIDE_G "\xef\x8a\xa6" // U+f2a6
#define ICON_FK_SIGN_LANGUAGE "\xef\x8a\xa7" // U+f2a7
#define ICON_FK_LOW_VISION "\xef\x8a\xa8" // U+f2a8
#define ICON_FK_VIADEO "\xef\x8a\xa9" // U+f2a9
#define ICON_FK_VIADEO_SQUARE "\xef\x8a\xaa" // U+f2aa
#define ICON_FK_SNAPCHAT "\xef\x8a\xab" // U+f2ab
#define ICON_FK_SNAPCHAT_GHOST "\xef\x8a\xac" // U+f2ac
#define ICON_FK_SNAPCHAT_SQUARE "\xef\x8a\xad" // U+f2ad
#define ICON_FK_FIRST_ORDER "\xef\x8a\xb0" // U+f2b0
#define ICON_FK_YOAST "\xef\x8a\xb1" // U+f2b1
#define ICON_FK_THEMEISLE "\xef\x8a\xb2" // U+f2b2
#define ICON_FK_GOOGLE_PLUS_OFFICIAL "\xef\x8a\xb3" // U+f2b3
#define ICON_FK_FONT_AWESOME "\xef\x8a\xb4" // U+f2b4
#define ICON_FK_HANDSHAKE_O "\xef\x8a\xb5" // U+f2b5
#define ICON_FK_ENVELOPE_OPEN "\xef\x8a\xb6" // U+f2b6
#define ICON_FK_ENVELOPE_OPEN_O "\xef\x8a\xb7" // U+f2b7
#define ICON_FK_LINODE "\xef\x8a\xb8" // U+f2b8
#define ICON_FK_ADDRESS_BOOK "\xef\x8a\xb9" // U+f2b9
#define ICON_FK_ADDRESS_BOOK_O "\xef\x8a\xba" // U+f2ba
#define ICON_FK_ADDRESS_CARD "\xef\x8a\xbb" // U+f2bb
#define ICON_FK_ADDRESS_CARD_O "\xef\x8a\xbc" // U+f2bc
#define ICON_FK_USER_CIRCLE "\xef\x8a\xbd" // U+f2bd
#define ICON_FK_USER_CIRCLE_O "\xef\x8a\xbe" // U+f2be
#define ICON_FK_USER_O "\xef\x8b\x80" // U+f2c0
#define ICON_FK_ID_BADGE "\xef\x8b\x81" // U+f2c1
#define ICON_FK_ID_CARD "\xef\x8b\x82" // U+f2c2
#define ICON_FK_ID_CARD_O "\xef\x8b\x83" // U+f2c3
#define ICON_FK_QUORA "\xef\x8b\x84" // U+f2c4
#define ICON_FK_FREE_CODE_CAMP "\xef\x8b\x85" // U+f2c5
#define ICON_FK_TELEGRAM "\xef\x8b\x86" // U+f2c6
#define ICON_FK_THERMOMETER_FULL "\xef\x8b\x87" // U+f2c7
#define ICON_FK_THERMOMETER_THREE_QUARTERS "\xef\x8b\x88" // U+f2c8
#define ICON_FK_THERMOMETER_HALF "\xef\x8b\x89" // U+f2c9
#define ICON_FK_THERMOMETER_QUARTER "\xef\x8b\x8a" // U+f2ca
#define ICON_FK_THERMOMETER_EMPTY "\xef\x8b\x8b" // U+f2cb
#define ICON_FK_SHOWER "\xef\x8b\x8c" // U+f2cc
#define ICON_FK_BATH "\xef\x8b\x8d" // U+f2cd
#define ICON_FK_PODCAST "\xef\x8b\x8e" // U+f2ce
#define ICON_FK_WINDOW_MAXIMIZE "\xef\x8b\x90" // U+f2d0
#define ICON_FK_WINDOW_MINIMIZE "\xef\x8b\x91" // U+f2d1
#define ICON_FK_WINDOW_RESTORE "\xef\x8b\x92" // U+f2d2
#define ICON_FK_WINDOW_CLOSE "\xef\x8b\x93" // U+f2d3
#define ICON_FK_WINDOW_CLOSE_O "\xef\x8b\x94" // U+f2d4
#define ICON_FK_BANDCAMP "\xef\x8b\x95" // U+f2d5
#define ICON_FK_GRAV "\xef\x8b\x96" // U+f2d6
#define ICON_FK_ETSY "\xef\x8b\x97" // U+f2d7
#define ICON_FK_IMDB "\xef\x8b\x98" // U+f2d8
#define ICON_FK_RAVELRY "\xef\x8b\x99" // U+f2d9
#define ICON_FK_EERCAST "\xef\x8b\x9a" // U+f2da
#define ICON_FK_MICROCHIP "\xef\x8b\x9b" // U+f2db
#define ICON_FK_SNOWFLAKE_O "\xef\x8b\x9c" // U+f2dc
#define ICON_FK_SUPERPOWERS "\xef\x8b\x9d" // U+f2dd
#define ICON_FK_WPEXPLORER "\xef\x8b\x9e" // U+f2de
#define ICON_FK_MEETUP "\xef\x8b\xa0" // U+f2e0
#define ICON_FK_MASTODON "\xef\x8b\xa1" // U+f2e1
#define ICON_FK_MASTODON_ALT "\xef\x8b\xa2" // U+f2e2
#define ICON_FK_FORK_AWESOME "\xef\x8b\xa3" // U+f2e3
#define ICON_FK_PEERTUBE "\xef\x8b\xa4" // U+f2e4
#define ICON_FK_DIASPORA "\xef\x8b\xa5" // U+f2e5
#define ICON_FK_FRIENDICA "\xef\x8b\xa6" // U+f2e6
#define ICON_FK_GNU_SOCIAL "\xef\x8b\xa7" // U+f2e7
#define ICON_FK_LIBERAPAY_SQUARE "\xef\x8b\xa8" // U+f2e8
#define ICON_FK_LIBERAPAY "\xef\x8b\xa9" // U+f2e9
#define ICON_FK_SCUTTLEBUTT "\xef\x8b\xaa" // U+f2ea
#define ICON_FK_HUBZILLA "\xef\x8b\xab" // U+f2eb
#define ICON_FK_SOCIAL_HOME "\xef\x8b\xac" // U+f2ec
#define ICON_FK_ARTSTATION "\xef\x8b\xad" // U+f2ed
#define ICON_FK_DISCORD "\xef\x8b\xae" // U+f2ee
#define ICON_FK_DISCORD_ALT "\xef\x8b\xaf" // U+f2ef
#define ICON_FK_PATREON "\xef\x8b\xb0" // U+f2f0
#define ICON_FK_SNOWDRIFT "\xef\x8b\xb1" // U+f2f1
#define ICON_FK_ACTIVITYPUB "\xef\x8b\xb2" // U+f2f2
#define ICON_FK_ETHEREUM "\xef\x8b\xb3" // U+f2f3
#define ICON_FK_KEYBASE "\xef\x8b\xb4" // U+f2f4
#define ICON_FK_SHAARLI "\xef\x8b\xb5" // U+f2f5
#define ICON_FK_SHAARLI_O "\xef\x8b\xb6" // U+f2f6
#define ICON_FK_KEY_MODERN "\xef\x8b\xb7" // U+f2f7
#define ICON_FK_XMPP "\xef\x8b\xb9" // U+f2f9
#define ICON_FK_ARCHIVE_ORG "\xef\x8b\xbc" // U+f2fc
#define ICON_FK_FREEDOMBOX "\xef\x8b\xbd" // U+f2fd
#define ICON_FK_FACEBOOK_MESSENGER "\xef\x8b\xbe" // U+f2fe
#define ICON_FK_DEBIAN "\xef\x8b\xbf" // U+f2ff
#define ICON_FK_MASTODON_SQUARE "\xef\x8c\x80" // U+f300
#define ICON_FK_TIPEEE "\xef\x8c\x81" // U+f301
#define ICON_FK_REACT "\xef\x8c\x82" // U+f302
#define ICON_FK_DOGMAZIC "\xef\x8c\x83" // U+f303
#define ICON_FK_ZOTERO "\xef\x8c\x89" // U+f309
#define ICON_FK_NODEJS "\xef\x8c\x88" // U+f308
#define ICON_FK_NEXTCLOUD "\xef\x8c\x86" // U+f306
#define ICON_FK_NEXTCLOUD_SQUARE "\xef\x8c\x87" // U+f307
#define ICON_FK_HACKADAY "\xef\x8c\x8a" // U+f30a
#define ICON_FK_LARAVEL "\xef\x8c\x8b" // U+f30b
#define ICON_FK_SIGNALAPP "\xef\x8c\x8c" // U+f30c
#define ICON_FK_GNUPG "\xef\x8c\x8d" // U+f30d
#define ICON_FK_PHP "\xef\x8c\x8e" // U+f30e
#define ICON_FK_FFMPEG "\xef\x8c\x8f" // U+f30f
#define ICON_FK_JOPLIN "\xef\x8c\x90" // U+f310
#define ICON_FK_SYNCTHING "\xef\x8c\x91" // U+f311
#define ICON_FK_INKSCAPE "\xef\x8c\x92" // U+f312
#define ICON_FK_MATRIX_ORG "\xef\x8c\x93" // U+f313
#define ICON_FK_PIXELFED "\xef\x8c\x94" // U+f314
#define ICON_FK_BOOTSTRAP "\xef\x8c\x95" // U+f315
#define ICON_FK_DEV_TO "\xef\x8c\x96" // U+f316
#define ICON_FK_HASHNODE "\xef\x8c\x97" // U+f317
#define ICON_FK_JIRAFEAU "\xef\x8c\x98" // U+f318
#define ICON_FK_EMBY "\xef\x8c\x99" // U+f319
#define ICON_FK_WIKIDATA "\xef\x8c\x9a" // U+f31a
#define ICON_FK_GIMP "\xef\x8c\x9b" // U+f31b
#define ICON_FK_C "\xef\x8c\x9c" // U+f31c
#define ICON_FK_DIGITALOCEAN "\xef\x8c\x9d" // U+f31d
#define ICON_FK_ATT "\xef\x8c\x9e" // U+f31e
#define ICON_FK_GITEA "\xef\x8c\x9f" // U+f31f
#define ICON_FK_FILE_EPUB "\xef\x8c\xa1" // U+f321
#define ICON_FK_PYTHON "\xef\x8c\xa2" // U+f322
#define ICON_FK_ARCHLINUX "\xef\x8c\xa3" // U+f323
#define ICON_FK_PLEROMA "\xef\x8c\xa4" // U+f324
#define ICON_FK_UNSPLASH "\xef\x8c\xa5" // U+f325
#define ICON_FK_HACKSTER "\xef\x8c\xa6" // U+f326
#define ICON_FK_SPELL_CHECK "\xef\x8c\xa7" // U+f327
#define ICON_FK_MOON "\xef\x8c\xa8" // U+f328
#define ICON_FK_SUN "\xef\x8c\xa9" // U+f329
#define ICON_FK_F_DROID "\xef\x8c\xaa" // U+f32a
#define ICON_FK_BIOMETRIC "\xef\x8c\xab" // U+f32b
#define ICON_FK_WIRE "\xef\x8c\xac" // U+f32c
#define ICON_FK_TOR_ONION "\xef\x8c\xae" // U+f32e
#define ICON_FK_VOLUME_MUTE "\xef\x8c\xaf" // U+f32f
#define ICON_FK_BELL_RINGING "\xef\x8c\xad" // U+f32d
#define ICON_FK_BELL_RINGING_O "\xef\x8c\xb0" // U+f330
#define ICON_FK_HAL "\xef\x8c\xb3" // U+f333
#define ICON_FK_JUPYTER "\xef\x8c\xb5" // U+f335
#define ICON_FK_JULIA "\xef\x8c\xb4" // U+f334
#define ICON_FK_CLASSICPRESS "\xef\x8c\xb1" // U+f331
#define ICON_FK_CLASSICPRESS_CIRCLE "\xef\x8c\xb2" // U+f332
#define ICON_FK_OPEN_COLLECTIVE "\xef\x8c\xb6" // U+f336
#define ICON_FK_ORCID "\xef\x8c\xb7" // U+f337
#define ICON_FK_RESEARCHGATE "\xef\x8c\xb8" // U+f338
#define ICON_FK_FUNKWHALE "\xef\x8c\xb9" // U+f339
#define ICON_FK_ASKFM "\xef\x8c\xba" // U+f33a
#define ICON_FK_BLOCKSTACK "\xef\x8c\xbb" // U+f33b
#define ICON_FK_BOARDGAMEGEEK "\xef\x8c\xbc" // U+f33c
#define ICON_FK_BUNNY "\xef\x8d\x9f" // U+f35f
#define ICON_FK_BUYMEACOFFEE "\xef\x8c\xbd" // U+f33d
#define ICON_FK_CC_BY "\xef\x8c\xbe" // U+f33e
#define ICON_FK_CC_CC "\xef\x8c\xbf" // U+f33f
#define ICON_FK_CC_NC_EU "\xef\x8d\x81" // U+f341
#define ICON_FK_CC_NC_JP "\xef\x8d\x82" // U+f342
#define ICON_FK_CC_NC "\xef\x8d\x80" // U+f340
#define ICON_FK_CC_ND "\xef\x8d\x83" // U+f343
#define ICON_FK_CC_PD "\xef\x8d\x84" // U+f344
#define ICON_FK_CC_REMIX "\xef\x8d\x85" // U+f345
#define ICON_FK_CC_SA "\xef\x8d\x86" // U+f346
#define ICON_FK_CC_SHARE "\xef\x8d\x87" // U+f347
#define ICON_FK_CC_ZERO "\xef\x8d\x88" // U+f348
#define ICON_FK_CONWAY_GLIDER "\xef\x8d\x89" // U+f349
#define ICON_FK_CSHARP "\xef\x8d\x8a" // U+f34a
#define ICON_FK_EMAIL_BULK "\xef\x8d\x8b" // U+f34b
#define ICON_FK_EMAIL_BULK_O "\xef\x8d\x8c" // U+f34c
#define ICON_FK_GNU "\xef\x8d\x8d" // U+f34d
#define ICON_FK_GOOGLE_PLAY "\xef\x8d\x8e" // U+f34e
#define ICON_FK_HEROKU "\xef\x8d\x8f" // U+f34f
#define ICON_FK_HOME_ASSISTANT "\xef\x8d\x90" // U+f350
#define ICON_FK_JAVA "\xef\x8d\x91" // U+f351
#define ICON_FK_MARIADB "\xef\x8d\x92" // U+f352
#define ICON_FK_MARKDOWN "\xef\x8d\x93" // U+f353
#define ICON_FK_MYSQL "\xef\x8d\x94" // U+f354
#define ICON_FK_NORDCAST "\xef\x8d\x95" // U+f355
#define ICON_FK_PLUME "\xef\x8d\x96" // U+f356
#define ICON_FK_POSTGRESQL "\xef\x8d\x97" // U+f357
#define ICON_FK_SASS_ALT "\xef\x8d\x99" // U+f359
#define ICON_FK_SASS "\xef\x8d\x98" // U+f358
#define ICON_FK_SKATE "\xef\x8d\x9a" // U+f35a
#define ICON_FK_SKETCHFAB "\xef\x8d\x9b" // U+f35b
#define ICON_FK_TEX "\xef\x8d\x9c" // U+f35c
#define ICON_FK_TEXTPATTERN "\xef\x8d\x9d" // U+f35d
#define ICON_FK_UNITY "\xef\x8d\x9e" // U+f35e

File diff suppressed because it is too large Load Diff

@ -0,0 +1,2 @@
/usr/lib/mangohud/lib32
/usr/lib/mangohud/lib64

@ -0,0 +1 @@
@libdir_mangohud@

@ -1,15 +1,13 @@
project('MangoHud',
['c', 'cpp'],
version : 'v0.7.2',
version : 'v0.4.0',
license : 'MIT',
meson_version: '>=0.60.0',
default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14', 'warning_level=2']
default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14']
)
cc = meson.get_compiler('c')
cpp = meson.get_compiler('cpp')
prog_python = import('python').find_installation('python3', modules: ['mako'])
prog_python = import('python').find_installation('python3')
null_dep = dependency('', required : false)
mangohud_version = vcs_tag(
@ -17,29 +15,24 @@ mangohud_version = vcs_tag(
input: 'version.h.in',
output: 'version.h')
mangohud_version_dep = declare_dependency(sources : mangohud_version)
pre_args = [
'-D__STDC_CONSTANT_MACROS',
'-D__STDC_FORMAT_MACROS',
'-D__STDC_LIMIT_MACROS',
'-DPACKAGE_VERSION="@0@"'.format(meson.project_version()),
'-DSPDLOG_COMPILED_LIB'
]
# Always set max spdlog level, handle this using MANGOHUD_LOG_LEVEL instead.
# Define DEBUG for debug builds only (debugoptimized is not included on this one)
if get_option('buildtype') == 'debug'
pre_args += '-DDEBUG'
pre_args += '-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE'
else
pre_args += '-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG'
pre_args += '-DNDEBUG'
endif
# TODO: this is very incomplete
is_unixy = false
if ['linux', 'cygwin', 'gnu'].contains(host_machine.system())
pre_args += '-D_GNU_SOURCE'
pre_args += '-DHAVE_PTHREAD'
is_unixy = true
endif
if get_option('glibcxx_asserts')
@ -84,17 +77,9 @@ endforeach
vulkan_wsi_args = []
vulkan_wsi_deps = []
if is_unixy
dep_x11 = dependency('x11', required: get_option('with_x11'))
dep_wayland_client = dependency('wayland-client',
required: get_option('with_wayland'), version : '>=1.11')
dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true)
dep_xkb = dependency('xkbcommon', required: get_option('with_wayland'))
else
dep_x11 = null_dep
dep_wayland_client = null_dep
dbus_dep = null_dep
endif
dep_x11 = dependency('x11', required: get_option('with_x11'))
dep_wayland_client = dependency('wayland-client',
required: get_option('with_wayland'), version : '>=1.11')
if dep_x11.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_XLIB_KHR']
@ -103,10 +88,9 @@ endif
if dep_wayland_client.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR']
vulkan_wsi_deps += dep_wayland_client
vulkan_wsi_deps += dep_xkb
endif
if is_unixy and not dep_x11.found() and not dep_wayland_client.found()
if not dep_x11.found() and not dep_wayland_client.found()
error('At least one of "with_x11" and "with_wayland" should be enabled')
endif
@ -114,70 +98,92 @@ inc_common = [
include_directories('include'),
]
dep_vulkan = dependency('vulkan', required: get_option('use_system_vulkan'))
dep_pthread = dependency('threads')
dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true)
# Check for generic C arguments
c_args = []
foreach a : ['-Werror=implicit-function-declaration',
'-Werror=missing-prototypes', '-Werror=return-type',
'-Werror=incompatible-pointer-types',
'-fno-math-errno',
'-fno-trapping-math', '-Qunused-arguments']
if cc.has_argument(a)
c_args += a
endif
endforeach
add_project_arguments(
cc.get_supported_arguments([
'-Werror=implicit-function-declaration',
'-Werror=missing-declarations',
'-Werror=missing-prototypes',
'-Werror=return-type',
'-Werror=incompatible-pointer-types',
'-Wno-unused-parameter',
'-Qunused-arguments',
'-fno-math-errno',
'-fno-trapping-math',
'-Wno-missing-field-initializers',
]), language : ['c'],
)
foreach a : ['missing-field-initializers', 'format-truncation']
if cc.has_argument('-W' + a)
c_args += '-Wno-' + a
endif
endforeach
add_project_arguments(
cpp.get_supported_arguments([
'-Werror=missing-declarations',
'-Werror=return-type',
'-Wno-unused-parameter',
'-Qunused-arguments',
'-fno-math-errno',
'-fno-trapping-math',
'-Wno-non-virtual-dtor',
'-Wno-missing-field-initializers',
]), language : ['cpp'],
)
c_vis_args = []
if cc.has_argument('-fvisibility=hidden')
c_vis_args += '-fvisibility=hidden'
endif
# Check for generic C++ arguments
cpp_args = []
foreach a : ['-Werror=return-type',
'-fno-math-errno', '-fno-trapping-math',
'-Qunused-arguments']
if cpp.has_argument(a)
cpp_args += a
endif
endforeach
# For some reason, the test for -Wno-foo always succeeds with gcc, even if the
# option is not supported. Hence, check for -Wfoo instead.
foreach a : ['non-virtual-dtor', 'missing-field-initializers', 'format-truncation']
if cpp.has_argument('-W' + a)
cpp_args += '-Wno-' + a
endif
endforeach
no_override_init_args = []
foreach a : ['override-init', 'initializer-overrides']
if cc.has_argument('-W' + a)
no_override_init_args += '-Wno-' + a
endif
endforeach
cpp_vis_args = []
if cpp.has_argument('-fvisibility=hidden')
cpp_vis_args += '-fvisibility=hidden'
endif
foreach a : pre_args
add_project_arguments(a, language : ['c', 'cpp'])
endforeach
foreach a : c_args
add_project_arguments(a, language : ['c'])
endforeach
foreach a : cpp_args
add_project_arguments(a, language : ['cpp'])
endforeach
# check for dl support
if is_unixy
if cc.has_function('dlopen')
dep_dl = null_dep
else
dep_dl = cc.find_library('dl')
endif
# check for linking with rt by default
if cc.has_function('clock_gettime')
dep_rt = null_dep
else
dep_rt = cc.find_library('rt')
endif
else
if cc.has_function('dlopen')
dep_dl = null_dep
dep_rt = null_dep
else
dep_dl = cc.find_library('dl')
endif
# Commented code can be used if mangohud start using latest SDK Vulkan-Headers
# Allowing user to build mangohud using system Vulkan-Headers
#if not dependency('VulkanHeaders').found()
if dep_vulkan.found()
datadir = get_option('datadir')
if not datadir.startswith('/')
datadir = get_option('prefix') / datadir
endif
vk_api_xml = files(datadir / 'vulkan/registry/vk.xml')
else
vkh_sp = subproject('vulkan-headers')
vk_api_xml = vkh_sp.get_variable('vulkan_api_xml')
dep_vulkan = vkh_sp.get_variable('vulkan_headers_dep')
#else
# dep_vulkan = dependency('VulkanHeaders', required: true)
# vk_api_xml = files('/usr/share/vulkan/registry/vk.xml')
#endif
endif
vk_enum_to_str = custom_target(
'vk_enum_to_str',
@ -189,25 +195,10 @@ vk_enum_to_str = custom_target(
],
)
imgui_options = [
'default_library=static',
'werror=false',
# use 'auto_features=disabled' once available: https://github.com/mesonbuild/meson/issues/5320
'dx9=disabled',
'dx10=disabled',
'dx11=disabled',
'dx12=disabled',
'metal=disabled',
'opengl=disabled',
'vulkan=disabled',
'glfw=disabled',
'sdl2=disabled',
'osx=disabled',
'win=disabled',
'allegro5=disabled',
'webgpu=disabled',
'sdl_renderer=disabled'
]
util_files = files(
'src/mesa/util/os_socket.c',
'src/mesa/util/os_time.c',
)
sizeof_ptr = cc.sizeof('void*')
if sizeof_ptr == 8
@ -216,87 +207,11 @@ elif sizeof_ptr == 4
pre_args += '-DMANGOHUD_ARCH="32bit"'
endif
if get_option('mangoapp')
imgui_options += [
'opengl=enabled',
'glfw=enabled',
]
endif
dearimgui_dep = dependency('imgui', fallback: ['imgui'], required: true, default_options: imgui_options)
if is_unixy
implot_dep = dependency('implot', fallback: ['implot'], required: true, default_options: ['default_library=static'])
else
implot_dep = null_dep
implot_lib = static_library('nulllib', [])
endif
spdlog_options = [
'default_library=static',
'compile_library=true',
'werror=false',
'tests=disabled',
'external_fmt=disabled',
'std_format=disabled'
]
spdlog_dep = dependency('spdlog', required: false)
if get_option('use_system_spdlog').disabled() or not spdlog_dep.found()
if get_option('use_system_spdlog').enabled()
warning('spdlog depedency not found follwing back to submodule')
endif
spdlog_sp = subproject('spdlog', default_options: spdlog_options)
spdlog_dep = spdlog_sp.get_variable('spdlog_dep')
endif
if ['windows', 'mingw'].contains(host_machine.system())
minhook_dep = dependency('minhook', fallback: ['minhook', 'minhook_dep'], required: true)
windows_deps = [
minhook_dep,
]
if get_option('use_system_nvml')
cpp_nvml_args = '-DUSE_SYSTEM_NVML'
else
windows_deps = null_dep
endif
if get_option('mangoapp') or get_option('mangoapp_layer')
glfw3_dep = dependency('glfw3')
glew_dep = dependency('glew')
cpp_nvml_args = []
endif
json_dep = dependency('nlohmann_json')
subdir('modules/ImGui')
subdir('src')
if get_option('include_doc')
subdir('data')
endif
if get_option('tests').enabled()
cmocka_dep = dependency('cmocka', fallback: ['cmocka', 'cmocka_dep'])
e = executable('amdgpu', 'tests/test_amdgpu.cpp',
files(
'src/amdgpu.cpp',
'src/cpu.cpp',
'src/gpu.cpp',
'src/mesa/util/os_time.c',
'src/file_utils.cpp',
),
cpp_args: ['-DTEST_ONLY'],
dependencies: [
dep_vulkan,
cmocka_dep,
spdlog_dep,
implot_dep,
dearimgui_dep
],
include_directories: inc_common)
test('test amdgpu', e, workdir : meson.project_source_root() + '/tests')
endif
# install helper sripts
if get_option('mangoplot').enabled()
subdir('bin')
endif

@ -1,16 +1,12 @@
option('glibcxx_asserts', type : 'boolean', value : false)
option('use_system_spdlog', type : 'feature', value : 'disabled', description: 'Use system spdlog library')
option('use_system_vulkan', type : 'feature', value : 'disabled', description: 'Use system vulkan headers instead of the provided ones')
option('use_system_nvml', type : 'boolean', value : false, description : 'Use system nvml headers instead of the provided ones')
option('mangohud_prefix', type : 'string', value : '', description: 'Add prefix to cross-compiled library, like "lib32-".')
option('append_libdir_mangohud', type : 'boolean', value : true, description: 'Append "mangohud" to libdir path or not.')
option('include_doc', type : 'boolean', value : true, description: 'Include the example config, man pages, appstream files etc.')
option('include_doc', type : 'boolean', value : true, description: 'Include the example config')
option('with_nvml', type : 'combo', value : 'enabled', choices: ['enabled', 'system', 'disabled'], description: 'Enable NVML support')
option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support')
option('with_x11', type : 'feature', value : 'enabled')
option('with_wayland', type : 'feature', value : 'enabled')
option('with_wayland', type : 'feature', value : 'disabled')
option('with_dbus', type : 'feature', value : 'enabled')
option('loglevel', type: 'combo', choices : ['trace', 'debug', 'info', 'warn', 'err', 'critical', 'off'], value : 'info', description: 'Max log level in non-debug build')
option('mangoapp', type: 'boolean', value : false)
option('mangohudctl', type: 'boolean', value : false)
option('mangoapp_layer', type: 'boolean', value : false)
option('tests', type: 'feature', value: 'auto', description: 'Run tests')
option('mangoplot', type: 'feature', value: 'enabled')
option('dynamic_string_tokens', type: 'boolean', value: true, description: 'Use dynamic string tokens in LD_PRELOAD')
option('with_dlsym', type : 'feature', value : 'disabled')

@ -1,16 +0,0 @@
[binaries]
c = 'i686-w64-mingw32-gcc'
cpp = 'i686-w64-mingw32-g++'
ar = 'i686-w64-mingw32-ar'
strip = 'i686-w64-mingw32-strip'
pkg-config = 'i686-w64-mingw32-pkg-config'
sh = '/usr/bin/sh'
[properties]
needs_exe_wrapper = true
[host_machine]
system = 'windows'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

@ -1,16 +0,0 @@
[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
strip = 'x86_64-w64-mingw32-strip'
pkg-config = 'x86_64-w64-mingw32-pkg-config'
sh = '/usr/bin/sh'
[properties]
needs_exe_wrapper = true
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

@ -0,0 +1,3 @@
# ignore this folder
root = true

@ -0,0 +1,11 @@
libimgui_core = static_library(
'imgui_core',
files('src/imgui.cpp', 'src/imgui_draw.cpp', 'src/imgui_widgets.cpp'),
cpp_args : ['-w'],
install : false
)
libimgui_core_dep = declare_dependency(
link_with : libimgui_core,
include_directories : include_directories('src')
)

@ -0,0 +1 @@
Subproject commit 1f02d240b38f445abb0381ade0867752d5d2bc7b

@ -1 +0,0 @@
Subproject commit df6622659e366c63dfc9591245fa6a9a10ec4759

@ -1,97 +0,0 @@
# Maintainer: Simon Hallsten <flightlessmangoyt@gmail.com>
pkgname=('mangohud' 'lib32-mangohud')
pkgver=0.7.2.rc3.r13.g5d744d3
pkgrel=1
pkgdesc="Vulkan and OpenGL overlay to display performance information"
arch=('x86_64')
makedepends=('dbus' 'gcc' 'meson' 'python-mako' 'libx11' 'lib32-libx11' 'git' 'pkgconf' 'vulkan-headers')
depends=('glslang' 'libglvnd' 'lib32-libglvnd' 'glew' 'glfw-x11' 'python-numpy' 'python-matplotlib'
'libxrandr' 'libxkbcommon' 'lib32-libxkbcommon')
replaces=('vulkan-mesa-layer-mango')
license=('MIT')
source=(
"mangohud"::"git+https://github.com/flightlessmango/MangoHud.git#branch=master"
"mangohud-minhook"::"git+https://github.com/flightlessmango/minhook.git"
"imgui-1.89.9.tar.gz::https://github.com/ocornut/imgui/archive/refs/tags/v1.89.9.tar.gz"
"imgui_1.89.9-1_patch.zip::https://wrapdb.mesonbuild.com/v2/imgui_1.89.9-1/get_patch"
"spdlog-1.14.1.tar.gz::https://github.com/gabime/spdlog/archive/refs/tags/v1.14.1.tar.gz"
"spdlog_1.14.1-1_patch.zip::https://wrapdb.mesonbuild.com/v2/spdlog_1.14.1-1/get_patch"
"nlohmann_json-3.10.5.zip::https://github.com/nlohmann/json/releases/download/v3.10.5/include.zip"
"vulkan-headers-1.2.158.tar.gz::https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.158.tar.gz"
"vulkan-headers-1.2.158-2-wrap.zip::https://wrapdb.mesonbuild.com/v2/vulkan-headers_1.2.158-2/get_patch"
"implot-0.16.zip::https://github.com/epezent/implot/archive/refs/tags/v0.16.zip"
"implot_0.16-1_patch.zip::https://wrapdb.mesonbuild.com/v2/implot_0.16-1/get_patch"
)
sha256sums=(
'SKIP'
'SKIP'
'1acc27a778b71d859878121a3f7b287cd81c29d720893d2b2bf74455bf9d52d6'
'9b21290c597d76bf8d4eeb3f9ffa024b11d9ea6c61e91d648ccc90b42843d584'
'1586508029a7d0670dfcb2d97575dcdc242d3868a259742b69f100801ab4e16b'
'ae878e732330ea1048f90d7e117c40c0cd2a6fb8ae5492c7955818ce3aaade6c'
'b94997df68856753b72f0d7a3703b7d484d4745c567f3584ef97c96c25a5798e'
"53361271cfe274df8782e1e47bdc9e61b7af432ba30acbfe31723f9df2c257f3"
"860358cf5e73f458cd1e88f8c38116d123ab421d5ce2e4129ec38eaedd820e17"
"24f772c688f6b8a6e19d7efc10e4923a04a915f13d487b08b83553aa62ae1708"
"1c6b1462066a5452fa50c1da1dd47fed841f28232972c82d778f2962936568c7"
)
_build_args="-Dappend_libdir_mangohud=false -Dwith_xnvctrl=disabled -Dmangoapp_layer=true -Dtests=disabled"
pkgver() {
cd "$srcdir/mangohud"
git describe --tags | sed -r 's/^v//;s/([^-]*-g)/r\1/;s/-/./g'
}
prepare() {
cd "${srcdir}/mangohud"
git submodule init
git config submodule.modules/minhook.url "$srcdir/mangohud-minhook"
git -c protocol.file.allow=always submodule update
# meson subprojects
ln -sv "$srcdir/imgui-1.89.9" subprojects
ln -sv "$srcdir/spdlog-1.14.1" subprojects
mkdir subprojects/nlohmann_json-3.10.5
ln -sv "$srcdir/include" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/single_include" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/LICENSE.MIT" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/meson.build" subprojects/nlohmann_json-3.10.5/
ln -sv "$srcdir/Vulkan-Headers-1.2.158" subprojects
ln -sv "$srcdir/implot-0.16" subprojects
}
build() {
arch-meson mangohud build64 \
${_build_args} -Dmangoapp=true -Dmangohudctl=true
ninja -C build64
export CC="${CC:-gcc} -m32"
export CXX="${CXX:-g++} -m32"
export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
export LLVM_CONFIG="/usr/bin/llvm-config32"
arch-meson mangohud build32 \
--libdir=lib32 \
-Dmangoapp=false \
-Dmangohudctl=false \
${_build_args}
ninja -C build32
}
package_mangohud() {
provides=("mangohud")
conflicts=('mangohud-common')
DESTDIR="${pkgdir}" ninja -C build64 install
}
package_lib32-mangohud() {
provides=("lib32-mangohud")
DESTDIR="${pkgdir}" ninja -C build32 install
rm -rf "$pkgdir/usr/bin"
rm -rf "$pkgdir/usr/share"
install -m644 -Dt "$pkgdir/usr/share/vulkan/implicit_layer.d" "$srcdir/build32/src/MangoHud.x86.json"
}

@ -1,287 +0,0 @@
#include <spdlog/spdlog.h>
#include <thread>
#ifdef __linux__
#include <sys/sysinfo.h>
#endif
#include "amdgpu.h"
#include "gpu.h"
#include "cpu.h"
#include "overlay.h"
#include "hud_elements.h"
#include "logging.h"
#include "mesa/util/macros.h"
std::string metrics_path = "";
struct amdgpu_common_metrics amdgpu_common_metrics;
std::mutex amdgpu_common_metrics_m;
std::mutex amdgpu_m;
std::condition_variable amdgpu_c;
bool amdgpu_run_thread = true;
std::unique_ptr<Throttling> throttling;
bool amdgpu_verify_metrics(const std::string& path)
{
metrics_table_header header {};
FILE *f;
f = fopen(path.c_str(), "rb");
if (!f) {
SPDLOG_DEBUG("Failed to read the metrics header of '{}'", path);
return false;
}
if (fread(&header, sizeof(header), 1, f) == 0)
{
SPDLOG_DEBUG("Failed to read the metrics header of '{}'", path);
return false;
}
switch (header.format_revision)
{
case 1: // v1_1, v1_2, v1_3
if(header.content_revision<=0 || header.content_revision>3)// v1_0, not naturally aligned
break;
cpuStats.cpu_type = "GPU";
return true;
case 2: // v2_1, v2_2, v2_3, v2_4
if(header.content_revision<=0 || header.content_revision>4)// v2_0, not naturally aligned
break;
cpuStats.cpu_type = "APU";
return true;
default:
break;
}
SPDLOG_WARN("Unsupported gpu_metrics version: {}.{}", header.format_revision, header.content_revision);
return false;
}
#define IS_VALID_METRIC(FIELD) (FIELD != 0xffff)
void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics) {
FILE *f;
void *buf[MAX(sizeof(struct gpu_metrics_v1_3), sizeof(struct gpu_metrics_v2_4))/sizeof(void*)+1];
struct metrics_table_header* header = (metrics_table_header*)buf;
f = fopen(metrics_path.c_str(), "rb");
if (!f)
return;
// Read the whole file
if (fread(buf, sizeof(buf), 1, f) != 0) {
SPDLOG_DEBUG("amdgpu metrics file '{}' is larger than the buffer", metrics_path.c_str());
fclose(f);
return;
}
fclose(f);
int64_t indep_throttle_status = 0;
if (header->format_revision == 1) {
// Desktop GPUs
struct gpu_metrics_v1_3 *amdgpu_metrics = (struct gpu_metrics_v1_3 *) buf;
metrics->gpu_load_percent = amdgpu_metrics->average_gfx_activity;
metrics->average_gfx_power_w = amdgpu_metrics->average_socket_power;
metrics->current_gfxclk_mhz = amdgpu_metrics->current_gfxclk;
metrics->current_uclk_mhz = amdgpu_metrics->current_uclk;
metrics->gpu_temp_c = amdgpu_metrics->temperature_edge;
indep_throttle_status = amdgpu_metrics->indep_throttle_status;
metrics->fan_speed = amdgpu_metrics->current_fan_speed;
} else if (header->format_revision == 2) {
// APUs
struct gpu_metrics_v2_3 *amdgpu_metrics = (struct gpu_metrics_v2_3 *) buf;
metrics->gpu_load_percent = amdgpu_metrics->average_gfx_activity;
metrics->average_gfx_power_w = amdgpu_metrics->average_gfx_power / 1000.f;
if( IS_VALID_METRIC(amdgpu_metrics->average_cpu_power) ) {
// prefered method
metrics->average_cpu_power_w = amdgpu_metrics->average_cpu_power / 1000.f;
} else if( IS_VALID_METRIC(amdgpu_metrics->average_core_power[0]) ) {
// fallback 1: sum of core power
metrics->average_cpu_power_w = 0;
unsigned i = 0;
do metrics->average_cpu_power_w = metrics->average_cpu_power_w + amdgpu_metrics->average_core_power[i] / 1000.f;
while (++i < ARRAY_SIZE(amdgpu_metrics->average_core_power) && IS_VALID_METRIC(amdgpu_metrics->average_core_power[i]));
} else if( IS_VALID_METRIC(amdgpu_metrics->average_socket_power) && IS_VALID_METRIC(amdgpu_metrics->average_gfx_power) ) {
// fallback 2: estimate cpu power from total socket power
metrics->average_cpu_power_w = amdgpu_metrics->average_socket_power / 1000.f - amdgpu_metrics->average_gfx_power / 1000.f;
} else {
// giving up
metrics->average_cpu_power_w = 0;
}
if( IS_VALID_METRIC(amdgpu_metrics->current_gfxclk) ) {
// prefered method
metrics->current_gfxclk_mhz = amdgpu_metrics->current_gfxclk;
} else if( IS_VALID_METRIC(amdgpu_metrics->average_gfxclk_frequency) ) {
// fallback 1
metrics->current_gfxclk_mhz = amdgpu_metrics->average_gfxclk_frequency;
} else {
// giving up
metrics->current_gfxclk_mhz = 0;
}
if( IS_VALID_METRIC(amdgpu_metrics->current_uclk) ) {
// prefered method
metrics->current_uclk_mhz = amdgpu_metrics->current_uclk;
} else if( IS_VALID_METRIC(amdgpu_metrics->average_uclk_frequency) ) {
// fallback 1
metrics->current_uclk_mhz = amdgpu_metrics->average_uclk_frequency;
} else {
// giving up
metrics->current_uclk_mhz = 0;
}
if( IS_VALID_METRIC(amdgpu_metrics->temperature_soc) ) {
// prefered method
metrics->soc_temp_c = amdgpu_metrics->temperature_soc / 100;
} else if( header->content_revision >= 3 && IS_VALID_METRIC(amdgpu_metrics->average_temperature_soc) ) {
// fallback 1
metrics->soc_temp_c = amdgpu_metrics->average_temperature_soc / 100;
} else {
// giving up
metrics->soc_temp_c = 0;
}
if( IS_VALID_METRIC(amdgpu_metrics->temperature_gfx) ) {
// prefered method
metrics->gpu_temp_c = amdgpu_metrics->temperature_gfx / 100;
} else if( header->content_revision >= 3 && IS_VALID_METRIC(amdgpu_metrics->average_temperature_gfx) ) {
// fallback 1
metrics->gpu_temp_c = amdgpu_metrics->average_temperature_gfx / 100;
} else {
// giving up
metrics->gpu_temp_c = 0;
}
int cpu_temp = 0;
if( IS_VALID_METRIC(amdgpu_metrics->temperature_core[0]) ) {
// prefered method
unsigned i = 0;
do cpu_temp = MAX(cpu_temp, amdgpu_metrics->temperature_core[i]);
while (++i < ARRAY_SIZE(amdgpu_metrics->temperature_core) && IS_VALID_METRIC(amdgpu_metrics->temperature_core[i]));
metrics->apu_cpu_temp_c = cpu_temp / 100;
} else if( header->content_revision >= 3 && IS_VALID_METRIC(amdgpu_metrics->average_temperature_core[0]) ) {
// fallback 1
unsigned i = 0;
do cpu_temp = MAX(cpu_temp, amdgpu_metrics->average_temperature_core[i]);
while (++i < ARRAY_SIZE(amdgpu_metrics->average_temperature_core) && IS_VALID_METRIC(amdgpu_metrics->average_temperature_core[i]));
metrics->apu_cpu_temp_c = cpu_temp / 100;
} else if( cpuStats.ReadcpuTempFile(cpu_temp) ) {
// fallback 2: Try temp from file 'm_cpuTempFile' of 'cpu.cpp'
metrics->apu_cpu_temp_c = cpu_temp;
} else {
// giving up
metrics->apu_cpu_temp_c = 0;
}
indep_throttle_status = amdgpu_metrics->indep_throttle_status;
}
/* Throttling: See
https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h
for the offsets */
metrics->is_power_throttled = ((indep_throttle_status >> 0) & 0xFF) != 0;
metrics->is_current_throttled = ((indep_throttle_status >> 16) & 0xFF) != 0;
metrics->is_temp_throttled = ((indep_throttle_status >> 32) & 0xFFFF) != 0;
metrics->is_other_throttled = ((indep_throttle_status >> 56) & 0xFF) != 0;
if (throttling)
throttling->indep_throttle_status = indep_throttle_status;
}
void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing) {
// Get all the samples
for (size_t cur_sample_id=0; cur_sample_id < METRICS_SAMPLE_COUNT; cur_sample_id++) {
amdgpu_get_instant_metrics(&metrics_buffer[cur_sample_id]);
// Detect and fix if the gpu load is reported in centipercent
if (gpu_load_needs_dividing || metrics_buffer[cur_sample_id].gpu_load_percent > 100){
gpu_load_needs_dividing = true;
metrics_buffer[cur_sample_id].gpu_load_percent /= 100;
}
usleep(METRICS_POLLING_PERIOD_MS * 1000);
}
// Copy the results from the different metrics to amdgpu_common_metrics
amdgpu_common_metrics_m.lock();
UPDATE_METRIC_AVERAGE(gpu_load_percent);
UPDATE_METRIC_AVERAGE_FLOAT(average_gfx_power_w);
UPDATE_METRIC_AVERAGE_FLOAT(average_cpu_power_w);
UPDATE_METRIC_AVERAGE(current_gfxclk_mhz);
UPDATE_METRIC_AVERAGE(current_uclk_mhz);
UPDATE_METRIC_AVERAGE(soc_temp_c);
UPDATE_METRIC_AVERAGE(gpu_temp_c);
UPDATE_METRIC_AVERAGE(apu_cpu_temp_c);
UPDATE_METRIC_MAX(is_power_throttled);
UPDATE_METRIC_MAX(is_current_throttled);
UPDATE_METRIC_MAX(is_temp_throttled);
UPDATE_METRIC_MAX(is_other_throttled);
UPDATE_METRIC_MAX(fan_speed);
amdgpu_common_metrics_m.unlock();
}
void amdgpu_metrics_polling_thread() {
struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT];
bool gpu_load_needs_dividing = false; //some GPUs report load as centipercent
// Initial poll of the metrics, so that we have values to display as fast as possible
amdgpu_get_instant_metrics(&amdgpu_common_metrics);
if (amdgpu_common_metrics.gpu_load_percent > 100){
gpu_load_needs_dividing = true;
amdgpu_common_metrics.gpu_load_percent /= 100;
}
// Set all the fields to 0 by default. Only done once as we're just replacing previous values after
memset(metrics_buffer, 0, sizeof(metrics_buffer));
while (1) {
std::unique_lock<std::mutex> lock(amdgpu_m);
amdgpu_c.wait(lock, []{return amdgpu_run_thread;});
lock.unlock();
#ifndef TEST_ONLY
if (HUDElements.params->no_display && !logger->is_active())
usleep(100000);
else
#endif
amdgpu_get_samples_and_copy(metrics_buffer, gpu_load_needs_dividing);
}
}
void amdgpu_get_metrics(uint32_t deviceID){
static bool init = false;
if (!init){
std::thread(amdgpu_metrics_polling_thread).detach();
init = true;
}
amdgpu_common_metrics_m.lock();
gpu_info.load = amdgpu_common_metrics.gpu_load_percent;
gpu_info.powerUsage = amdgpu_common_metrics.average_gfx_power_w;
gpu_info.MemClock = amdgpu_common_metrics.current_uclk_mhz;
// Use hwmon instead, see gpu.cpp
if ( deviceID == 0x1435 || deviceID == 0x163f )
{
// If we are on VANGOGH (Steam Deck), then
// always use use core clock from GPU metrics.
gpu_info.CoreClock = amdgpu_common_metrics.current_gfxclk_mhz;
}
// gpu_info.temp = amdgpu_common_metrics.gpu_temp_c;
gpu_info.apu_cpu_power = amdgpu_common_metrics.average_cpu_power_w;
gpu_info.apu_cpu_temp = amdgpu_common_metrics.apu_cpu_temp_c;
gpu_info.is_power_throttled = amdgpu_common_metrics.is_power_throttled;
gpu_info.is_current_throttled = amdgpu_common_metrics.is_current_throttled;
gpu_info.is_temp_throttled = amdgpu_common_metrics.is_temp_throttled;
gpu_info.is_other_throttled = amdgpu_common_metrics.is_other_throttled;
gpu_info.fan_speed = amdgpu_common_metrics.fan_speed;
amdgpu_common_metrics_m.unlock();
}

@ -1,310 +0,0 @@
#pragma once
// #include <fstream>
// #include <iostream>
#include <stdio.h>
#include <inttypes.h>
#include <unistd.h>
#include <string>
#include "overlay_params.h"
#include <mutex>
#include <condition_variable>
#include <vector>
#include <sys/param.h>
#include <algorithm>
#define METRICS_UPDATE_PERIOD_MS 500
#define METRICS_POLLING_PERIOD_MS 25
#define METRICS_SAMPLE_COUNT (METRICS_UPDATE_PERIOD_MS/METRICS_POLLING_PERIOD_MS)
#define NUM_HBM_INSTANCES 4
#define UPDATE_METRIC_AVERAGE(FIELD) do { int value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } amdgpu_common_metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0)
#define UPDATE_METRIC_AVERAGE_FLOAT(FIELD) do { float value_sum = 0; for (size_t s=0; s < METRICS_SAMPLE_COUNT; s++) { value_sum += metrics_buffer[s].FIELD; } amdgpu_common_metrics.FIELD = value_sum / METRICS_SAMPLE_COUNT; } while(0)
#define UPDATE_METRIC_MAX(FIELD) do { int cur_max = metrics_buffer[0].FIELD; for (size_t s=1; s < METRICS_SAMPLE_COUNT; s++) { cur_max = MAX(cur_max, metrics_buffer[s].FIELD); }; amdgpu_common_metrics.FIELD = cur_max; } while(0)
#define UPDATE_METRIC_LAST(FIELD) do { amdgpu_common_metrics.FIELD = metrics_buffer[METRICS_SAMPLE_COUNT - 1].FIELD; } while(0)
#ifdef _WIN32
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#endif
struct metrics_table_header {
uint16_t structure_size;
uint8_t format_revision;
uint8_t content_revision;
};
struct gpu_metrics_v1_3 {
struct metrics_table_header common_header;
/* Temperature */
uint16_t temperature_edge;
uint16_t temperature_hotspot;
uint16_t temperature_mem;
uint16_t temperature_vrgfx;
uint16_t temperature_vrsoc;
uint16_t temperature_vrmem;
/* Utilization */
uint16_t average_gfx_activity;
uint16_t average_umc_activity; // memory controller
uint16_t average_mm_activity; // UVD or VCN
/* Power/Energy */
uint16_t average_socket_power;
uint64_t energy_accumulator;
/* Driver attached timestamp (in ns) */
uint64_t system_clock_counter;
/* Average clocks */
uint16_t average_gfxclk_frequency;
uint16_t average_socclk_frequency;
uint16_t average_uclk_frequency;
uint16_t average_vclk0_frequency;
uint16_t average_dclk0_frequency;
uint16_t average_vclk1_frequency;
uint16_t average_dclk1_frequency;
/* Current clocks */
uint16_t current_gfxclk;
uint16_t current_socclk;
uint16_t current_uclk;
uint16_t current_vclk0;
uint16_t current_dclk0;
uint16_t current_vclk1;
uint16_t current_dclk1;
/* Throttle status */
uint32_t throttle_status;
/* Fans */
uint16_t current_fan_speed;
/* Link width/speed */
uint16_t pcie_link_width;
uint16_t pcie_link_speed; // in 0.1 GT/s
uint16_t padding;
uint32_t gfx_activity_acc;
uint32_t mem_activity_acc;
uint16_t temperature_hbm[NUM_HBM_INSTANCES];
/* PMFW attached timestamp (10ns resolution) */
uint64_t firmware_timestamp;
/* Voltage (mV) */
uint16_t voltage_soc;
uint16_t voltage_gfx;
uint16_t voltage_mem;
uint16_t padding1;
/* Throttle status (ASIC independent) */
uint64_t indep_throttle_status;
};
struct gpu_metrics_v2_3 {
struct metrics_table_header common_header;
/* Temperature */
uint16_t temperature_gfx; // gfx temperature on APUs
uint16_t temperature_soc; // soc temperature on APUs
uint16_t temperature_core[8]; // CPU core temperature on APUs
uint16_t temperature_l3[2];
/* Utilization */
uint16_t average_gfx_activity;
uint16_t average_mm_activity; // UVD or VCN
/* Driver attached timestamp (in ns) */
uint64_t system_clock_counter;
/* Power/Energy */
uint16_t average_socket_power; // dGPU + APU power on A + A platform
uint16_t average_cpu_power;
uint16_t average_soc_power;
uint16_t average_gfx_power;
uint16_t average_core_power[8]; // CPU core power on APUs
/* Average clocks */
uint16_t average_gfxclk_frequency;
uint16_t average_socclk_frequency;
uint16_t average_uclk_frequency;
uint16_t average_fclk_frequency;
uint16_t average_vclk_frequency;
uint16_t average_dclk_frequency;
/* Current clocks */
uint16_t current_gfxclk;
uint16_t current_socclk;
uint16_t current_uclk;
uint16_t current_fclk;
uint16_t current_vclk;
uint16_t current_dclk;
uint16_t current_coreclk[8]; // CPU core clocks
uint16_t current_l3clk[2];
/* Throttle status (ASIC dependent) */
uint32_t throttle_status;
/* Fans */
uint16_t fan_pwm;
uint16_t padding[3];
/* Throttle status (ASIC independent) */
uint64_t indep_throttle_status;
/* Average Temperature */
uint16_t average_temperature_gfx; // average gfx temperature on APUs
uint16_t average_temperature_soc; // average soc temperature on APUs
uint16_t average_temperature_core[8]; // average CPU core temperature on APUs
uint16_t average_temperature_l3[2];
};
struct gpu_metrics_v2_4 {
struct metrics_table_header common_header;
/* Temperature (unit: centi-Celsius) */
uint16_t temperature_gfx;
uint16_t temperature_soc;
uint16_t temperature_core[8];
uint16_t temperature_l3[2];
/* Utilization (unit: centi) */
uint16_t average_gfx_activity;
uint16_t average_mm_activity;
/* Driver attached timestamp (in ns) */
uint64_t system_clock_counter;
/* Power/Energy (unit: mW) */
uint16_t average_socket_power;
uint16_t average_cpu_power;
uint16_t average_soc_power;
uint16_t average_gfx_power;
uint16_t average_core_power[8];
/* Average clocks (unit: MHz) */
uint16_t average_gfxclk_frequency;
uint16_t average_socclk_frequency;
uint16_t average_uclk_frequency;
uint16_t average_fclk_frequency;
uint16_t average_vclk_frequency;
uint16_t average_dclk_frequency;
/* Current clocks (unit: MHz) */
uint16_t current_gfxclk;
uint16_t current_socclk;
uint16_t current_uclk;
uint16_t current_fclk;
uint16_t current_vclk;
uint16_t current_dclk;
uint16_t current_coreclk[8];
uint16_t current_l3clk[2];
/* Throttle status (ASIC dependent) */
uint32_t throttle_status;
/* Fans */
uint16_t fan_pwm;
uint16_t padding[3];
/* Throttle status (ASIC independent) */
uint64_t indep_throttle_status;
/* Average Temperature (unit: centi-Celsius) */
uint16_t average_temperature_gfx;
uint16_t average_temperature_soc;
uint16_t average_temperature_core[8];
uint16_t average_temperature_l3[2];
/* Power/Voltage (unit: mV) */
uint16_t average_cpu_voltage;
uint16_t average_soc_voltage;
uint16_t average_gfx_voltage;
/* Power/Current (unit: mA) */
uint16_t average_cpu_current;
uint16_t average_soc_current;
uint16_t average_gfx_current;
};
/* This structure is used to communicate the latest values of the amdgpu metrics.
* The direction of communication is amdgpu_polling_thread -> amdgpu_get_metrics().
*/
struct amdgpu_common_metrics {
/* Load level: averaged across the sampling period */
uint16_t gpu_load_percent;
// uint16_t mem_load_percent;
/* Power usage: averaged across the sampling period */
float average_gfx_power_w;
float average_cpu_power_w;
/* Clocks: latest value of the clock */
uint16_t current_gfxclk_mhz;
uint16_t current_uclk_mhz;
/* Temperatures: maximum values over the sampling period */
uint16_t soc_temp_c;
uint16_t gpu_temp_c;
uint16_t apu_cpu_temp_c;
/* throttling status */
bool is_power_throttled;
bool is_current_throttled;
bool is_temp_throttled;
bool is_other_throttled;
uint16_t fan_speed;
};
bool amdgpu_verify_metrics(const std::string& path);
void amdgpu_get_metrics(uint32_t deviceID);
extern std::string metrics_path;
extern std::condition_variable amdgpu_c;
extern bool amdgpu_run_thread;
void amdgpu_get_instant_metrics(struct amdgpu_common_metrics *metrics);
void amdgpu_metrics_polling_thread();
void amdgpu_get_samples_and_copy(struct amdgpu_common_metrics metrics_buffer[METRICS_SAMPLE_COUNT], bool &gpu_load_needs_dividing);
void amdgpu_trottling_thread(std::vector<float> &power, std::vector<float> &thermal);
class Throttling {
public:
std::vector<float> power;
std::vector<float> thermal;
int64_t indep_throttle_status;
Throttling()
: power(200, 0.0f),
thermal(200, 0.0f) {}
void update(){
if (((indep_throttle_status >> 0) & 0xFF) != 0)
power.push_back(0.1);
else
power.push_back(0);
if (((indep_throttle_status >> 32) & 0xFFFF) != 0)
thermal.push_back(0.1);
else
thermal.push_back(0);
power.erase(power.begin());
thermal.erase(thermal.begin());
}
bool power_throttling(){
return std::find(power.begin(), power.end(), 0.1f) != power.end();
}
bool thermal_throttling(){
return std::find(thermal.begin(), thermal.end(), 0.1f) != thermal.end();
}
};
extern std::unique_ptr<Throttling> throttling;

@ -1,79 +0,0 @@
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "mangoapp_proto.h"
static void help_and_quit() {
fprintf(stderr, "Usage: mangohudctl [set|toggle] attribute [value]\n");
fprintf(stderr, " mangohudctl reload-cfg\n");
fprintf(stderr, "Attributes:\n");
fprintf(stderr, " no_display hides or shows hud\n");
fprintf(stderr, " log_session handles logging status\n");
fprintf(stderr, " reload_config reloads the config\n");
fprintf(stderr, "Accepted values:\n");
fprintf(stderr, " true\n");
fprintf(stderr, " false\n");
fprintf(stderr, " 1\n");
fprintf(stderr, " 0\n");
exit(1);
}
static bool str_to_bool(const char *value)
{
if (strcasecmp(value, "true") == 0 || strcmp(value, "1") == 0)
return true;
else if (strcasecmp(value, "false") == 0 || strcmp(value, "0") == 0)
return false;
/* invalid boolean, display a nice error message saying that */
fprintf(stderr, "The value '%s' is not an accepted boolean. Use 0/1 or true/false\n", value);
exit(1);
}
int main(int argc, char *argv[])
{
/* Set up message queue */
int key = ftok("mangoapp", 65);
int msgid = msgget(key, 0666 | IPC_CREAT);
/* Create the message that we will send to mangohud */
struct mangoapp_ctrl_msgid1_v1 ctrl_msg = {
.hdr.msg_type = 2,
.hdr.ctrl_msg_type = 1,
.hdr.version = 1,
};
uint8_t value;
if (argc <= 2)
help_and_quit();
if (strcmp(argv[1], "set") == 0) {
if (argc != 4)
help_and_quit();
value = str_to_bool(argv[3]) ? 1 : 2;
} else if (strcmp(argv[1], "toggle") == 0) {
if (argc != 3)
help_and_quit();
value = 3;
} else {
help_and_quit();
}
if (strcmp(argv[2], "no_display") == 0)
ctrl_msg.no_display = value;
else if (strcmp(argv[2], "log_session") == 0)
ctrl_msg.log_session = value;
else if (strcmp(argv[2], "reload_config") == 0)
ctrl_msg.reload_config = value;
else
help_and_quit();
msgsnd(msgid, &ctrl_msg, sizeof(struct mangoapp_ctrl_msgid1_v1), IPC_NOWAIT);
return 0;
}

@ -1,211 +0,0 @@
#include <mutex>
#include <list>
#include <fstream>
#include <unordered_map>
#include <sys/stat.h>
#include <unistd.h>
#include <inttypes.h>
#include "mesa/util/macros.h"
#include "vk_enum_to_str.h"
#include <vulkan/vk_layer.h>
#include <vulkan/vk_util.h>
#include "nlohmann/json.hpp"
#include "engine_types.h"
using namespace std;
using json = nlohmann::json;
// single global lock, for simplicity
std::mutex global_lock;
typedef std::lock_guard<std::mutex> scoped_lock;
std::unordered_map<uint64_t, void *> vk_object_to_data;
/* Mapped from VkInstace/VkPhysicalDevice */
struct instance_data {
struct vk_instance_dispatch_table vtable;
VkInstance instance;
string engineName, engineVersion;
enum EngineTypes engine;
};
#define HKEY(obj) ((uint64_t)(obj))
#define FIND(type, obj) (reinterpret_cast<type *>(find_object_data(HKEY(obj))))
static void *find_object_data(uint64_t obj)
{
::scoped_lock lk(global_lock);
return vk_object_to_data[obj];
}
static void map_object(uint64_t obj, void *data)
{
::scoped_lock lk(global_lock);
vk_object_to_data[obj] = data;
}
static void unmap_object(uint64_t obj)
{
::scoped_lock lk(global_lock);
vk_object_to_data.erase(obj);
}
static VkLayerInstanceCreateInfo *get_instance_chain_info(const VkInstanceCreateInfo *pCreateInfo,
VkLayerFunction func)
{
vk_foreach_struct(item, pCreateInfo->pNext) {
if (item->sType == VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO &&
((VkLayerInstanceCreateInfo *) item)->function == func)
return (VkLayerInstanceCreateInfo *) item;
}
unreachable("instance chain info not found");
return NULL;
}
static struct instance_data *new_instance_data(VkInstance instance)
{
struct instance_data *data = new instance_data();
data->instance = instance;
map_object(HKEY(data->instance), data);
return data;
}
static void instance_data_map_physical_devices(struct instance_data *instance_data,
bool map)
{
uint32_t physicalDeviceCount = 0;
instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
&physicalDeviceCount,
NULL);
std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
instance_data->vtable.EnumeratePhysicalDevices(instance_data->instance,
&physicalDeviceCount,
physicalDevices.data());
for (uint32_t i = 0; i < physicalDeviceCount; i++) {
if (map)
map_object(HKEY(physicalDevices[i]), instance_data);
else
unmap_object(HKEY(physicalDevices[i]));
}
}
static VkResult overlay_CreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance)
{
fprintf(stderr, "MANGOAPP LAYER: Init\n");
VkLayerInstanceCreateInfo *chain_info =
get_instance_chain_info(pCreateInfo, VK_LAYER_LINK_INFO);
std::string engineVersion,engineName;
enum EngineTypes engine = EngineTypes::UNKNOWN;
const char* pEngineName = nullptr;
if (pCreateInfo->pApplicationInfo)
pEngineName = pCreateInfo->pApplicationInfo->pEngineName;
if (pEngineName)
engineName = pEngineName;
if (engineName == "DXVK" || engineName == "vkd3d") {
int engineVer = pCreateInfo->pApplicationInfo->engineVersion;
engineVersion = to_string(VK_VERSION_MAJOR(engineVer)) + "." + to_string(VK_VERSION_MINOR(engineVer)) + "." + to_string(VK_VERSION_PATCH(engineVer));
}
if (engineName == "DXVK")
engine = DXVK;
else if (engineName == "vkd3d")
engine = VKD3D;
else if(engineName == "mesa zink")
engine = ZINK;
else if (engineName == "Damavand")
engine = DAMAVAND;
else if (engineName == "Feral3D")
engine = FERAL3D;
else
engine = VULKAN;
assert(chain_info->u.pLayerInfo);
PFN_vkGetInstanceProcAddr fpGetInstanceProcAddr =
chain_info->u.pLayerInfo->pfnNextGetInstanceProcAddr;
PFN_vkCreateInstance fpCreateInstance =
(PFN_vkCreateInstance)fpGetInstanceProcAddr(NULL, "vkCreateInstance");
if (fpCreateInstance == NULL) {
return VK_ERROR_INITIALIZATION_FAILED;
}
// Advance the link info for the next element on the chain
chain_info->u.pLayerInfo = chain_info->u.pLayerInfo->pNext;
VkResult result = fpCreateInstance(pCreateInfo, pAllocator, pInstance);
if (result != VK_SUCCESS) return result;
struct instance_data *instance_data = new_instance_data(*pInstance);
vk_load_instance_commands(instance_data->instance,
fpGetInstanceProcAddr,
&instance_data->vtable);
instance_data_map_physical_devices(instance_data, true);
instance_data->engine = engine;
instance_data->engineName = engineName;
instance_data->engineVersion = engineVersion;
struct stat info;
string path = "/tmp/mangoapp/";
string command = "mkdir -p " + path;
string json_path = path + to_string(getpid()) + ".json";
if( stat(path.c_str(), &info ) != 0 )
system(command.c_str());
json j;
j["engine"] = engine;
ofstream o(json_path);
if (!o.fail()){
o << std::setw(4) << j << std::endl;
} else{
fprintf(stderr, "MANGOAPP LAYER: failed to write json\n");
}
o.close();
return result;
}
extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance,
const char *funcName);
static const struct {
const char *name;
void *ptr;
} name_to_funcptr_map[] = {
{ "vkGetInstanceProcAddr", (void *) overlay_GetInstanceProcAddr },
#define ADD_HOOK(fn) { "vk" # fn, (void *) overlay_ ## fn }
#define ADD_ALIAS_HOOK(alias, fn) { "vk" # alias, (void *) overlay_ ## fn }
ADD_HOOK(CreateInstance),
#undef ADD_HOOK
};
static void *find_ptr(const char *name)
{
for (uint32_t i = 0; i < ARRAY_SIZE(name_to_funcptr_map); i++) {
if (strcmp(name, name_to_funcptr_map[i].name) == 0)
return name_to_funcptr_map[i].ptr;
}
return NULL;
}
extern "C" VK_LAYER_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL overlay_GetInstanceProcAddr(VkInstance instance,
const char *funcName)
{
void *ptr = find_ptr(funcName);
if (ptr) return reinterpret_cast<PFN_vkVoidFunction>(ptr);
if (instance == NULL) return NULL;
struct instance_data *instance_data = FIND(struct instance_data, instance);
if (instance_data->vtable.GetInstanceProcAddr == NULL) return NULL;
return instance_data->vtable.GetInstanceProcAddr(instance, funcName);
}

@ -1,423 +0,0 @@
// Dear ImGui: standalone example application for GLFW + OpenGL 3, using programmable pipeline
// (GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan/Metal graphics context creation, etc.)
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#include <sys/ipc.h>
#include <sys/msg.h>
#include "imgui.h"
#include "imgui_impl_glfw.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <thread>
#include <unistd.h>
#include "../overlay.h"
#include "notify.h"
#include "mangoapp.h"
#include "mangoapp_proto.h"
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include "amdgpu.h"
#ifdef __linux__
#include "implot.h"
#endif
#define GLFW_EXPOSE_NATIVE_X11
#include <GLFW/glfw3native.h>
#include <X11/Xatom.h>
#include "nlohmann/json.hpp"
using json = nlohmann::json;
using namespace std;
static void glfw_error_callback(int error, const char* description)
{
fprintf(stderr, "Glfw Error %d: %s\n", error, description);
}
swapchain_stats sw_stats {};
overlay_params params {};
static ImVec2 window_size;
static uint32_t vendorID;
static std::string deviceName;
static notify_thread notifier;
static int msgid;
static bool mangoapp_paused = false;
std::mutex mangoapp_m;
std::condition_variable mangoapp_cv;
static uint8_t raw_msg[1024] = {0};
static uint32_t screenWidth, screenHeight;
static unsigned int get_prop(const char* propName){
Display *x11_display = glfwGetX11Display();
Atom gamescope_focused = XInternAtom(x11_display, propName, false);
auto scr = DefaultScreen(x11_display);
auto root = RootWindow(x11_display, scr);
Atom actual;
int format;
unsigned long n, left;
uint64_t *data;
int result = XGetWindowProperty(x11_display, root, gamescope_focused, 0L, 1L, false,
XA_CARDINAL, &actual, &format,
&n, &left, ( unsigned char** )&data);
if (result == Success && data != NULL){
bool *found = nullptr;
unsigned int i;
memcpy(&i, data, sizeof(unsigned int));
XFree((void *) data);
if ( found != nullptr )
{
*found = true;
}
return i;
}
return -1;
}
static void ctrl_thread(){
while (1){
const struct mangoapp_ctrl_msgid1_v1 *mangoapp_ctrl_v1 = (const struct mangoapp_ctrl_msgid1_v1*) raw_msg;
memset(raw_msg, 0, sizeof(raw_msg));
msgrcv(msgid, (void *) raw_msg, sizeof(raw_msg), 2, 0);
switch (mangoapp_ctrl_v1->log_session) {
case 0:
// Keep as-is
break;
case 1:
if (!logger->is_active())
logger->start_logging();
break;
case 2:
if (logger->is_active())
logger->stop_logging();
break;
case 3:
logger->is_active() ? logger->stop_logging() : logger->start_logging();
break;
}
switch (mangoapp_ctrl_v1->reload_config) {
case 0:
break;
case 1:
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
break;
case 2:
break;
case 3:
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
break;
}
{
std::lock_guard<std::mutex> lk(mangoapp_m);
switch (mangoapp_ctrl_v1->no_display){
case 0:
// Keep as-is
break;
case 1:
params.no_display = 1;
break;
case 2:
params.no_display = 0;
break;
case 3:
params.no_display ? params.no_display = 0 : params.no_display = 1;
break;
}
}
mangoapp_cv.notify_one();
}
}
bool new_frame = false;
static void gamescope_frametime(uint64_t app_frametime_ns, uint64_t latency_ns){
if (app_frametime_ns != uint64_t(-1))
{
float app_frametime_ms = app_frametime_ns / 1000000.f;
HUDElements.gamescope_debug_app.push_back(app_frametime_ms);
if (HUDElements.gamescope_debug_app.size() > 200)
HUDElements.gamescope_debug_app.erase(HUDElements.gamescope_debug_app.begin());
}
float latency_ms = latency_ns / 1000000.f;
if (latency_ns == uint64_t(-1))
latency_ms = -1;
HUDElements.gamescope_debug_latency.push_back(latency_ms);
if (HUDElements.gamescope_debug_latency.size() > 200)
HUDElements.gamescope_debug_latency.erase(HUDElements.gamescope_debug_latency.begin());
}
static void msg_read_thread(){
for (size_t i = 0; i < 200; i++){
HUDElements.gamescope_debug_app.push_back(0);
HUDElements.gamescope_debug_latency.push_back(0);
}
int key = ftok("mangoapp", 65);
msgid = msgget(key, 0666 | IPC_CREAT);
// uint32_t previous_pid = 0;
const struct mangoapp_msg_header *hdr = (const struct mangoapp_msg_header*) raw_msg;
const struct mangoapp_msg_v1 *mangoapp_v1 = (const struct mangoapp_msg_v1*) raw_msg;
while (1){
// make sure that the message recieved is compatible
// and that we're not trying to use variables that don't exist (yet)
size_t msg_size = msgrcv(msgid, (void *) raw_msg, sizeof(raw_msg), 1, 0);
if (msg_size != -1)
{
if (hdr->version == 1){
if (msg_size > offsetof(struct mangoapp_msg_v1, visible_frametime_ns)){
bool should_new_frame = false;
if (mangoapp_v1->visible_frametime_ns != ~(0lu) && (!params.no_display || logger->is_active())) {
update_hud_info_with_frametime(sw_stats, params, vendorID, mangoapp_v1->visible_frametime_ns);
should_new_frame = true;
}
if (msg_size > offsetof(mangoapp_msg_v1, fsrUpscale)){
HUDElements.g_fsrUpscale = mangoapp_v1->fsrUpscale;
if (params.fsr_steam_sharpness < 0)
HUDElements.g_fsrSharpness = mangoapp_v1->fsrSharpness;
else
HUDElements.g_fsrSharpness = params.fsr_steam_sharpness - mangoapp_v1->fsrSharpness;
}
if (!HUDElements.params->enabled[OVERLAY_PARAM_ENABLED_mangoapp_steam]){
steam_focused = get_prop("GAMESCOPE_FOCUSED_APP_GFX") == 769;
} else {
steam_focused = false;
}
// if (!steam_focused && mangoapp_v1->pid != previous_pid){
// string path = "/tmp/mangoapp/" + to_string(mangoapp_v1->pid) + ".json";
// ifstream i(path);
// if (i.fail()){
// sw_stats.engine = EngineTypes::GAMESCOPE;
// } else {
// json j;
// i >> j;
// sw_stats.engine = static_cast<EngineTypes> (j["engine"]);
// }
// previous_pid = mangoapp_v1->pid;
// }
if (msg_size > offsetof(mangoapp_msg_v1, latency_ns))
gamescope_frametime(mangoapp_v1->app_frametime_ns, mangoapp_v1->latency_ns);
if (should_new_frame)
{
{
std::unique_lock<std::mutex> lk(mangoapp_m);
new_frame = true;
}
mangoapp_cv.notify_one();
screenWidth = mangoapp_v1->outputWidth;
screenHeight = mangoapp_v1->outputHeight;
}
}
} else {
printf("Unsupported mangoapp struct version: %i\n", hdr->version);
exit(1);
}
}
else
{
printf("mangoapp: msgrcv returned -1 with error %d - %s\n", errno, strerror(errno));
}
}
}
static const char *GamescopeOverlayProperty = "GAMESCOPE_EXTERNAL_OVERLAY";
static GLFWwindow* init(const char* glsl_version){
init_spdlog();
GLFWwindow *window = glfwCreateWindow(1280, 800, "mangoapp overlay window", NULL, NULL);
Display *x11_display = glfwGetX11Display();
Window x11_window = glfwGetX11Window(window);
if (x11_window && x11_display)
{
// Set atom for gamescope to render as an overlay.
Atom overlay_atom = XInternAtom (x11_display, GamescopeOverlayProperty, False);
uint32_t value = 1;
XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropertyNewValue, (unsigned char *)&value, 1);
}
glfwMakeContextCurrent(window);
glfwSwapInterval(1); // Enable vsync
ImGui::CreateContext();
#ifdef __linux__
ImPlot::CreateContext();
#endif
ImGuiIO& io = ImGui::GetIO(); (void)io;
io.IniFilename = NULL;
ImGui::StyleColorsDark();
ImGui_ImplGlfw_InitForOpenGL(window, true);
ImGui_ImplOpenGL3_Init(glsl_version);
return window;
}
static void shutdown(GLFWwindow* window){
ImGui_ImplOpenGL3_Shutdown();
ImGui_ImplGlfw_Shutdown();
ImGui::DestroyContext();
glfwDestroyWindow(window);
}
static void get_atom_info(){
HUDElements.hdr_status = get_prop("GAMESCOPE_COLOR_APP_WANTS_HDR_FEEDBACK");
HUDElements.refresh = get_prop("GAMESCOPE_DISPLAY_REFRESH_RATE_FEEDBACK");
}
static bool render(GLFWwindow* window) {
if (HUDElements.colors.update)
HUDElements.convert_colors(params);
ImVec2 last_window_size = window_size;
ImGui_ImplGlfw_NewFrame();
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
overlay_new_frame(params);
position_layer(sw_stats, params, window_size);
render_imgui(sw_stats, params, window_size, true);
get_atom_info();
overlay_end_frame();
if (screenWidth && screenHeight)
glfwSetWindowSize(window, screenWidth, screenHeight);
ImGui::EndFrame();
return last_window_size.x != window_size.x || last_window_size.y != window_size.y;
}
int main(int, char**)
{
// Setup window
glfwSetErrorCallback(glfw_error_callback);
if (!glfwInit())
return 1;
const char* glsl_version = "#version 130";
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_RESIZABLE, 1);
glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, 1);
// Create window with graphics context
GLFWwindow* window = init(glsl_version);
Display *x11_display = glfwGetX11Display();
Window x11_window = glfwGetX11Window(window);
Atom overlay_atom = XInternAtom (x11_display, GamescopeOverlayProperty, False);
// Initialize OpenGL loader
bool err = glewInit() != GLEW_OK;
if (err)
{
fprintf(stderr, "Failed to initialize OpenGL loader!\n");
return 1;
}
// Setup Platform/Renderer backends
int control_client = -1;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
create_fonts(nullptr, params, sw_stats.font1, sw_stats.font_text);
HUDElements.convert_colors(params);
init_cpu_stats(params);
notifier.params = &params;
start_notifier(notifier);
window_size = ImVec2(params.width, params.height);
deviceName = (char*)glGetString(GL_RENDERER);
sw_stats.deviceName = deviceName;
SPDLOG_DEBUG("mangoapp deviceName: {}", deviceName);
#define GLX_RENDERER_VENDOR_ID_MESA 0x8183
auto pfn_glXQueryCurrentRendererIntegerMESA = (Bool (*)(int, unsigned int*)) (glfwGetProcAddress("glXQueryCurrentRendererIntegerMESA"));
// This will return 0x0 vendorID on NVIDIA so just go to else
if (pfn_glXQueryCurrentRendererIntegerMESA && vendorID != 0x0) {
pfn_glXQueryCurrentRendererIntegerMESA(GLX_RENDERER_VENDOR_ID_MESA, &vendorID);
SPDLOG_DEBUG("mangoapp vendorID: {:#x}", vendorID);
} else {
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
vendorID = 0x1002;
} else if (deviceName.find("Intel") != std::string::npos) {
vendorID = 0x8086;
} else {
vendorID = 0x10de;
}
}
HUDElements.vendorID = vendorID;
init_gpu_stats(vendorID, 0, params);
init_system_info();
sw_stats.engine = EngineTypes::GAMESCOPE;
std::thread(msg_read_thread).detach();
std::thread(ctrl_thread).detach();
if(!logger) logger = std::make_unique<Logger>(HUDElements.params);
Atom noFocusAtom = XInternAtom(x11_display, "GAMESCOPE_NO_FOCUS", False);
uint32_t value = 1;
XChangeProperty(x11_display, x11_window, noFocusAtom, XA_CARDINAL, 32,
PropModeReplace, (unsigned char *)&value, 1);
// Main loop
while (!glfwWindowShouldClose(window)){
if (!params.no_display){
if (mangoapp_paused){
glfwRestoreWindow(window);
uint32_t value = 1;
XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1);
XSync(x11_display, 0);
mangoapp_paused = false;
{
amdgpu_run_thread = true;
amdgpu_c.notify_one();
}
}
{
std::unique_lock<std::mutex> lk(mangoapp_m);
mangoapp_cv.wait(lk, []{return new_frame || params.no_display;});
new_frame = false;
}
check_keybinds(params, vendorID);
// Start the Dear ImGui frame
{
if (render(window)) {
// If we need to resize our window, give it another couple of rounds for the
// stupid display size stuff to propagate through ImGUI (using NDC and scaling
// in GL makes me a very unhappy boy.)
render(window);
render(window);
}
if (params.control >= 0) {
control_client_check(params.control, control_client, deviceName);
process_control_socket(control_client, params);
}
}
// Rendering
ImGui::Render();
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
glfwSwapBuffers(window);
} else if (!mangoapp_paused) {
glfwIconifyWindow(window);
uint32_t value = 0;
XChangeProperty(x11_display, x11_window, overlay_atom, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&value, 1);
XSync(x11_display, 0);
mangoapp_paused = true;
{
amdgpu_run_thread = false;
amdgpu_c.notify_one();
}
std::unique_lock<std::mutex> lk(mangoapp_m);
mangoapp_cv.wait(lk, []{return !params.no_display;});
}
}
// Cleanup
shutdown(window);
glfwTerminate();
return 0;
}

@ -1,8 +0,0 @@
#pragma once
#include <stdint.h>
#include <mutex>
#include <condition_variable>
#include <vector>
extern std::mutex mangoapp_m;
extern std::condition_variable mangoapp_cv;

@ -1,39 +0,0 @@
#include <stdint.h>
struct mangoapp_msg_header {
long msg_type; // Message queue ID, never change
uint32_t version; /* for major changes in the way things work */
} __attribute__((packed));
struct mangoapp_msg_v1 {
struct mangoapp_msg_header hdr;
uint32_t pid;
uint64_t visible_frametime_ns;
uint8_t fsrUpscale;
uint8_t fsrSharpness;
// For debugging
uint64_t app_frametime_ns;
uint64_t latency_ns;
uint32_t outputWidth;
uint32_t outputHeight;
// WARNING: Always ADD fields, never remove or repurpose fields
} __attribute__((packed));
struct mangoapp_ctrl_header {
long msg_type; // Message queue ID, never change
uint32_t ctrl_msg_type; /* This is a way to share the same thread between multiple types of messages */
uint32_t version; /* version of the message type, for backwards incompatible changes */
} __attribute__((packed));
struct mangoapp_ctrl_msgid1_v1 {
struct mangoapp_ctrl_header hdr;
// When a field is set to 0, it should always mean "ignore" or "no changes"
uint8_t no_display; // 0x0 = ignore; 0x1 = disable; 0x2 = enable; 0x3 = toggle
uint8_t log_session; // 0x0 = ignore; 0x1 = start a session; 0x2 = stop the current session; 0x3 = toggle logging
char log_session_name[64]; // if byte 0 is NULL, ignore. Needs to be set when starting/toggling a session if we want to override the default name
uint8_t reload_config;
// WARNING: Always ADD fields, never remove or repurpose fields
} __attribute__((packed));

@ -1,198 +0,0 @@
#include <spdlog/spdlog.h>
#include <filesystem.h>
#include "battery.h"
namespace fs = ghc::filesystem;
using namespace std;
void BatteryStats::numBattery() {
int batteryCount = 0;
if (!fs::exists("/sys/class/power_supply/")) {
batteryCount = 0;
}
fs::path path("/sys/class/power_supply/");
for (auto& p : fs::directory_iterator(path)) {
string fileName = p.path().filename();
if (fileName.find("BAT") != std::string::npos) {
battPath[batteryCount] = p.path();
batteryCount += 1;
}
}
batt_count = batteryCount;
batt_check = true;
}
void BatteryStats::update() {
if (!batt_check) {
numBattery();
if (batt_count == 0) {
SPDLOG_ERROR("No battery found");
}
}
if (batt_count > 0) {
current_watt = getPower();
current_percent = getPercent();
remaining_time = getTimeRemaining();
}
}
float BatteryStats::getPercent()
{
float charge_n = 0;
float charge_f = 0;
for(int i = 0; i < batt_count; i++) {
string syspath = battPath[i];
string charge_now = syspath + "/charge_now";
string charge_full = syspath + "/charge_full";
string energy_now = syspath + "/energy_now";
string energy_full = syspath + "/energy_full";
string capacity = syspath + "/capacity";
if (fs::exists(charge_now)) {
std::ifstream input(charge_now);
std::string line;
if(std::getline(input, line)) {
charge_n += (stof(line) / 1000000);
}
std::ifstream input2(charge_full);
if(std::getline(input2, line)) {
charge_f += (stof(line) / 1000000);
}
}
else if (fs::exists(energy_now)) {
std::ifstream input(energy_now);
std::string line;
if(std::getline(input, line)) {
charge_n += (stof(line) / 1000000);
}
std::ifstream input2(energy_full);
if(std::getline(input2, line)) {
charge_f += (stof(line) / 1000000);
}
}
else {
// using /sys/class/power_supply/BAT*/capacity
// No way to get an accurate reading just average the percents if mutiple batteries
std::ifstream input(capacity);
std::string line;
if(std::getline(input, line)) {
charge_n += stof(line) / 100;
charge_f = batt_count;
}
}
}
return (charge_n / charge_f) * 100;
}
float BatteryStats::getPower() {
float current = 0;
float voltage = 0;
for(int i = 0; i < batt_count; i++) {
string syspath = battPath[i];
string current_power = syspath + "/current_now";
string current_voltage = syspath + "/voltage_now";
string power_now = syspath + "/power_now";
string status = syspath + "/status";
std::ifstream input(status);
std::string line;
if(std::getline(input,line)) {
current_status= line;
state[i]=current_status;
}
if (state[i] == "Charging" || state[i] == "Unknown" || state[i] == "Full") {
return 0;
}
if (fs::exists(current_power)) {
std::ifstream input(current_power);
std::string line;
if(std::getline(input,line)) {
current += (stof(line) / 1000000);
}
std::ifstream input2(current_voltage);
if(std::getline(input2, line)) {
voltage += (stof(line) / 1000000);
}
}
else {
std::ifstream input(power_now);
std::string line;
if(std::getline(input,line)) {
current += (stof(line) / 1000000);
voltage = 1;
}
}
}
return current * voltage;
}
float BatteryStats::getTimeRemaining(){
float current = 0;
float charge = 0;
for(int i = 0; i < batt_count; i++) {
string syspath = battPath[i];
string current_now = syspath + "/current_now";
string charge_now = syspath + "/charge_now";
string energy_now = syspath + "/energy_now";
string voltage_now = syspath + "/voltage_now";
string power_now = syspath + "/power_now";
if (fs::exists(current_now)) {
std::ifstream input(current_now);
std::string line;
if(std::getline(input, line)){
current_now_vec.push_back(stof(line));
}
} else if (fs::exists(power_now)){
float voltage = 0;
float power = 0;
std::ifstream input_power(power_now);
std::ifstream input_voltage(voltage_now);
std::string line;
if(std::getline(input_voltage, line)){
voltage = stof(line);
}
if(std::getline(input_power, line)){
power = stof(line);
}
current_now_vec.push_back(power / voltage);
}
if (fs::exists(charge_now)){
std::string line;
std::ifstream input(charge_now);
if(std::getline(input, line)){
charge += stof(line);
}
}
else if (fs::exists(energy_now)) {
float energy = 0;
float voltage = 0;
std::string line;
std::ifstream input_energy(energy_now);
std::ifstream input_voltage(voltage_now);
if(std::getline(input_energy, line)){
energy = stof(line);
}
if(std::getline(input_voltage, line)){
voltage = stof(line);
}
charge += energy / voltage;
}
if (current_now_vec.size() > 25)
current_now_vec.erase(current_now_vec.begin());
}
for(auto& current_now : current_now_vec){
current += current_now;
}
current /= current_now_vec.size();
return charge / current;
}
BatteryStats Battery_Stats;

@ -1,22 +0,0 @@
#pragma once
#include <string>
class BatteryStats{
public:
void numBattery();
void update();
float getPower();
float getPercent();
float getTimeRemaining();
std::string battPath[2];
float current_watt = 0;
float current_percent = 0;
float remaining_time = 0;
std::string current_status;
std::string state [2];
int batt_count=0;
bool batt_check = false;
std::vector<float> current_now_vec = {};
};
extern BatteryStats Battery_Stats;

@ -1,95 +1,59 @@
#include <vector>
#include <string>
#include <algorithm>
#include <spdlog/spdlog.h>
#include "blacklist.h"
#include "string_utils.h"
#include "file_utils.h"
std::string global_proc_name;
static std::string get_proc_name() {
// Note: It is possible to use GNU program_invocation_short_name.
const std::string proc_name = get_wine_exe_name(/*keep_ext=*/true);
if (!proc_name.empty()) {
return proc_name;
#ifdef _GNU_SOURCE_OFF
std::string p(program_invocation_name);
std::string proc_name = p.substr(p.find_last_of("/\\") + 1);
#else
std::string p = get_exe_path();
std::string proc_name;
if (ends_with(p, "wine-preloader") || ends_with(p, "wine64-preloader")) {
get_wine_exe_name(proc_name, true);
} else {
proc_name = p.substr(p.find_last_of("/\\") + 1);
}
return get_basename(get_exe_path());
#endif
return proc_name;
}
static std::vector<std::string> blacklist {
"Amazon Games UI.exe",
"Battle.net.exe",
"BethesdaNetLauncher.exe",
"EpicGamesLauncher.exe",
"IGOProxy.exe",
"IGOProxy64.exe",
"monado-service",
"Origin.exe",
"OriginThinSetupInternal.exe",
"steam",
"steamwebhelper",
"vrcompositor",
"gldriverquery",
"vulkandriverquery",
"Steam.exe",
"ffxivlauncher.exe",
"ffxivlauncher64.exe",
"LeagueClient.exe",
"LeagueClientUxRender.exe",
"SocialClubHelper.exe",
"EADesktop.exe",
"EALauncher.exe",
"StarCitizen_Launcher.exe",
"InsurgencyEAC.exe",
"GalaxyClient.exe",
"REDprelauncher.exe",
"REDlauncher.exe",
"gamescope",
"RSI Launcher.exe",
"tabtip.exe",
"steam.exe",
"explorer.exe",
"wine-preloader",
"iexplore.exe",
"rundll32.exe",
"Launcher", //Paradox Interactive Launcher
"steamwebhelper.exe",
"EpicWebHelper.exe",
"UplayWebCore.exe"
};
static bool check_blacklisted() {
static const std::vector<std::string> blacklist {
"Battle.net.exe",
"BethesdaNetLauncher.exe",
"EpicGamesLauncher.exe",
"IGOProxy.exe",
"IGOProxy64.exe",
"Origin.exe",
"OriginThinSetupInternal.exe",
"steam",
"steamwebhelper",
"gldriverquery",
"vulkandriverquery",
"Steam.exe",
"ffxivlauncher.exe",
"ffxivlauncher64.exe",
"LeagueClient.exe",
"LeagueClientUxRender.exe",
"SocialClubHelper.exe",
};
std::string proc_name = get_proc_name();
global_proc_name = proc_name;
bool blacklisted = std::find(blacklist.begin(), blacklist.end(), proc_name) != blacklist.end();
static bool printed = false;
if(blacklisted && !printed) {
printed = true;
SPDLOG_INFO("process '{}' is blacklisted in MangoHud", proc_name);
if(blacklisted) {
fprintf(stderr, "INFO: process %s is blacklisted in MangoHud\n", proc_name.c_str());
}
return blacklisted;
}
bool is_blacklisted(bool force_recheck) {
bool is_blacklisted() {
static bool blacklisted = check_blacklisted();
if (force_recheck)
blacklisted = check_blacklisted();
return blacklisted;
}
void add_blacklist(const std::string& new_item) {
// check if item exits in blacklist before adding new item
if(std::find(blacklist.begin(), blacklist.end(), new_item) != blacklist.end()) {
return;
}
blacklist.push_back (new_item);
is_blacklisted(true);
}

@ -1,9 +1,3 @@
#pragma once
#ifndef MANGOHUD_BLACKLIST_H
#define MANGOHUD_BLACKLIST_H
#include<string>
bool is_blacklisted(bool force_recheck = false);
void add_blacklist(const std::string& proc);
extern std::string global_proc_name;
#endif //MANGOHUD_BLACKLIST_H
bool is_blacklisted();

@ -3,16 +3,11 @@
#include <iostream>
#include <string.h>
#include <thread>
#include <unordered_map>
#include <string>
#include <spdlog/spdlog.h>
#include "config.h"
#include "file_utils.h"
#include "string_utils.h"
#include "hud_elements.h"
#include "blacklist.h"
void parseConfigLine(std::string line, std::unordered_map<std::string, std::string>& options) {
void parseConfigLine(std::string line, std::unordered_map<std::string,std::string>& options) {
std::string param, value;
if (line.find("#") != std::string::npos)
@ -27,78 +22,43 @@ void parseConfigLine(std::string line, std::unordered_map<std::string, std::stri
param = line.substr(0, equal);
trim(param);
trim(value);
if (!param.empty()){
HUDElements.options.push_back({param, value});
if (!param.empty())
options[param] = value;
}
}
static std::string get_program_dir() {
const std::string exe_path = get_exe_path();
if (exe_path.empty()) {
return std::string();
}
const auto n = exe_path.find_last_of('/');
if (n != std::string::npos) {
return exe_path.substr(0, n);
}
return std::string();
}
std::string get_program_name() {
const std::string exe_path = get_exe_path();
std::string basename = "unknown";
if (exe_path.empty()) {
return basename;
}
const auto n = exe_path.find_last_of('/');
if (n == std::string::npos) {
return basename;
}
if (n < exe_path.size() - 1) {
// An executable's name.
basename = exe_path.substr(n + 1);
}
return basename;
}
static void enumerate_config_files(std::vector<std::string>& paths) {
void enumerate_config_files(std::vector<std::string>& paths)
{
static const char *mangohud_dir = "/MangoHud/";
const std::string data_dir = get_data_dir();
const std::string config_dir = get_config_dir();
const std::string program_name = get_program_name();
if (config_dir.empty()) {
// If we can't find 'HOME' just abandon hope.
return;
}
paths.push_back(config_dir + mangohud_dir + "MangoHud.conf");
paths.push_back("/etc/MangoHud.conf");
if (is_blacklisted()) {
// Don't bother looking for conf file
return;
}
if (!program_name.empty()) {
paths.push_back(config_dir + mangohud_dir + program_name + ".conf");
}
const std::string program_dir = get_program_dir();
if (!program_dir.empty()) {
paths.push_back(program_dir + "/MangoHud.conf");
std::string env_data = get_data_dir();
std::string env_config = get_config_dir();
if (!env_config.empty())
paths.push_back(env_config + mangohud_dir + "MangoHud.conf");
std::string exe_path = get_exe_path();
auto n = exe_path.find_last_of('/');
if (!exe_path.empty() && n != std::string::npos && n < exe_path.size() - 1) {
// as executable's name
std::string basename = exe_path.substr(n + 1);
if (!env_config.empty())
paths.push_back(env_config + mangohud_dir + basename + ".conf");
// in executable's folder though not much sense in /usr/bin/
paths.push_back(exe_path.substr(0, n) + "/MangoHud.conf");
// find executable's path when run in Wine
if (!env_config.empty() && (basename == "wine-preloader" || basename == "wine64-preloader")) {
std::string name;
if (get_wine_exe_name(name)) {
paths.push_back(env_config + mangohud_dir + "wine-" + name + ".conf");
}
}
}
const std::string wine_program_name = get_wine_exe_name();
if (!wine_program_name.empty()) {
paths.push_back(config_dir + mangohud_dir + "wine-" + wine_program_name + ".conf");
}
}
void parseConfigFile(overlay_params& params) {
params.options.clear();
std::vector<std::string> paths;
const char *cfg_file = getenv("MANGOHUD_CONFIGFILE");
@ -106,24 +66,22 @@ void parseConfigFile(overlay_params& params) {
paths.push_back(cfg_file);
else
enumerate_config_files(paths);
#ifdef _WIN32
paths.push_back("C:\\mangohud\\MangoHud.conf");
#endif
std::string line;
for (auto p = paths.rbegin(); p != paths.rend(); p++) {
std::ifstream stream(*p);
if (!stream.good()) {
// printing just so user has an idea of possible configs
SPDLOG_DEBUG("skipping config: '{}' [ not found ]", *p);
std::cerr << "skipping config: " << *p << " [ not found ]" << std::endl;
continue;
}
stream.imbue(std::locale::classic());
SPDLOG_DEBUG("parsing config: '{}'", *p);
std::cerr << "parsing config: " << *p;
while (std::getline(stream, line))
{
parseConfigLine(line, params.options);
}
std::cerr << " [ ok ]" << std::endl;
params.config_file_path = *p;
return;
}

@ -1,11 +1,2 @@
#pragma once
#ifndef MANGOHUD_CONFIG_H
#define MANGOHUD_CONFIG_H
#include "overlay_params.h"
#include <string>
void parseConfigFile(overlay_params& p);
std::string get_program_name();
void parseConfigLine(std::string line, std::unordered_map<std::string, std::string>& options);
#endif //MANGOHUD_CONFIG_H

@ -1,217 +0,0 @@
#include <cstdint>
#include <assert.h>
#include <cerrno>
#include <cstring>
#include <sys/socket.h>
#include "mesa/util/os_socket.h"
#include "overlay.h"
#include "version.h"
#include "app/mangoapp.h"
int global_control_client;
using namespace std;
static void parse_command(overlay_params &params,
const char *cmd, unsigned cmdlen,
const char *param, unsigned paramlen)
{
if (!strncmp(cmd, "hud", cmdlen)) {
params.no_display = !params.no_display;
} else if (!strncmp(cmd, "logging", cmdlen)) {
if (param && param[0])
{
int value = atoi(param);
if (!value && logger->is_active())
logger->stop_logging();
else if (value > 0 && !logger->is_active())
logger->start_logging();
}
else
{
if (logger->is_active())
logger->stop_logging();
else
logger->start_logging();
}
} else if (!strncmp(cmd, "fcat", cmdlen)) {
params.enabled[OVERLAY_PARAM_ENABLED_fcat] = !params.enabled[OVERLAY_PARAM_ENABLED_fcat];
}
}
#define BUFSIZE 4096
/**
* This function will process commands through the control file.
*
* A command starts with a colon, followed by the command, and followed by an
* option '=' and a parameter. It has to end with a semi-colon. A full command
* + parameter looks like:
*
* :cmd=param;
*/
static void process_char(const int control_client, overlay_params &params, char c)
{
static char cmd[BUFSIZE];
static char param[BUFSIZE];
static unsigned cmdpos = 0;
static unsigned parampos = 0;
static bool reading_cmd = false;
static bool reading_param = false;
switch (c) {
case ':':
cmdpos = 0;
parampos = 0;
reading_cmd = true;
reading_param = false;
break;
case ';':
if (!reading_cmd)
break;
cmd[cmdpos++] = '\0';
param[parampos++] = '\0';
parse_command(params, cmd, cmdpos, param, parampos);
reading_cmd = false;
reading_param = false;
break;
case '=':
if (!reading_cmd)
break;
reading_param = true;
break;
default:
if (!reading_cmd)
break;
if (reading_param) {
/* overflow means an invalid parameter */
if (parampos >= BUFSIZE - 1) {
reading_cmd = false;
reading_param = false;
break;
}
param[parampos++] = c;
} else {
/* overflow means an invalid command */
if (cmdpos >= BUFSIZE - 1) {
reading_cmd = false;
break;
}
cmd[cmdpos++] = c;
}
}
}
void control_send(int control_client,
const char *cmd, unsigned cmdlen,
const char *param, unsigned paramlen)
{
unsigned msglen = 0;
char buffer[BUFSIZE];
assert(cmdlen + paramlen + 3 < BUFSIZE);
buffer[msglen++] = ':';
memcpy(&buffer[msglen], cmd, cmdlen);
msglen += cmdlen;
if (paramlen > 0) {
buffer[msglen++] = '=';
memcpy(&buffer[msglen], param, paramlen);
msglen += paramlen;
buffer[msglen++] = ';';
}
os_socket_send(control_client, buffer, msglen, MSG_NOSIGNAL);
}
static void control_send_connection_string(int control_client, const std::string& deviceName)
{
const char *controlVersionCmd = "MangoHudControlVersion";
const char *controlVersionString = "1";
control_send(control_client, controlVersionCmd, strlen(controlVersionCmd),
controlVersionString, strlen(controlVersionString));
const char *deviceCmd = "DeviceName";
control_send(control_client, deviceCmd, strlen(deviceCmd),
deviceName.c_str(), deviceName.size());
const char *versionCmd = "MangoHudVersion";
const char *versionString = "MangoHud " MANGOHUD_VERSION;
control_send(control_client, versionCmd, strlen(versionCmd),
versionString, strlen(versionString));
}
void control_client_check(int control, int& control_client, const std::string& deviceName)
{
/* Already connected, just return. */
if (control_client >= 0){
global_control_client = control_client;
return;
}
int socket = os_socket_accept(control);
if (socket == -1) {
if (errno != EAGAIN && errno != EWOULDBLOCK && errno != ECONNABORTED)
fprintf(stderr, "ERROR on socket: %s\n", strerror(errno));
return;
}
if (socket >= 0) {
os_socket_block(socket, false);
control_client = socket;
control_send_connection_string(control_client, deviceName);
}
}
static void control_client_disconnected(int& control_client)
{
os_socket_close(control_client);
control_client = -1;
}
void process_control_socket(int& control_client, overlay_params &params)
{
if (control_client >= 0) {
char buf[BUFSIZE];
while (true) {
ssize_t n = os_socket_recv(control_client, buf, BUFSIZE, 0);
if (n == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
/* nothing to read, try again later */
break;
}
if (errno != ECONNRESET)
fprintf(stderr, "ERROR on connection: %s\n", strerror(errno));
control_client_disconnected(control_client);
} else if (n == 0) {
/* recv() returns 0 when the client disconnects */
control_client_disconnected(control_client);
}
for (ssize_t i = 0; i < n; i++) {
process_char(control_client, params, buf[i]);
}
/* If we try to read BUFSIZE and receive BUFSIZE bytes from the
* socket, there's a good chance that there's still more data to be
* read, so we will try again. Otherwise, simply be done for this
* iteration and try again on the next frame.
*/
if (n < BUFSIZE)
break;
}
}
}

@ -1,5 +1,4 @@
#include "cpu.h"
#include <memory>
#include <string>
#include <iostream>
#include <fstream>
@ -8,10 +7,7 @@
#include <string.h>
#include <algorithm>
#include <regex>
#include <inttypes.h>
#include <spdlog/spdlog.h>
#include "string_utils.h"
#include "gpu.h"
#ifndef PROCDIR
#define PROCDIR "/proc"
@ -31,7 +27,7 @@
#include "file_utils.h"
static void calculateCPUData(CPUData& cpuData,
void calculateCPUData(CPUData& cpuData,
unsigned long long int usertime,
unsigned long long int nicetime,
unsigned long long int systemtime,
@ -118,13 +114,13 @@ bool CPUStats::Init()
m_cpuData.clear();
if (!file.is_open()) {
SPDLOG_ERROR("Failed to opening " PROCSTATFILE);
std::cerr << "Failed to opening " << PROCSTATFILE << std::endl;
return false;
}
do {
if (!std::getline(file, line)) {
SPDLOG_DEBUG("Failed to read all of " PROCSTATFILE);
std::cerr << "Failed to read all of " << PROCSTATFILE << std::endl;
return false;
} else if (starts_with(line, "cpu")) {
if (first) {
@ -135,7 +131,6 @@ bool CPUStats::Init()
CPUData cpu = {};
cpu.totalTime = 1;
cpu.totalPeriod = 1;
sscanf(line.c_str(), "cpu%4d ", &cpu.cpu_id);
m_cpuData.push_back(cpu);
} else if (starts_with(line, "btime ")) {
@ -156,19 +151,12 @@ bool CPUStats::Init()
return UpdateCPUData();
}
bool CPUStats::Reinit()
{
m_inited = false;
return Init();
}
//TODO take sampling interval into account?
bool CPUStats::UpdateCPUData()
{
unsigned long long int usertime, nicetime, systemtime, idletime;
unsigned long long int ioWait, irq, softIrq, steal, guest, guestnice;
int cpuid = -1;
size_t cpu_count = 0;
if (!m_inited)
return false;
@ -178,7 +166,7 @@ bool CPUStats::UpdateCPUData()
bool ret = false;
if (!file.is_open()) {
SPDLOG_ERROR("Failed to opening " PROCSTATFILE);
std::cerr << "Failed to opening " << PROCSTATFILE << std::endl;
return false;
}
@ -192,36 +180,28 @@ bool CPUStats::UpdateCPUData()
} else if (sscanf(line.c_str(), "cpu%4d %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu %16llu",
&cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest, &guestnice) == 11) {
//SPDLOG_DEBUG("Parsing 'cpu{}' line:{}", cpuid, line);
//std::cerr << "Parsing 'cpu" << cpuid << "' line:" << line << std::endl;
if (!ret) {
SPDLOG_DEBUG("Failed to parse 'cpu' line:{}", line);
//std::cerr << "Failed to parse 'cpu' line" << std::endl;
std::cerr << "Failed to parse 'cpu' line:" << line << std::endl;
return false;
}
if (cpuid < 0 /* can it? */) {
SPDLOG_DEBUG("Cpu id '{}' is out of bounds", cpuid);
if (cpuid < 0 /* can it? */ || (size_t)cpuid > m_cpuData.size()) {
std::cerr << "Cpu id '" << cpuid << "' is out of bounds" << std::endl;
return false;
}
if (cpu_count + 1 > m_cpuData.size() || m_cpuData[cpu_count].cpu_id != cpuid) {
SPDLOG_DEBUG("Cpu id '{}' is out of bounds or wrong index, reiniting", cpuid);
return Reinit();
}
CPUData& cpuData = m_cpuData[cpu_count];
CPUData& cpuData = m_cpuData[cpuid];
calculateCPUData(cpuData, usertime, nicetime, systemtime, idletime, ioWait, irq, softIrq, steal, guest, guestnice);
cpuid = -1;
cpu_count++;
} else {
break;
}
} while(true);
if (cpu_count < m_cpuData.size())
m_cpuData.resize(cpu_count);
m_cpuPeriod = (double)m_cpuData[0].totalPeriod / m_cpuData.size();
m_updatedCPUs = true;
return ret;
@ -229,234 +209,37 @@ bool CPUStats::UpdateCPUData()
bool CPUStats::UpdateCoreMhz() {
m_coreMhz.clear();
FILE *fp;
static bool scaling_freq = true;
if (scaling_freq){
for (auto& cpu : m_cpuData){
std::string path = "/sys/devices/system/cpu/cpu" + std::to_string(cpu.cpu_id) + "/cpufreq/scaling_cur_freq";
if ((fp = fopen(path.c_str(), "r"))){
int64_t temp;
if (fscanf(fp, "%" PRId64, &temp) != 1)
temp = 0;
cpu.mhz = temp / 1000;
fclose(fp);
scaling_freq = true;
} else {
scaling_freq = false;
break;
}
}
} else {
static std::ifstream cpuInfo(PROCCPUINFOFILE);
static std::string row;
size_t i = 0;
while (std::getline(cpuInfo, row) && i < m_cpuData.size()) {
if (row.find("MHz") != std::string::npos){
row = std::regex_replace(row, std::regex(R"([^0-9.])"), "");
if (!try_stoi(m_cpuData[i].mhz, row))
m_cpuData[i].mhz = 0;
i++;
}
std::ifstream cpuInfo(PROCCPUINFOFILE);
std::string row;
size_t i = 0;
while (std::getline(cpuInfo, row) && i < m_cpuData.size()) {
if (row.find("MHz") != std::string::npos){
row = std::regex_replace(row, std::regex(R"([^0-9.])"), "");
if (!try_stoi(m_cpuData[i].mhz, row))
m_cpuData[i].mhz = 0;
i++;
}
}
m_cpuDataTotal.cpu_mhz = 0;
for (auto data : m_cpuData)
if (data.mhz > m_cpuDataTotal.cpu_mhz)
m_cpuDataTotal.cpu_mhz = data.mhz;
return true;
}
bool CPUStats::ReadcpuTempFile(int& temp) {
if (!m_cpuTempFile)
return false;
rewind(m_cpuTempFile);
fflush(m_cpuTempFile);
bool ret = (fscanf(m_cpuTempFile, "%d", &temp) == 1);
temp = temp / 1000;
return ret;
}
bool CPUStats::UpdateCpuTemp() {
if (cpu_type == "APU"){
m_cpuDataTotal.temp = gpu_info.apu_cpu_temp;
return true;
} else {
int temp = 0;
bool ret = ReadcpuTempFile(temp);
m_cpuDataTotal.temp = temp;
return ret;
}
}
static bool get_cpu_power_k10temp(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_k10temp* powerData_k10temp = (CPUPowerData_k10temp*)cpuPowerData;
if(powerData_k10temp->corePowerFile || powerData_k10temp->socPowerFile)
{
rewind(powerData_k10temp->corePowerFile);
rewind(powerData_k10temp->socPowerFile);
fflush(powerData_k10temp->corePowerFile);
fflush(powerData_k10temp->socPowerFile);
int corePower, socPower;
if (fscanf(powerData_k10temp->corePowerFile, "%d", &corePower) != 1)
goto voltagebased;
if (fscanf(powerData_k10temp->socPowerFile, "%d", &socPower) != 1)
goto voltagebased;
power = (corePower + socPower) / 1000000;
return true;
}
voltagebased:
if (!powerData_k10temp->coreVoltageFile || !powerData_k10temp->coreCurrentFile || !powerData_k10temp->socVoltageFile || !powerData_k10temp->socCurrentFile)
return false;
rewind(powerData_k10temp->coreVoltageFile);
rewind(powerData_k10temp->coreCurrentFile);
rewind(powerData_k10temp->socVoltageFile);
rewind(powerData_k10temp->socCurrentFile);
fflush(powerData_k10temp->coreVoltageFile);
fflush(powerData_k10temp->coreCurrentFile);
fflush(powerData_k10temp->socVoltageFile);
fflush(powerData_k10temp->socCurrentFile);
int coreVoltage, coreCurrent;
int socVoltage, socCurrent;
if (fscanf(powerData_k10temp->coreVoltageFile, "%d", &coreVoltage) != 1)
return false;
if (fscanf(powerData_k10temp->coreCurrentFile, "%d", &coreCurrent) != 1)
return false;
if (fscanf(powerData_k10temp->socVoltageFile, "%d", &socVoltage) != 1)
return false;
if (fscanf(powerData_k10temp->socCurrentFile, "%d", &socCurrent) != 1)
return false;
power = (coreVoltage * coreCurrent + socVoltage * socCurrent) / 1000000;
return true;
}
static bool get_cpu_power_zenpower(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_zenpower* powerData_zenpower = (CPUPowerData_zenpower*)cpuPowerData;
if (!powerData_zenpower->corePowerFile || !powerData_zenpower->socPowerFile)
if (!m_cpuTempFile)
return false;
rewind(powerData_zenpower->corePowerFile);
rewind(powerData_zenpower->socPowerFile);
fflush(powerData_zenpower->corePowerFile);
fflush(powerData_zenpower->socPowerFile);
int corePower, socPower;
if (fscanf(powerData_zenpower->corePowerFile, "%d", &corePower) != 1)
return false;
if (fscanf(powerData_zenpower->socPowerFile, "%d", &socPower) != 1)
m_cpuDataTotal.temp = 0;
rewind(m_cpuTempFile);
fflush(m_cpuTempFile);
if (fscanf(m_cpuTempFile, "%d", &m_cpuDataTotal.temp) != 1)
return false;
power = (corePower + socPower) / 1000000;
m_cpuDataTotal.temp /= 1000;
return true;
}
static bool get_cpu_power_zenergy(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_zenergy* powerData_zenergy = (CPUPowerData_zenergy*)cpuPowerData;
if (!powerData_zenergy->energyCounterFile)
return false;
rewind(powerData_zenergy->energyCounterFile);
fflush(powerData_zenergy->energyCounterFile);
uint64_t energyCounterValue = 0;
if (fscanf(powerData_zenergy->energyCounterFile, "%" SCNu64, &energyCounterValue) != 1)
return false;
Clock::time_point now = Clock::now();
Clock::duration timeDiff = now - powerData_zenergy->lastCounterValueTime;
int64_t timeDiffMicro = std::chrono::duration_cast<std::chrono::microseconds>(timeDiff).count();
uint64_t energyCounterDiff = energyCounterValue - powerData_zenergy->lastCounterValue;
if (powerData_zenergy->lastCounterValue > 0 && energyCounterValue > powerData_zenergy->lastCounterValue)
power = (float) energyCounterDiff / (float) timeDiffMicro;
powerData_zenergy->lastCounterValue = energyCounterValue;
powerData_zenergy->lastCounterValueTime = now;
return true;
}
static bool get_cpu_power_rapl(CPUPowerData* cpuPowerData, float& power) {
CPUPowerData_rapl* powerData_rapl = (CPUPowerData_rapl*)cpuPowerData;
if (!powerData_rapl->energyCounterFile)
return false;
rewind(powerData_rapl->energyCounterFile);
fflush(powerData_rapl->energyCounterFile);
uint64_t energyCounterValue = 0;
if (fscanf(powerData_rapl->energyCounterFile, "%" SCNu64, &energyCounterValue) != 1)
return false;
Clock::time_point now = Clock::now();
Clock::duration timeDiff = now - powerData_rapl->lastCounterValueTime;
int64_t timeDiffMicro = std::chrono::duration_cast<std::chrono::microseconds>(timeDiff).count();
uint64_t energyCounterDiff = energyCounterValue - powerData_rapl->lastCounterValue;
if (powerData_rapl->lastCounterValue > 0 && energyCounterValue > powerData_rapl->lastCounterValue)
power = energyCounterDiff / timeDiffMicro;
powerData_rapl->lastCounterValue = energyCounterValue;
powerData_rapl->lastCounterValueTime = now;
return true;
}
static bool get_cpu_power_amdgpu(float& power) {
power = gpu_info.apu_cpu_power;
return true;
}
bool CPUStats::UpdateCpuPower() {
if(!m_cpuPowerData)
return false;
float power = 0;
switch(m_cpuPowerData->source) {
case CPU_POWER_K10TEMP:
if (!get_cpu_power_k10temp(m_cpuPowerData.get(), power)) return false;
break;
case CPU_POWER_ZENPOWER:
if (!get_cpu_power_zenpower(m_cpuPowerData.get(), power)) return false;
break;
case CPU_POWER_ZENERGY:
if (!get_cpu_power_zenergy(m_cpuPowerData.get(), power)) return false;
break;
case CPU_POWER_RAPL:
if (!get_cpu_power_rapl(m_cpuPowerData.get(), power)) return false;
break;
case CPU_POWER_AMDGPU:
if (!get_cpu_power_amdgpu(power)) return false;
break;
default:
return false;
}
m_cpuDataTotal.power = power;
return true;
}
static bool find_input(const std::string& path, const char* input_prefix, std::string& input, const std::string& name)
static bool find_temp_input(const std::string path, std::string& input, const std::string& name)
{
auto files = ls(path.c_str(), input_prefix, LS_FILES);
auto files = ls(path.c_str(), "temp", LS_FILES);
for (auto& file : files) {
if (!ends_with(file, "_label"))
continue;
@ -469,16 +252,15 @@ static bool find_input(const std::string& path, const char* input_prefix, std::s
if (uscore != std::string::npos) {
file.erase(uscore, std::string::npos);
input = path + "/" + file + "_input";
//9 characters should not overflow the 32-bit int
return std::stoi(read_line(input).substr(0, 9)) > 0;
return true;
}
}
return false;
}
static bool find_fallback_input(const std::string& path, const char* input_prefix, std::string& input)
static bool find_fallback_temp_input(const std::string path, std::string& input)
{
auto files = ls(path.c_str(), input_prefix, LS_FILES);
auto files = ls(path.c_str(), "temp", LS_FILES);
if (!files.size())
return false;
@ -487,7 +269,9 @@ static bool find_fallback_input(const std::string& path, const char* input_prefi
if (!ends_with(file, "_input"))
continue;
input = path + "/" + file;
SPDLOG_DEBUG("fallback cpu {} input: {}", input_prefix, input);
#ifndef NDEBUG
std::cerr << "fallback cpu temp input: " << input << "\n";
#endif
return true;
}
return false;
@ -504,171 +288,27 @@ bool CPUStats::GetCpuFile() {
for (auto& dir : dirs) {
path = hwmon + dir;
name = read_line(path + "/name");
SPDLOG_DEBUG("hwmon: sensor name: {}", name);
if (name == "coretemp") {
find_input(path, "temp", input, "Package id 0");
#ifndef NDEBUG
std::cerr << "hwmon: sensor name: " << name << std::endl;
#endif
if (name == "coretemp" && find_temp_input(path, input, "Package id 0")) {
break;
}
else if ((name == "zenpower" || name == "k10temp")) {
if (!find_input(path, "temp", input, "Tdie"))
find_input(path, "temp", input, "Tctl");
break;
} else if (name == "atk0110") {
find_input(path, "temp", input, "CPU Temperature");
else if ((name == "zenpower" || name == "k10temp") && find_temp_input(path, input, "Tdie")) {
break;
} else if (name == "it8603") {
find_input(path, "temp", input, "temp1");
break;
} else if (starts_with(name, "nct")) {
// Only break if nct module has TSI0_TEMP node
if (find_input(path, "temp", input, "TSI0_TEMP"))
break;
} else if (name == "asusec") {
find_input(path, "temp", input, "CPU");
break;
} else {
path.clear();
}
}
if (path.empty() || (!file_exists(input) && !find_fallback_input(path, "temp", input))) {
SPDLOG_ERROR("Could not find cpu temp sensor location");
if (!file_exists(input) && !find_fallback_temp_input(path, input)) {
std::cerr << "MANGOHUD: Could not find cpu temp sensor location" << std::endl;
return false;
} else {
SPDLOG_DEBUG("hwmon: using input: {}", input);
#ifndef NDEBUG
std::cerr << "hwmon: using input: " << input << std::endl;
#endif
m_cpuTempFile = fopen(input.c_str(), "r");
}
return true;
}
static CPUPowerData_k10temp* init_cpu_power_data_k10temp(const std::string path) {
auto powerData = std::make_unique<CPUPowerData_k10temp>();
std::string coreVoltageInput, coreCurrentInput;
std::string socVoltageInput, socCurrentInput;
std::string socPowerInput, corePowerInput;
if(find_input(path, "power", corePowerInput, "Pcore") && find_input(path, "power", socPowerInput, "Psoc")) {
powerData->corePowerFile = fopen(corePowerInput.c_str(), "r");
powerData->socPowerFile = fopen(socPowerInput.c_str(), "r");
SPDLOG_DEBUG("hwmon: using input: {}", corePowerInput);
SPDLOG_DEBUG("hwmon: using input: {}", socPowerInput);
return powerData.release();
}
if(!find_input(path, "in", coreVoltageInput, "Vcore")) return nullptr;
if(!find_input(path, "curr", coreCurrentInput, "Icore")) return nullptr;
if(!find_input(path, "in", socVoltageInput, "Vsoc")) return nullptr;
if(!find_input(path, "curr", socCurrentInput, "Isoc")) return nullptr;
SPDLOG_DEBUG("hwmon: using input: {}", coreVoltageInput);
SPDLOG_DEBUG("hwmon: using input: {}", coreCurrentInput);
SPDLOG_DEBUG("hwmon: using input: {}", socVoltageInput);
SPDLOG_DEBUG("hwmon: using input: {}", socCurrentInput);
powerData->coreVoltageFile = fopen(coreVoltageInput.c_str(), "r");
powerData->coreCurrentFile = fopen(coreCurrentInput.c_str(), "r");
powerData->socVoltageFile = fopen(socVoltageInput.c_str(), "r");
powerData->socCurrentFile = fopen(socCurrentInput.c_str(), "r");
return powerData.release();
}
static CPUPowerData_zenpower* init_cpu_power_data_zenpower(const std::string path) {
auto powerData = std::make_unique<CPUPowerData_zenpower>();
std::string corePowerInput, socPowerInput;
if(!find_input(path, "power", corePowerInput, "SVI2_P_Core")) return nullptr;
if(!find_input(path, "power", socPowerInput, "SVI2_P_SoC")) return nullptr;
SPDLOG_DEBUG("hwmon: using input: {}", corePowerInput);
SPDLOG_DEBUG("hwmon: using input: {}", socPowerInput);
powerData->corePowerFile = fopen(corePowerInput.c_str(), "r");
powerData->socPowerFile = fopen(socPowerInput.c_str(), "r");
return powerData.release();
}
static CPUPowerData_zenergy* init_cpu_power_data_zenergy(const std::string path) {
auto powerData = std::make_unique<CPUPowerData_zenergy>();
std::string energyCounterPath;
if(!find_input(path, "energy", energyCounterPath, "Esocket0")) return nullptr;
SPDLOG_DEBUG("hwmon: using input: {}", energyCounterPath);
powerData->energyCounterFile = fopen(energyCounterPath.c_str(), "r");
return powerData.release();
}
static CPUPowerData_rapl* init_cpu_power_data_rapl(const std::string path) {
auto powerData = std::make_unique<CPUPowerData_rapl>();
std::string energyCounterPath = path + "/energy_uj";
if (!file_exists(energyCounterPath)) return nullptr;
powerData->energyCounterFile = fopen(energyCounterPath.c_str(), "r");
return powerData.release();
}
bool CPUStats::InitCpuPowerData() {
if(m_cpuPowerData != nullptr)
return true;
std::string name, path;
std::string hwmon = "/sys/class/hwmon/";
bool intel = false;
CPUPowerData* cpuPowerData = nullptr;
auto dirs = ls(hwmon.c_str());
for (auto& dir : dirs) {
path = hwmon + dir;
name = read_line(path + "/name");
SPDLOG_DEBUG("hwmon: sensor name: {}", name);
if (name == "k10temp") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_k10temp(path);
} else if (name == "zenpower") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenpower(path);
break;
} else if (name == "zenergy") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_zenergy(path);
break;
} else if (name == "coretemp") {
intel = true;
}
}
if (!cpuPowerData && intel) {
std::string powercap = "/sys/class/powercap/";
auto powercap_dirs = ls(powercap.c_str());
for (auto& dir : powercap_dirs) {
path = powercap + dir;
name = read_line(path + "/name");
SPDLOG_DEBUG("powercap: name: {}", name);
if (name == "package-0") {
cpuPowerData = (CPUPowerData*)init_cpu_power_data_rapl(path);
break;
}
}
}
if (!cpuPowerData && !intel) {
auto powerData = std::make_unique<CPUPowerData_amdgpu>();
cpuPowerData = (CPUPowerData*)powerData.release();
}
if(cpuPowerData == nullptr) {
SPDLOG_ERROR("Failed to initialize CPU power data");
return false;
}
m_cpuPowerData.reset(cpuPowerData);
return true;
}
CPUStats cpuStats;

@ -1,16 +1,6 @@
#pragma once
#ifndef MANGOHUD_CPU_H
#define MANGOHUD_CPU_H
#include <vector>
#include <cstdint>
#include <cstdio>
#include <memory>
#include <string>
#ifdef WIN32
#include <windows.h>
#endif
#include "timing.hpp"
typedef struct CPUData_ {
unsigned long long int totalTime;
@ -38,119 +28,17 @@ typedef struct CPUData_ {
unsigned long long int softIrqPeriod;
unsigned long long int stealPeriod;
unsigned long long int guestPeriod;
int cpu_id;
float percent;
int mhz;
int temp;
int cpu_mhz;
float power;
} CPUData;
enum {
CPU_POWER_K10TEMP,
CPU_POWER_ZENPOWER,
CPU_POWER_ZENERGY,
CPU_POWER_RAPL,
CPU_POWER_AMDGPU
};
struct CPUPowerData {
virtual ~CPUPowerData() = default;
int source;
};
struct CPUPowerData_k10temp : public CPUPowerData {
CPUPowerData_k10temp() {
this->source = CPU_POWER_K10TEMP;
};
~CPUPowerData_k10temp() {
if(this->coreVoltageFile)
fclose(this->coreVoltageFile);
if(this->coreCurrentFile)
fclose(this->coreCurrentFile);
if(this->socVoltageFile)
fclose(this->socVoltageFile);
if(this->socCurrentFile)
fclose(this->socCurrentFile);
if(this->corePowerFile)
fclose(this->corePowerFile);
if(this->socPowerFile)
fclose(this->socPowerFile);
};
FILE* coreVoltageFile {nullptr};
FILE* coreCurrentFile {nullptr};
FILE* socVoltageFile {nullptr};
FILE* socCurrentFile {nullptr};
FILE* corePowerFile {nullptr};
FILE* socPowerFile {nullptr};
};
struct CPUPowerData_zenpower : public CPUPowerData {
CPUPowerData_zenpower() {
this->source = CPU_POWER_ZENPOWER;
};
~CPUPowerData_zenpower() {
if(this->corePowerFile)
fclose(this->corePowerFile);
if(this->socPowerFile)
fclose(this->socPowerFile);
};
FILE* corePowerFile {nullptr};
FILE* socPowerFile {nullptr};
};
struct CPUPowerData_zenergy : public CPUPowerData {
CPUPowerData_zenergy() {
this->source = CPU_POWER_ZENERGY;
this->lastCounterValue = 0;
this->lastCounterValueTime = Clock::now();
};
~CPUPowerData_zenergy() {
if(this->energyCounterFile)
fclose(this->energyCounterFile);
};
FILE* energyCounterFile {nullptr};
uint64_t lastCounterValue;
Clock::time_point lastCounterValueTime;
};
struct CPUPowerData_rapl : public CPUPowerData {
CPUPowerData_rapl() {
this->source = CPU_POWER_RAPL;
this->lastCounterValue = 0;
this->lastCounterValueTime = Clock::now();
};
~CPUPowerData_rapl() {
if(this->energyCounterFile)
fclose(this->energyCounterFile);
};
FILE* energyCounterFile {nullptr};
uint64_t lastCounterValue;
Clock::time_point lastCounterValueTime;
};
struct CPUPowerData_amdgpu : public CPUPowerData {
CPUPowerData_amdgpu() {
this->source = CPU_POWER_AMDGPU;
};
};
class CPUStats
{
public:
CPUStats();
~CPUStats();
bool Init();
bool Reinit();
bool Updated()
{
return m_updatedCPUs;
@ -159,10 +47,7 @@ public:
bool UpdateCPUData();
bool UpdateCoreMhz();
bool UpdateCpuTemp();
bool UpdateCpuPower();
bool ReadcpuTempFile(int& temp);
bool GetCpuFile();
bool InitCpuPowerData();
double GetCPUPeriod() { return m_cpuPeriod; }
const std::vector<CPUData>& GetCPUData() const {
@ -171,7 +56,6 @@ public:
const CPUData& GetCPUDataTotal() const {
return m_cpuDataTotal;
}
std::string cpu_type = "CPU";
private:
unsigned long long int m_boottime = 0;
std::vector<CPUData> m_cpuData;
@ -181,11 +65,6 @@ private:
bool m_updatedCPUs = false; // TODO use caching or just update?
bool m_inited = false;
FILE *m_cpuTempFile = nullptr;
std::unique_ptr<CPUPowerData> m_cpuPowerData;
};
extern CPUStats cpuStats;
#ifdef WIN32
uint64_t FileTimeToInt64( const FILETIME& ft );
#endif
#endif //MANGOHUD_CPU_H

@ -1,65 +0,0 @@
#include <windows.h>
#include <thread>
#include <string.h>
#include "cpu.h"
#include <iostream>
#define SystemProcessorPerformanceInformation 0x8
#define SystemBasicInformation 0x0
FILETIME last_userTime, last_kernelTime, last_idleTime;
uint64_t FileTimeToInt64( const FILETIME& ft ) {
ULARGE_INTEGER uli = { 0 };
uli.LowPart = ft.dwLowDateTime;
uli.HighPart = ft.dwHighDateTime;
return uli.QuadPart;
}
bool CPUStats::UpdateCPUData()
{
#define NUMBER_OF_PROCESSORS (8)
#define PROCESSOR_BUFFER_SIZE (NUMBER_OF_PROCESSORS * 8)
static ULONG64 ProcessorIdleTimeBuffer [ PROCESSOR_BUFFER_SIZE ];
FILETIME IdleTime, KernelTime, UserTime;
static unsigned long long PrevTotal = 0;
static unsigned long long PrevIdle = 0;
static unsigned long long PrevUser = 0;
unsigned long long ThisTotal;
unsigned long long ThisIdle, ThisKernel, ThisUser;
unsigned long long TotalSinceLast, IdleSinceLast, UserSinceLast;
// GET THE KERNEL / USER / IDLE times.
// And oh, BTW, kernel time includes idle time
GetSystemTimes( & IdleTime, & KernelTime, & UserTime);
ThisIdle = FileTimeToInt64(IdleTime);
ThisKernel = FileTimeToInt64 (KernelTime);
ThisUser = FileTimeToInt64 (UserTime);
ThisTotal = ThisKernel + ThisUser;
TotalSinceLast = ThisTotal - PrevTotal;
IdleSinceLast = ThisIdle - PrevIdle;
UserSinceLast = ThisUser - PrevUser;
double Headroom;
Headroom = (double)IdleSinceLast / (double)TotalSinceLast ;
double Load;
Load = 1.0 - Headroom;
Headroom *= 100.0; // to make it percent
Load *= 100.0; // percent
PrevTotal = ThisTotal;
PrevIdle = ThisIdle;
PrevUser = ThisUser;
// print results to output window of VS when run in Debug
m_cpuDataTotal.percent = Load;
return true;
}
CPUStats::CPUStats()
{
}
CPUStats::~CPUStats()
{
}
CPUStats cpuStats;

File diff suppressed because it is too large Load Diff

@ -1,370 +0,0 @@
#pragma once
#ifndef MANGOHUD_DBUS_HELPERS
#define MANGOHUD_DBUS_HELPERS
#include <vector>
#include <spdlog/spdlog.h>
#include <signal.h>
#include "loaders/loader_dbus.h"
namespace DBus_helpers {
namespace detail {
// clang-format off
template<class T> struct dbus_type_traits{};
template<> struct dbus_type_traits<bool> { const int value = DBUS_TYPE_BOOLEAN; const bool is_fixed = true; };
template<> struct dbus_type_traits<uint8_t> { const int value = DBUS_TYPE_BYTE; const bool is_fixed = true; };
template<> struct dbus_type_traits<uint16_t> { const int value = DBUS_TYPE_UINT16; const bool is_fixed = true; };
template<> struct dbus_type_traits<uint32_t> { const int value = DBUS_TYPE_UINT32; const bool is_fixed = true; };
template<> struct dbus_type_traits<uint64_t> { const int value = DBUS_TYPE_UINT64; const bool is_fixed = true; };
template<> struct dbus_type_traits<int16_t> { const int value = DBUS_TYPE_INT16; const bool is_fixed = true; };
template<> struct dbus_type_traits<int32_t> { const int value = DBUS_TYPE_INT32; const bool is_fixed = true; };
template<> struct dbus_type_traits<int64_t> { const int value = DBUS_TYPE_INT64; const bool is_fixed = true; };
template<> struct dbus_type_traits<double> { const int value = DBUS_TYPE_DOUBLE; const bool is_fixed = true; };
template<> struct dbus_type_traits<const char*> { const int value = DBUS_TYPE_STRING; const bool is_fixed = false; };
// clang-format on
template <class T>
const int dbus_type_identifier = dbus_type_traits<T>().value;
template <class T>
const bool is_fixed = dbus_type_traits<T>().is_fixed;
} // namespace detail
class DBusMessageIter_wrap {
public:
DBusMessageIter_wrap(DBusMessage* msg, libdbus_loader* loader);
DBusMessageIter_wrap(DBusMessageIter iter, libdbus_loader* loader);
// Type accessors
int type() const noexcept { return m_type; }
bool is_unsigned() const noexcept;
bool is_signed() const noexcept;
bool is_string() const noexcept;
bool is_double() const noexcept;
bool is_primitive() const noexcept;
bool is_array() const noexcept;
operator bool() const noexcept { return type() != DBUS_TYPE_INVALID; }
// Value accessors
// Primitives
template <class T>
auto get_primitive() -> T;
auto get_unsigned() -> uint64_t;
auto get_signed() -> int64_t;
auto get_stringified() -> std::string;
// Composites
auto get_array_iter() -> DBusMessageIter_wrap;
auto get_dict_entry_iter() -> DBusMessageIter_wrap;
// Looping
template <class Callable>
void array_for_each(Callable);
template <class Callable>
void array_for_each_stringify(Callable);
template <class T, class Callable>
void array_for_each_value(Callable);
template <class Callable>
void string_map_for_each(Callable);
template <class Callable>
void string_multimap_for_each_stringify(Callable);
auto next() -> DBusMessageIter_wrap&;
private:
DBusMessageIter resolve_variants() {
auto iter = m_Iter;
auto field_type = m_DBus->message_iter_get_arg_type(&m_Iter);
while (field_type == DBUS_TYPE_VARIANT) {
m_DBus->message_iter_recurse(&iter, &iter);
field_type = m_DBus->message_iter_get_arg_type(&iter);
}
return iter;
}
DBusMessageIter m_Iter;
DBusMessageIter m_resolved_iter;
int m_type;
libdbus_loader* m_DBus;
};
DBusMessageIter_wrap::DBusMessageIter_wrap(DBusMessage* msg,
libdbus_loader* loader) {
m_DBus = loader;
if (msg) {
m_DBus->message_iter_init(msg, &m_Iter);
m_resolved_iter = resolve_variants();
m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter);
} else {
m_type = DBUS_TYPE_INVALID;
}
}
DBusMessageIter_wrap::DBusMessageIter_wrap(DBusMessageIter iter,
libdbus_loader* loader)
: m_Iter(iter), m_DBus(loader) {
m_resolved_iter = resolve_variants();
m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter);
}
bool DBusMessageIter_wrap::is_unsigned() const noexcept {
return ((type() == DBUS_TYPE_BYTE) || (type() == DBUS_TYPE_INT16) ||
(type() == DBUS_TYPE_INT32) || (type() == DBUS_TYPE_INT64));
}
bool DBusMessageIter_wrap::is_signed() const noexcept {
return ((type() == DBUS_TYPE_INT16) || (type() == DBUS_TYPE_INT32) ||
(type() == DBUS_TYPE_INT64));
}
bool DBusMessageIter_wrap::is_string() const noexcept {
return (type() == DBUS_TYPE_STRING);
}
bool DBusMessageIter_wrap::is_double() const noexcept {
return (type() == DBUS_TYPE_DOUBLE);
}
bool DBusMessageIter_wrap::is_primitive() const noexcept {
return (is_double() || is_signed() || is_unsigned() || is_string());
}
bool DBusMessageIter_wrap::is_array() const noexcept {
return (type() == DBUS_TYPE_ARRAY);
}
template <class T>
auto DBusMessageIter_wrap::get_primitive() -> T {
auto requested_type = detail::dbus_type_identifier<T>;
if (type() == DBUS_TYPE_OBJECT_PATH && requested_type == DBUS_TYPE_STRING) {
// no special type, just a string
}
else if (requested_type != type()) {
SPDLOG_ERROR("Type mismatch: '{}' vs '{}'",
((char)requested_type), (char)type());
#ifndef NDEBUG
raise(SIGTRAP);
#endif
return T();
}
T ret;
m_DBus->message_iter_get_basic(&m_resolved_iter, &ret);
return ret;
}
template <>
auto DBusMessageIter_wrap::get_primitive<std::string>() -> std::string {
auto s = get_primitive<const char*>();
if (!s)
return std::string();
return std::string(s);
}
uint64_t DBusMessageIter_wrap::get_unsigned() {
auto t = type();
switch (t) {
case DBUS_TYPE_BYTE:
return get_primitive<uint8_t>();
case DBUS_TYPE_UINT16:
return get_primitive<uint16_t>();
case DBUS_TYPE_UINT32:
return get_primitive<uint32_t>();
case DBUS_TYPE_UINT64:
return get_primitive<uint64_t>();
default:
return 0;
}
}
int64_t DBusMessageIter_wrap::get_signed() {
auto t = type();
switch (t) {
case DBUS_TYPE_INT16:
return get_primitive<int16_t>();
case DBUS_TYPE_INT32:
return get_primitive<int32_t>();
case DBUS_TYPE_INT64:
return get_primitive<int64_t>();
default:
return 0;
}
}
auto DBusMessageIter_wrap::get_stringified() -> std::string {
if (is_string()) return get_primitive<std::string>();
if (is_unsigned()) return std::to_string(get_unsigned());
if (is_signed()) return std::to_string(get_signed());
if (is_double()) return std::to_string(get_primitive<double>());
SPDLOG_ERROR("stringify failed");
return std::string();
}
auto DBusMessageIter_wrap::get_array_iter() -> DBusMessageIter_wrap {
if (!is_array()) {
SPDLOG_ERROR("Not an array; {}", (char)type());
return DBusMessageIter_wrap(DBusMessageIter{}, m_DBus);
}
DBusMessageIter ret;
m_DBus->message_iter_recurse(&m_resolved_iter, &ret);
return DBusMessageIter_wrap(ret, m_DBus);
}
auto DBusMessageIter_wrap::get_dict_entry_iter() -> DBusMessageIter_wrap {
if (type() != DBUS_TYPE_DICT_ENTRY) {
SPDLOG_ERROR("Not a dict entry {}", (char)type());
return DBusMessageIter_wrap(DBusMessageIter{}, m_DBus);
}
DBusMessageIter ret;
m_DBus->message_iter_recurse(&m_resolved_iter, &ret);
return DBusMessageIter_wrap(ret, m_DBus);
}
template <class T, class Callable>
void DBusMessageIter_wrap::array_for_each_value(Callable action) {
auto iter = get_array_iter();
for (; iter; iter.next()) {
action(iter.get_primitive<T>());
}
}
template <class Callable>
void DBusMessageIter_wrap::array_for_each(Callable action) {
auto iter = get_array_iter();
for (; iter; iter.next()) {
action(iter);
}
}
template <class Callable>
void DBusMessageIter_wrap::array_for_each_stringify(Callable action) {
auto iter = get_array_iter();
for (; iter; iter.next()) {
action(iter.get_stringified());
}
}
template <class T>
void DBusMessageIter_wrap::string_map_for_each(T action) {
auto iter = get_array_iter();
for (; iter; iter.next()) {
auto it = iter.get_dict_entry_iter();
auto key = it.get_primitive<std::string>();
it.next();
action(key, it);
}
}
template <class T>
void DBusMessageIter_wrap::string_multimap_for_each_stringify(T action) {
string_map_for_each([&action](const std::string& key, DBusMessageIter_wrap it) {
if (it.is_array()) {
it.array_for_each_stringify(
[&](const std::string& val) { action(key, val); });
} else if (it.is_primitive()) {
action(key, it.get_stringified());
}
});
}
auto DBusMessageIter_wrap::next() -> DBusMessageIter_wrap& {
if (!*this) return *this;
m_DBus->message_iter_next(&m_Iter);
// Resolve any variants
m_resolved_iter = resolve_variants();
m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter);
return *this;
}
class DBusMessage_wrap {
public:
DBusMessage_wrap(DBusMessage* msg, libdbus_loader* ldr, bool owning = false)
: m_owning(owning), m_msg(msg), m_DBus(ldr) {}
~DBusMessage_wrap() { free_if_owning(); }
DBusMessage_wrap(const DBusMessage_wrap&) = delete;
DBusMessage_wrap(DBusMessage_wrap&&) = default;
operator bool() const noexcept { return m_msg != nullptr; }
template <class T>
DBusMessage_wrap& argument(T arg);
DBusMessage_wrap send_with_reply_and_block(DBusConnection* conn,
int timeout);
DBusMessageIter_wrap iter() { return DBusMessageIter_wrap(m_msg, m_DBus); }
static DBusMessage_wrap new_method_call(const std::string& bus_name,
const std::string& path,
const std::string& iface,
const std::string& method,
libdbus_loader* loader);
private:
void free_if_owning();
bool m_owning;
DBusMessage* m_msg;
libdbus_loader* m_DBus;
std::vector<std::string> m_args;
};
template <class T>
DBusMessage_wrap& DBusMessage_wrap::argument(T arg) {
if (!m_msg) return *this;
if (!m_DBus->message_append_args(m_msg, detail::dbus_type_identifier<T>,
&arg, DBUS_TYPE_INVALID)) {
free_if_owning();
}
return *this;
}
template <>
DBusMessage_wrap& DBusMessage_wrap::argument<const std::string&>(
const std::string& str) {
return argument<const char*>(str.c_str());
}
DBusMessage_wrap DBusMessage_wrap::send_with_reply_and_block(
DBusConnection* conn, int timeout) {
if (!m_msg) {
return DBusMessage_wrap(nullptr, m_DBus);
}
DBusError err;
m_DBus->error_init(&err);
auto reply = m_DBus->connection_send_with_reply_and_block(conn, m_msg,
timeout, &err);
if (reply == nullptr) {
SPDLOG_ERROR("[{}]: {}", __func__, err.message);
free_if_owning();
m_DBus->error_free(&err);
}
return DBusMessage_wrap(reply, m_DBus, true);
}
DBusMessage_wrap DBusMessage_wrap::new_method_call(const std::string& bus_name,
const std::string& path,
const std::string& iface,
const std::string& method,
libdbus_loader* loader) {
auto msg = loader->message_new_method_call(
(bus_name.empty()) ? nullptr : bus_name.c_str(), path.c_str(),
(iface.empty()) ? nullptr : iface.c_str(), method.c_str());
return DBusMessage_wrap(msg, loader, true);
}
void DBusMessage_wrap::free_if_owning() {
if (m_msg and m_owning) {
m_DBus->message_unref(m_msg);
}
m_msg = nullptr;
}
} // namespace DBus_helpers
#endif // MANGOHUD_DBUS_HELPERS

@ -1,148 +1,137 @@
#pragma once
#ifndef MANGOHUD_DBUS_INFO_H
#define MANGOHUD_DBUS_INFO_H
#ifdef HAVE_DBUS
#include <array>
#include <functional>
#include <map>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
#include <mutex>
#include "loaders/loader_dbus.h"
struct metadata {
// std::vector<std::string> artists;
std::string artists; // pre-concatenate
//std::vector<std::string> artists;
std::string artists; // pre-concatenate
std::string title;
std::string album;
std::string something;
std::string artUrl;
bool playing = false;
bool valid = false;
bool got_song_data = false;
bool got_playback_data = false;
};
struct mp_fmt
{
std::string text;
float width;
};
struct mutexed_metadata {
std::mutex mtx;
metadata meta;
struct {
float pos;
float longest;
int dir = -1;
bool needs_recalc = true;
bool needs_recalc;
std::vector<mp_fmt> formatted;
float tw0;
float tw1;
float tw2;
} ticker;
bool valid = false;
std::mutex mutex;
void clear()
{
artists.clear();
title.clear();
album.clear();
artUrl.clear();
ticker = {};
ticker.dir = -1;
valid = false;
}
};
enum SignalType {
enum SignalType
{
ST_NAMEOWNERCHANGED,
ST_PROPERTIESCHANGED,
};
extern struct mutexed_metadata main_metadata;
namespace dbusmgr {
class dbus_manager;
using signal_handler_func = bool (dbus_manager::*)(DBusMessage*, const char*);
enum Service
struct DBusSignal
{
SRV_NONE = 0,
SRV_MPRIS = (1ul << 0),
SRV_GAMEMODE = (1ul << 1),
SRV_ALL = 0xFFFFFFFF,
const char * intf;
const char * signal;
SignalType type;
};
struct DBusSignal {
Service srv;
const char* intf;
const char* signal;
signal_handler_func handler;
};
extern struct metadata main_metadata;
extern struct metadata generic_mpris;
class dbus_manager {
public:
dbus_manager() {}
~dbus_manager();
bool init(Service srv);
bool init_mpris(const std::string& requested_player);
void deinit(Service srv);
bool get_media_player_metadata(metadata& meta, std::string name = "");
void connect_to_signals(Service srv);
void disconnect_from_signals(Service srv);
DBusConnection* get_conn() const { return m_dbus_conn; }
bool gamemode_enabled(pid_t pid);
libdbus_loader& dbus() { return m_dbus_ldr; }
protected:
bool init_internal();
void stop_thread();
void start_thread();
void dbus_thread();
bool dbus_list_name_to_owner();
bool select_active_player();
static DBusHandlerResult filter_signals(DBusConnection*, DBusMessage*,
void*);
bool handle_properties_changed(DBusMessage*, const char*);
bool handle_name_owner_changed(DBusMessage*, const char*);
bool handle_game_registered(DBusMessage*, const char*);
bool handle_game_unregistered(DBusMessage*, const char*);
void onNewPlayer(
metadata& meta); // A different player has become the active player
void onNoPlayer(); // There is no longer any player active
void onPlayerUpdate(
metadata& meta); // The active player has sent an update
DBusError m_error;
DBusConnection* m_dbus_conn = nullptr;
bool m_quit = false;
bool m_inited = false;
std::thread m_thread;
libdbus_loader m_dbus_ldr;
std::unordered_map<std::string, std::string> m_name_owners;
std::string m_requested_player;
std::string m_active_player;
uint32_t m_active_srvs = SRV_NONE;
const std::array<DBusSignal, 2> m_signals{{
{SRV_MPRIS, "org.freedesktop.DBus", "NameOwnerChanged",
&dbus_manager::handle_name_owner_changed},
{SRV_MPRIS, "org.freedesktop.DBus.Properties", "PropertiesChanged",
&dbus_manager::handle_properties_changed},
// {SRV_GAMEMODE, "com.feralinteractive.GameMode", "GameRegistered",
// &dbus_manager::handle_game_registered},
// {SRV_GAMEMODE, "com.feralinteractive.GameMode", "GameUnregistered",
// &dbus_manager::handle_game_unregistered},
}};
};
namespace dbusmgr {
using callback_func = std::function<void(/*metadata*/)>;
enum CBENUM {
CB_CONNECTED,
CB_DISCONNECTED,
CB_NEW_METADATA,
};
/* class dbus_error : public std::runtime_error
{
public:
dbus_error(libdbus_loader& dbus_, DBusError *src) : std::runtime_error(src->message)
{
dbus = &dbus_;
dbus->error_init(&error);
dbus->move_error (src, &error);
}
virtual ~dbus_error() { dbus->error_free (&error); }
private:
DBusError error;
libdbus_loader *dbus;
};*/
class dbus_manager
{
public:
dbus_manager()
{
}
~dbus_manager();
bool init(const std::string& dest);
void deinit();
void add_callback(CBENUM type, callback_func func);
void connect_to_signals();
void disconnect_from_signals();
DBusConnection* get_conn() const {
return m_dbus_conn;
}
libdbus_loader& dbus() {
return m_dbus_ldr;
}
protected:
void stop_thread();
void start_thread();
static void dbus_thread(dbus_manager *pmgr);
DBusError m_error;
DBusConnection * m_dbus_conn = nullptr;
DBusMessage * m_dbus_msg = nullptr;
DBusMessage * m_dbus_reply = nullptr;
bool m_quit = false;
bool m_inited = false;
std::thread m_thread;
std::map<CBENUM, callback_func> m_callbacks;
libdbus_loader m_dbus_ldr;
std::unordered_map<std::string, std::string> m_name_owners;
std::string m_dest;
const std::array<DBusSignal, 2> m_signals {{
{ "org.freedesktop.DBus", "NameOwnerChanged", ST_NAMEOWNERCHANGED },
{ "org.freedesktop.DBus.Properties", "PropertiesChanged", ST_PROPERTIESCHANGED },
}};
};
extern dbus_manager dbus_mgr;
}
extern dbus_manager dbus_mgr;
} // namespace dbusmgr
bool get_media_player_metadata(dbusmgr::dbus_manager& dbus, const std::string& name, metadata& meta);
#endif // HAVE_DBUS
#endif //MANGOHUD_DBUS_INFO_H

@ -1,225 +0,0 @@
#include "device.h"
#include <filesystem.h>
#include <iostream>
#include <algorithm>
#include <spdlog/spdlog.h>
namespace fs = ghc::filesystem;
using namespace std;
std::mutex device_lock;
std::vector<device_batt> device_data;
std::vector<std::string> list;
bool device_found = false;
bool check_gamepad = false;
bool check_mouse = false;
int device_count = 0;
int xbox_count = 0;
int ds4_count = 0;
int ds5_count = 0;
int switch_count = 0;
int bitdo_count = 0;
int logi_count = 0; //Logitech devices, mice & keyboards etc.
int shield_count = 0;
std::string xbox_paths [2]{"gip","xpadneo"};
static bool operator<(const device_batt& a, const device_batt& b)
{
return a.name < b.name;
}
void device_update(const struct overlay_params& params){
std::unique_lock<std::mutex> l(device_lock);
fs::path path("/sys/class/power_supply");
list.clear();
xbox_count = 0;
ds4_count = 0;
ds5_count = 0;
switch_count = 0;
bitdo_count = 0;
shield_count = 0;
for (auto &p : fs::directory_iterator(path)) {
string fileName = p.path().filename();
//Gamepads
if (std::find(params.device_battery.begin(), params.device_battery.end(), "gamepad") != params.device_battery.end()){
check_gamepad = true;
//CHECK XONE AND XPADNEO DEVICES
for (string n : xbox_paths ) {
if (fileName.find(n) != std::string::npos) {
list.push_back(p.path());
device_found = true;
xbox_count += 1;
}
}
//CHECK FOR DUAL SHOCK 4 DEVICES
if (fileName.find("sony_controller") != std::string::npos) {
list.push_back(p.path());
device_found = true;
ds4_count +=1 ;
}
if (fileName.find("ps-controller") != std::string::npos) {
list.push_back(p.path());
device_found = true;
ds5_count +=1 ;
}
//CHECK FOR NINTENDO SWITCH DEVICES
if (fileName.find("nintendo_switch_controller") != std::string::npos) {
list.push_back(p.path());
device_found = true;
switch_count += 1;
}
//CHECK * BITDO DEVICES
if (fileName.find("hid-e4") != std::string::npos) {
list.push_back(p.path());
device_found = true;
bitdo_count += 1;
}
//CHECK NVIDIA SHIELD DEVICES
if (fileName.find("thunderstrike") != std::string::npos) {
list.push_back(p.path());
device_found = true;
shield_count += 1;
}
}
// Mice and Keyboards
//CHECK LOGITECH DEVICES
if (std::find(params.device_battery.begin(), params.device_battery.end(), "mouse") != params.device_battery.end()) {
check_mouse = true;
if (fileName.find("hidpp_battery") != std::string::npos) {
list.push_back(p.path());
device_found = true;
}
}
}
}
void device_info () {
std::unique_lock<std::mutex> l(device_lock);
device_count = 0;
device_data.clear();
//gamepad counters
int xbox_counter = 0;
int ds4_counter = 0;
int ds5_counter = 0;
int switch_counter = 0;
int bitdo_counter = 0;
int shield_counter = 0;
for (auto &path : list ) {
//Set devices paths
std::string capacity = path + "/capacity";
std::string capacity_level = path + "/capacity_level";
std::string status = path + "/status";
std::string model = path + "/model_name";
std::ifstream input_capacity(capacity);
std::ifstream input_capacity_level(capacity_level);
std::ifstream input_status(status);
std::ifstream device_name(model);
std::string line;
device_data.push_back(device_batt());
// GAMEPADS
//Xone and xpadneo devices
if (check_gamepad == true) {
if (path.find("gip") != std::string::npos || path.find("xpadneo") != std::string::npos) {
if (xbox_count == 1 )
device_data[device_count].name = "XBOX PAD";
else
device_data[device_count].name = "XBOX PAD-" + to_string(xbox_counter + 1);
xbox_counter++;
}
//DualShock 4 devices
if (path.find("sony_controller") != std::string::npos) {
if (ds4_count == 1)
device_data[device_count].name = "DS4 PAD";
else
device_data[device_count].name = "DS4 PAD-" + to_string(ds4_counter + 1);
ds4_counter++;
}
//DualSense 5 devices
//Dual Shock 4 added to hid-playstation in Linux 6.2
if (path.find("ps-controller") != std::string::npos) {
if (ds5_count == 1)
device_data[device_count].name = "DS4/5 PAD";
else
device_data[device_count].name = "DS4/5 PAD-" + to_string(ds5_counter + 1);
ds5_counter++;
}
//Nintendo Switch devices
if (path.find("nintendo_switch_controller") != std::string::npos) {
if (switch_count == 1)
device_data[device_count].name = "SWITCH PAD";
else
device_data[device_count].name = "SWITCH PAD-" + to_string(switch_counter + 1);
switch_counter++;
}
//8bitdo devices
if (path.find("hid-e4") != std::string::npos) {
if (bitdo_count == 1)
device_data[device_count].name = "8BITDO PAD";
else
device_data[device_count].name = "8BITDO PAD-" + to_string(bitdo_counter + 1);
bitdo_counter++;
}
//Shield devices
if (path.find("thunderstrike") != std::string::npos) {
if (shield_count == 1)
device_data[device_count].name = "SHIELD PAD";
else
device_data[device_count].name = "SHIELD PAD-" + to_string(shield_counter + 1);
shield_counter++;
}
}
// MICE AND KEYBOARDS
//Logitech Devices
if (check_mouse == true) {
if (path.find("hidpp_battery") != std::string::npos) {
// Find a good way truncate name or retreive device type before using this
// if (std::getline(device_name, line)) {
// device_data[device_count].name = line;
// }
device_data[device_count].name = "LOGI MOUSE/KB";
}
}
//Get device charging status
if (std::getline(input_status, line)) {
if (line == "Charging" || line == "Full")
device_data[device_count].is_charging = true;
}
//Get device Battery
if (fs::exists(capacity)) {
if (std::getline(input_capacity, line)) {
device_data[device_count].battery_percent = line;
device_data[device_count].report_percent = true;
switch(std::stoi(line)) {
case 0 ... 25:
device_data[device_count].battery = "Low";
break;
case 26 ... 49:
device_data[device_count].battery = "Normal";
break;
case 50 ... 74:
device_data[device_count].battery = "High";
break;
case 75 ... 100:
device_data[device_count].battery = "Full";
break;
}
}
}
else {
if (std::getline(input_capacity_level, line)) {
device_data[device_count].battery = line;
}
}
std::sort(device_data.begin(), device_data.end());
device_count += 1;
}
}

@ -1,25 +0,0 @@
#pragma once
#ifndef MANGOHUD_DEVICE_H
#define MANGOHUD_DEVICE_H
#include <vector>
#include <string>
#include "overlay_params.h"
struct overlay_params;
struct device_batt {
std::string battery;
std::string name;
bool report_percent;
std::string battery_percent;
bool is_charging;
};
extern std::vector<device_batt> device_data;
extern std::mutex device_lock;
extern bool device_found;
extern int device_count;
void device_update(const overlay_params& params);
void device_info();
#endif // MANGOHUD_DEVICE_H

@ -29,12 +29,6 @@
* \{
*/
#ifdef __GLIBC__
# define ABS_ADDR(obj, ptr) (ptr)
#else
# define ABS_ADDR(obj, ptr) ((obj->addr) + (ptr))
#endif
struct eh_iterate_callback_args {
eh_iterate_obj_callback_func callback;
void *arg;
@ -50,7 +44,6 @@ int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val);
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg);
int eh_iterate_callback(struct dl_phdr_info *info, size_t size, void *argptr);
int eh_find_sym_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
int eh_find_sym_gnu_hash(eh_obj_t *obj, const char *name, eh_sym_t *sym);
@ -202,22 +195,22 @@ int eh_init_obj(eh_obj_t *obj)
if (obj->strtab)
return ENOTSUP;
obj->strtab = (const char *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
obj->strtab = (const char *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_HASH) {
if (obj->hash)
return ENOTSUP;
obj->hash = (ElfW(Word) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
obj->hash = (ElfW(Word) *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_GNU_HASH) {
if (obj->gnu_hash)
return ENOTSUP;
obj->gnu_hash = (Elf32_Word *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
obj->gnu_hash = (Elf32_Word *) obj->dynamic[p].d_un.d_ptr;
} else if (obj->dynamic[p].d_tag == DT_SYMTAB) {
if (obj->symtab)
return ENOTSUP;
obj->symtab = (ElfW(Sym) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
obj->symtab = (ElfW(Sym) *) obj->dynamic[p].d_un.d_ptr;
}
p++;
}
@ -455,7 +448,7 @@ int eh_find_next_dyn(eh_obj_t *obj, ElfW_Sword tag, int i, ElfW(Dyn) **next)
int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rela) *rela = (ElfW(Rela) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relasize;
unsigned int i;
@ -476,7 +469,7 @@ int eh_set_rela_plt(eh_obj_t *obj, int p, const char *sym, void *val)
int eh_set_rel_plt(eh_obj_t *obj, int p, const char *sym, void *val)
{
ElfW(Rel) *rel = (ElfW(Rel) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Rel) *rel = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relsize;
unsigned int i;
@ -526,7 +519,7 @@ int eh_set_rel(eh_obj_t *obj, const char *sym, void *val)
int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rela) *rela = (ElfW(Rela) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Rela) *rela = (ElfW(Rela) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relasize;
eh_rel_t rel;
eh_sym_t sym;
@ -556,7 +549,7 @@ int eh_iterate_rela_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callb
int eh_iterate_rel_plt(eh_obj_t *obj, int p, eh_iterate_rel_callback_func callback, void *arg)
{
ElfW(Rel) *relp = (ElfW(Rel) *) ABS_ADDR(obj, obj->dynamic[p].d_un.d_ptr);
ElfW(Rel) *relp = (ElfW(Rel) *) obj->dynamic[p].d_un.d_ptr;
ElfW(Dyn) *relsize;
eh_rel_t rel;
eh_sym_t sym;

@ -1,22 +0,0 @@
#pragma once
enum EngineTypes
{
UNKNOWN,
OPENGL,
VULKAN,
DXVK,
VKD3D,
DAMAVAND,
ZINK,
WINED3D,
FERAL3D,
TOGL,
GAMESCOPE
};
extern const char* engines[];
extern const char* engines_short[];

@ -1,63 +0,0 @@
#pragma once
#ifndef MANGOHUD_FCAT_H
#define MANGOHUD_FCAT_H
#include <iostream>
#include <vector>
#include <fstream>
#include <chrono>
#include <thread>
#include <condition_variable>
#include <array>
#include "timing.hpp"
#include "overlay_params.h"
#include "overlay.h"
struct fcatoverlay{
const struct overlay_params* params = nullptr;
const std::array<const ImColor,16> sequence={{{255, 255, 255},{0, 255, 0},{0, 0, 255},{255, 0, 0},{0, 128, 128},{0, 0, 128},{0, 128, 0},{0, 255, 255},{128, 0, 0},{192, 192, 192},{128, 0, 128},{128, 128, 0},{128, 128, 128},{255, 0, 255},{255, 255, 0},{255, 128, 0}}};
void update(const struct overlay_params* params_){
params=params_;
};
ImColor get_next_color (const swapchain_stats& sw_stats){
size_t currentColor = sw_stats.n_frames % 16;// should probably be sequence.size(); but this doesn't matter as all FCAT analysis tools use this exact 16 colour sequence.
ImColor output = sequence[currentColor];
return output;
};
std::array<ImVec2,3> get_overlay_corners()
{
unsigned short screen_edge=params->fcat_screen_edge;
auto window_size = ImVec2(params->fcat_overlay_width,ImGui::GetIO().DisplaySize.y);
auto p_min = ImVec2(0.,0.);
auto p_max = ImVec2(window_size.x,ImGui::GetIO().DisplaySize.y);
//Switch the used screen edge, this enables capture from devices with any screen orientation.
//This goes counter-clockwise from the left edge (0)
switch (screen_edge)
{
default:
case 0:
break;
case 1:
window_size = ImVec2(ImGui::GetIO().DisplaySize.x,window_size.x);
p_min = ImVec2(0,ImGui::GetIO().DisplaySize.y - window_size.y);
p_max = ImVec2(ImGui::GetIO().DisplaySize.x,ImGui::GetIO().DisplaySize.y);
break;
case 2:
window_size = ImVec2(window_size.x,ImGui::GetIO().DisplaySize.y);
p_min = ImVec2(ImGui::GetIO().DisplaySize.x-window_size.x,0);
p_max = ImVec2(ImGui::GetIO().DisplaySize.x,ImGui::GetIO().DisplaySize.y);
break;
case 3:
window_size = ImVec2(ImGui::GetIO().DisplaySize.x,window_size.x);
p_min = ImVec2(0,0);
p_max = ImVec2(ImGui::GetIO().DisplaySize.x,window_size.y);
break;
}
std::array<ImVec2,3> output={{p_min,p_max,window_size}};
return output;
};
};
#endif

@ -4,42 +4,47 @@
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <limits.h>
#include <linux/limits.h>
#include <iostream>
#include <fstream>
#include <cstring>
#include <string>
#include <spdlog/spdlog.h>
#include <filesystem.h>
namespace fs = ghc::filesystem;
#ifndef PROCDIR
#define PROCDIR "/proc"
#endif
std::string read_line(const std::string& filename)
{
std::string line;
std::ifstream file(filename);
if (file.fail()){
return line;
}
std::getline(file, line);
return line;
}
std::string get_basename(const std::string&& path)
bool find_folder(const char* root, const char* prefix, std::string& dest)
{
auto npos = path.find_last_of("/\\");
if (npos == std::string::npos)
return path;
struct dirent* dp;
DIR* dirp = opendir(root);
if (!dirp) {
std::cerr << "Error opening directory '" << root << "': ";
perror("");
return false;
}
if (npos < path.size() - 1)
return path.substr(npos + 1);
return path;
// XXX xfs/jfs need stat() for inode type
while ((dp = readdir(dirp))) {
if ((dp->d_type == DT_LNK || dp->d_type == DT_DIR) && starts_with(dp->d_name, prefix)) {
dest = dp->d_name;
closedir(dirp);
return true;
}
}
closedir(dirp);
return false;
}
bool find_folder(const std::string& root, const std::string& prefix, std::string& dest)
{
return find_folder(root.c_str(), prefix.c_str(), dest);
}
#ifdef __linux__
std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags)
{
std::vector<std::string> list;
@ -47,7 +52,8 @@ std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags
DIR* dirp = opendir(root);
if (!dirp) {
SPDLOG_ERROR("Error opening directory '{}': {}", root, strerror(errno));
std::cerr << "Error opening directory '" << root << "': ";
perror("");
return list;
}
@ -57,8 +63,7 @@ std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags
|| !strcmp(dp->d_name, ".."))
continue;
switch (dp->d_type) {
case DT_LNK: {
if (dp->d_type == DT_LNK) {
struct stat s;
std::string path(root);
if (path.back() != '/')
@ -69,19 +74,13 @@ std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags
continue;
if (((flags & LS_DIRS) && S_ISDIR(s.st_mode))
|| ((flags & LS_FILES) && S_ISREG(s.st_mode))) {
|| ((flags & LS_FILES) && !S_ISDIR(s.st_mode))) {
list.push_back(dp->d_name);
}
break;
}
case DT_DIR:
if (flags & LS_DIRS)
list.push_back(dp->d_name);
break;
case DT_REG:
if (flags & LS_FILES)
list.push_back(dp->d_name);
break;
} else if (((flags & LS_DIRS) && dp->d_type == DT_DIR)
|| ((flags & LS_FILES) && dp->d_type == DT_REG)
) {
list.push_back(dp->d_name);
}
}
@ -101,41 +100,25 @@ bool dir_exists(const std::string& path)
return !stat(path.c_str(), &s) && S_ISDIR(s.st_mode);
}
std::string read_symlink(const char * link)
std::string readlink(const char * link)
{
char result[PATH_MAX] {};
ssize_t count = readlink(link, result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
}
std::string read_symlink(const std::string&& link)
{
return read_symlink(link.c_str());
}
std::string get_exe_path()
{
return read_symlink(PROCDIR "/self/exe");
return readlink("/proc/self/exe");
}
std::string get_wine_exe_name(bool keep_ext)
bool get_wine_exe_name(std::string& name, bool keep_ext)
{
const std::string exe_path = get_exe_path();
if (!ends_with(exe_path, "wine-preloader") && !ends_with(exe_path, "wine64-preloader")) {
return std::string();
}
std::string line = read_line(PROCDIR "/self/comm"); // max 16 characters though
if (ends_with(line, ".exe", true))
std::string line;
std::ifstream cmdline("/proc/self/cmdline");
auto n = std::string::npos;
while (std::getline(cmdline, line, '\0'))
{
auto dot = keep_ext ? std::string::npos : line.find_last_of('.');
return line.substr(0, dot);
}
std::ifstream cmdline(PROCDIR "/self/cmdline");
// Iterate over arguments (separated by NUL byte).
while (std::getline(cmdline, line, '\0')) {
auto n = std::string::npos;
if (!line.empty()
&& ((n = line.find_last_of("/\\")) != std::string::npos)
&& n < line.size() - 1) // have at least one character
@ -143,15 +126,17 @@ std::string get_wine_exe_name(bool keep_ext)
auto dot = keep_ext ? std::string::npos : line.find_last_of('.');
if (dot < n)
dot = line.size();
return line.substr(n + 1, dot - n - 1);
name = line.substr(n + 1, dot - n - 1);
return true;
}
else if (ends_with(line, ".exe", true))
{
auto dot = keep_ext ? std::string::npos : line.find_last_of('.');
return line.substr(0, dot);
name = line.substr(0, dot);
return true;
}
}
return std::string();
return false;
}
std::string get_home_dir()
@ -187,24 +172,3 @@ std::string get_config_dir()
path += "/.config";
return path;
}
bool lib_loaded(const std::string& lib) {
fs::path path(PROCDIR "/self/map_files/");
for (auto& p : fs::directory_iterator(path)) {
auto file = p.path().string();
auto sym = read_symlink(file.c_str());
if (sym.find(lib) != std::string::npos) {
return true;
}
}
return false;
}
std::string remove_parentheses(const std::string& text) {
// Remove parentheses and text between them
std::regex pattern("\\([^)]*\\)");
return std::regex_replace(text, pattern, "");
}
#endif // __linux__

@ -1,29 +1,23 @@
#pragma once
#ifndef MANGOHUD_FILE_UTILS_H
#define MANGOHUD_FILE_UTILS_H
#include <string>
#include <vector>
#include <regex>
enum LS_FLAGS
{
LS_ALL = 0x00,
LS_DIRS = 0x01,
LS_FILES = 0x02,
};
std::string read_line(const std::string& filename);
bool find_folder(const char* root, const char* prefix, std::string& dest);
bool find_folder(const std::string& root, const std::string& prefix, std::string& dest);
std::vector<std::string> ls(const char* root, const char* prefix = nullptr, LS_FLAGS flags = LS_DIRS);
bool file_exists(const std::string& path);
bool dir_exists(const std::string& path);
std::string read_symlink(const char * link);
std::string read_symlink(const std::string&& link);
std::string get_basename(const std::string&& path); //prefix so it doesn't conflict libgen
std::string readlink(const char * link);
std::string get_exe_path();
std::string get_wine_exe_name(bool keep_ext = false);
bool get_wine_exe_name(std::string& name, bool keep_ext = false);
std::string get_home_dir();
std::string get_data_dir();
std::string get_config_dir();
bool lib_loaded(const std::string& lib);
std::string remove_parentheses(const std::string&);
#endif //MANGOHUD_FILE_UTILS_H

@ -1,48 +0,0 @@
#include "file_utils.h"
#include "string_utils.h"
#include <fstream>
#include <string>
std::vector<std::string> ls(const char* root, const char* prefix, LS_FLAGS flags)
{
std::vector<std::string> list;
return list;
}
bool file_exists(const std::string& path)
{
return false;
}
bool dir_exists(const std::string& path)
{
return false;
}
std::string get_exe_path()
{
return std::string();
}
std::string get_wine_exe_name(bool keep_ext)
{
return std::string();
}
std::string get_home_dir()
{
std::string path;
return path;
}
std::string get_data_dir()
{
std::string path;
return path;
}
std::string get_config_dir()
{
std::string path;
return path;
}

@ -1,102 +0,0 @@
#include <cstdint>
#include "overlay.h"
#include "file_utils.h"
#include "font_default.h"
#include "IconsForkAwesome.h"
#include "forkawesome.h"
void create_fonts(ImFontAtlas* font_atlas, const overlay_params& params, ImFont*& small_font, ImFont*& text_font)
{
auto& io = ImGui::GetIO();
if (!font_atlas)
font_atlas = io.Fonts;
font_atlas->Clear();
ImGui::GetIO().FontGlobalScale = params.font_scale; // set here too so ImGui::CalcTextSize is correct
float font_size = params.font_size;
if (font_size < FLT_EPSILON)
font_size = 24;
float font_size_text = params.font_size_text;
if (font_size_text < FLT_EPSILON)
font_size_text = font_size;
static const ImWchar default_range[] =
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
0x2018, 0x201F, // Bunch of quotation marks
//0x0100, 0x017F, // Latin Extended-A
//0x2103, 0x2103, // Degree Celsius
//0x2109, 0x2109, // Degree Fahrenheit
0,
};
// Load Icon file and merge to exisitng font
ImFontConfig config;
config.MergeMode = true;
// ImGui changed OversampleH default to 2, but it appears to sometimes cause
// crashing issues in 32bit applications.
config.OversampleH = 3;
static const ImWchar icon_ranges[] = { ICON_MIN_FK, ICON_MAX_FK, 0 };
ImVector<ImWchar> glyph_ranges;
ImFontGlyphRangesBuilder builder;
builder.AddRanges(font_atlas->GetGlyphRangesDefault());
if (params.font_glyph_ranges & FG_KOREAN)
builder.AddRanges(font_atlas->GetGlyphRangesKorean());
if (params.font_glyph_ranges & FG_CHINESE_FULL)
builder.AddRanges(font_atlas->GetGlyphRangesChineseFull());
if (params.font_glyph_ranges & FG_CHINESE_SIMPLIFIED)
builder.AddRanges(font_atlas->GetGlyphRangesChineseSimplifiedCommon());
if (params.font_glyph_ranges & FG_JAPANESE)
builder.AddRanges(font_atlas->GetGlyphRangesJapanese()); // Not exactly Shift JIS compatible?
if (params.font_glyph_ranges & FG_CYRILLIC)
builder.AddRanges(font_atlas->GetGlyphRangesCyrillic());
if (params.font_glyph_ranges & FG_THAI)
builder.AddRanges(font_atlas->GetGlyphRangesThai());
if (params.font_glyph_ranges & FG_VIETNAMESE)
builder.AddRanges(font_atlas->GetGlyphRangesVietnamese());
if (params.font_glyph_ranges & FG_LATIN_EXT_A) {
constexpr ImWchar latin_ext_a[] { 0x0100, 0x017F, 0 };
builder.AddRanges(latin_ext_a);
}
if (params.font_glyph_ranges & FG_LATIN_EXT_B) {
constexpr ImWchar latin_ext_b[] { 0x0180, 0x024F, 0 };
builder.AddRanges(latin_ext_b);
}
builder.BuildRanges(&glyph_ranges);
bool same_font = (params.font_file == params.font_file_text || params.font_file_text.empty());
bool same_size = (font_size == font_size_text);
// ImGui takes ownership of the data, no need to free it
if (!params.font_file.empty() && file_exists(params.font_file)) {
font_atlas->AddFontFromFileTTF(params.font_file.c_str(), font_size, nullptr, same_font && same_size ? glyph_ranges.Data : default_range);
font_atlas->AddFontFromMemoryCompressedBase85TTF(forkawesome_compressed_data_base85, font_size, &config, icon_ranges);
if (params.no_small_font)
small_font = font_atlas->Fonts[0];
else {
small_font = font_atlas->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f, nullptr, default_range);
font_atlas->AddFontFromMemoryCompressedBase85TTF(forkawesome_compressed_data_base85, font_size * 0.55f, &config, icon_ranges);
}
} else {
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
font_atlas->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, nullptr, default_range);
font_atlas->AddFontFromMemoryCompressedBase85TTF(forkawesome_compressed_data_base85, font_size, &config, icon_ranges);
if (params.no_small_font)
small_font = font_atlas->Fonts[0];
else {
small_font = font_atlas->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55f, nullptr, default_range);
font_atlas->AddFontFromMemoryCompressedBase85TTF(forkawesome_compressed_data_base85, font_size * 0.55f, &config, icon_ranges);
}
}
auto font_file_text = params.font_file_text;
if (font_file_text.empty())
font_file_text = params.font_file;
if ((!same_font || !same_size) && file_exists(font_file_text))
text_font = font_atlas->AddFontFromFileTTF(font_file_text.c_str(), font_size_text, nullptr, glyph_ranges.Data);
else
text_font = font_atlas->Fonts[0];
font_atlas->Build();
}

@ -1,6 +1,5 @@
#pragma once
#ifndef MANGOHUD_FONT_DEFAULT_H
#define MANGOHUD_FONT_DEFAULT_H
#ifndef FONT_DEFAULT_H
#define FONT_DEFAULT_H
#ifdef __cplusplus
extern "C" {
@ -12,4 +11,4 @@ const char* GetDefaultCompressedFontDataTTFBase85(void);
}
#endif
#endif // MANGOHUD_FONT_DEFAULT_H
#endif

File diff suppressed because it is too large Load Diff

@ -1,165 +0,0 @@
#pragma once
#include <vector>
#include <string>
#include <memory>
#include <thread>
#include <mesa/util/os_time.h>
#include <numeric>
#include <mutex>
#include <algorithm>
#include <condition_variable>
#include <stdexcept>
#include <iomanip>
#include <spdlog/spdlog.h>
struct metric_t {
std::string name;
float value;
std::string display_name;
};
class fpsMetrics {
private:
std::vector<std::pair<uint64_t, float>> fps_stats;
std::thread thread;
std::mutex mtx;
std::condition_variable cv;
bool run = false;
bool thread_init = false;
bool terminate = false;
bool resetting = false;
void calculate(){
thread_init = true;
while (true){
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [this] { return run; });
if (terminate)
break;
std::vector<float> sorted_values;
for (const auto& p : fps_stats)
sorted_values.push_back(p.second);
std::sort(sorted_values.begin(), sorted_values.end());
auto it = metrics.begin();
while (it != metrics.end()) {
if (it->name == "AVG") {
it->display_name = it->name;
if (!fps_stats.empty()) {
float sum = std::accumulate(fps_stats.begin(), fps_stats.end(), 0.0f,
[](float acc, const std::pair<uint64_t, float>& p) {
return acc + p.second;
});
it->value = sum / fps_stats.size();
++it;
}
} else {
try {
float val = std::stof(it->name);
if (val <= 0 || val >= 1 ) {
SPDLOG_DEBUG("Failed to use fps metric, it's out of range {}", it->name);
it = metrics.erase(it);
break;
}
float multiplied_val = val * 100;
std::ostringstream stream;
if (multiplied_val == static_cast<int>(multiplied_val)) {
stream << std::fixed << std::setprecision(0) << multiplied_val << "%";
} else {
stream << std::fixed << std::setprecision(1) << multiplied_val << "%";
}
it->display_name = stream.str();
uint64_t idx = val * sorted_values.size() - 1;
if (idx >= sorted_values.size())
break;
it->value = sorted_values[idx];
++it;
} catch (const std::invalid_argument& e) {
SPDLOG_DEBUG("Failed to use fps metric value {}", it->name);
it = metrics.erase(it);
}
}
}
run = false;
}
}
public:
std::vector<metric_t> metrics;
fpsMetrics(std::vector<std::string> values){
// capitalize string
for (auto& val : values){
for(char& c : val) {
c = std::toupper(static_cast<unsigned char>(c));
}
metrics.push_back({val, 0.0f});
}
if (!thread_init){
thread = std::thread(&fpsMetrics::calculate, this);
}
};
void update(uint64_t now, double fps){
if (resetting)
return;
if (fps > 0.0001)
fps_stats.push_back({now, fps});
uint64_t ten_minute_duration = 600000000000ULL; // 10 minutes in nanoseconds
// Check if the system's uptime is less than 10 minutes
if (now >= ten_minute_duration) {
uint64_t ten_minutes_ago = now - ten_minute_duration;
fps_stats.erase(
std::remove_if(
fps_stats.begin(),
fps_stats.end(),
[ten_minutes_ago](const std::pair<uint64_t, float>& entry) {
return entry.first < ten_minutes_ago;
}
),
fps_stats.end()
);
}
}
void update_thread(){
if (resetting)
return;
{
std::lock_guard<std::mutex> lock(mtx);
run = true;
}
cv.notify_one();
}
void reset_metrics(){
resetting = true;
while (run){}
fps_stats.clear();
resetting = false;
}
~fpsMetrics(){
terminate = true;
{
std::lock_guard<std::mutex> lock(mtx);
run = true;
}
cv.notify_one();
thread.join();
}
};
extern std::unique_ptr<fpsMetrics> fpsmetrics;

@ -1,10 +1,8 @@
#pragma once
#ifndef MANGOHUD_GL_GL_H
#define MANGOHUD_GL_GL_H
#ifdef __cplusplus
extern "C" {
#endif //__cplusplus
#endif
void * glXCreateContext(void *, void *, void *, int);
void glXDestroyContext(void *, void*);
@ -15,7 +13,6 @@ int glXSwapIntervalMESA(unsigned int);
int glXGetSwapIntervalMESA(void);
int glXMakeCurrent(void*, void*, void*);
void* glXGetCurrentContext();
void *glXCreateContextAttribsARB(void *dpy, void *config,void *share_context, int direct, const int *attrib_list);
void* glXGetProcAddress(const unsigned char*);
void* glXGetProcAddressARB(const unsigned char*);
@ -27,6 +24,4 @@ unsigned int eglSwapBuffers( void*, void* );
#ifdef __cplusplus
}
#endif //__cplusplus
#endif //MANGOHUD_GL_GL_H
#endif

@ -1,26 +0,0 @@
#pragma once
#ifndef MANGOHUD_GL_IMGUI_HUD_H
#define MANGOHUD_GL_IMGUI_HUD_H
#include "overlay.h"
#include "gl_renderer.h"
namespace MangoHud { namespace GL {
enum gl_wsi
{
GL_WSI_UNKNOWN,
GL_WSI_GLX,
GL_WSI_EGL,
};
extern overlay_params params;
void imgui_init();
void imgui_create(void *ctx, const gl_wsi plat);
void imgui_shutdown();
void imgui_set_context(void *ctx, const gl_wsi plat);
void imgui_render(unsigned int width, unsigned int height);
}} // namespace
#endif //MANGOHUD_GL_IMGUI_HUD_H

@ -3,26 +3,19 @@
#include <thread>
#include <string>
#include <iostream>
#include <sstream>
#include <memory>
#include <unistd.h>
#include <spdlog/spdlog.h>
#include <imgui.h>
#ifdef __linux__
#include <implot.h>
#endif
#include "gl_hud.h"
#include "font_default.h"
#include "cpu.h"
#include "file_utils.h"
#include "imgui_hud.h"
#include "notify.h"
#include "blacklist.h"
#include <glad/glad.h>
#ifdef HAVE_DBUS
#include "dbus_info.h"
#endif
#define GLX_RENDERER_VENDOR_ID_MESA 0x8183
#define GLX_RENDERER_DEVICE_ID_MESA 0x8184
bool glx_mesa_queryInteger(int attrib, unsigned int *value);
#include <glad/glad.h>
namespace MangoHud { namespace GL {
@ -51,10 +44,12 @@ struct GLVec
struct state {
ImGuiContext *imgui_ctx = nullptr;
ImFont* font = nullptr;
ImFont* font1 = nullptr;
};
static GLVec last_vp {}, last_sb {};
swapchain_stats sw_stats {};
static swapchain_stats sw_stats {};
static state state;
static uint32_t vendorID;
static std::string deviceName;
@ -73,28 +68,7 @@ void imgui_init()
{
if (cfg_inited)
return;
init_spdlog();
if (is_blacklisted())
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"), false);
_params = &params;
//check for blacklist item in the config file
for (auto& item : params.blacklist) {
add_blacklist(item);
}
if (sw_stats.engine != EngineTypes::ZINK){
sw_stats.engine = OPENGL;
if (lib_loaded("wined3d"))
sw_stats.engine = WINED3D;
if (lib_loaded("libtogl.so") || lib_loaded("libtogl_client.so"))
sw_stats.engine = TOGL;
}
is_blacklisted(true);
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
notifier.params = &params;
start_notifier(notifier);
window_size = ImVec2(params.width, params.height);
@ -104,64 +78,37 @@ void imgui_init()
}
//static
void imgui_create(void *ctx, const gl_wsi plat)
void imgui_create(void *ctx)
{
if (inited)
return;
inited = true;
if (!ctx)
return;
imgui_shutdown();
imgui_init();
inited = true;
if (!gladLoadGL())
spdlog::error("Failed to initialize OpenGL context, crash incoming");
deviceName = (char*)glGetString(GL_RENDERER);
// If we're running zink we want to rely on the vulkan loader for the hud instead.
if (deviceName.find("zink") != std::string::npos)
return;
gladLoadGL();
GetOpenGLVersion(sw_stats.version_gl.major,
sw_stats.version_gl.minor,
sw_stats.version_gl.is_gles);
std::string vendor = (char*)glGetString(GL_VENDOR);
SPDLOG_DEBUG("vendor: {}, deviceName: {}", vendor, deviceName);
deviceName = (char*)glGetString(GL_RENDERER);
sw_stats.deviceName = deviceName;
if (vendor.find("AMD") != std::string::npos
|| deviceName.find("AMD") != std::string::npos
|| deviceName.find("Radeon") != std::string::npos
|| deviceName.find("NAVI") != std::string::npos) {
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
vendorID = 0x1002;
} else if (vendor.find("Intel") != std::string::npos
|| deviceName.find("Intel") != std::string::npos) {
vendorID = 0x8086;
} else if (vendor.find("freedreno") != std::string::npos) {
vendorID = 0x5143;
} else {
} else {
vendorID = 0x10de;
}
HUDElements.vendorID = vendorID;
uint32_t device_id = 0;
if (plat == gl_wsi::GL_WSI_GLX)
glx_mesa_queryInteger(GLX_RENDERER_DEVICE_ID_MESA, &device_id);
SPDLOG_DEBUG("GL device id: {:04X}", device_id);
init_gpu_stats(vendorID, device_id, params);
sw_stats.gpuName = gpu = remove_parentheses(deviceName);
SPDLOG_DEBUG("gpu: {}", gpu);
init_gpu_stats(vendorID, params);
get_device_name(vendorID, deviceID, sw_stats);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
state.imgui_ctx = ImGui::CreateContext();
#ifdef __linux__
ImPlot::CreateContext();
#endif
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
@ -169,7 +116,7 @@ void imgui_create(void *ctx, const gl_wsi plat)
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
HUDElements.convert_colors(false, params);
imgui_custom_style(params);
glGetIntegerv (GL_VIEWPORT, last_vp.v);
glGetIntegerv (GL_SCISSOR_BOX, last_sb.v);
@ -178,9 +125,14 @@ void imgui_create(void *ctx, const gl_wsi plat)
ImGui::GetIO().DisplaySize = ImVec2(last_vp[2], last_vp[3]);
ImGui_ImplOpenGL3_Init();
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
create_fonts(nullptr, params, sw_stats.font1, sw_stats.font_text);
sw_stats.font_params_hash = params.font_params_hash;
create_fonts(params, state.font, state.font1);
sw_stats.font1 = state.font1;
// Restore global context or ours might clash with apps that use Dear ImGui
ImGui::SetCurrentContext(saved_ctx);
@ -188,6 +140,10 @@ void imgui_create(void *ctx, const gl_wsi plat)
void imgui_shutdown()
{
#ifndef NDEBUG
std::cerr << __func__ << std::endl;
#endif
if (state.imgui_ctx) {
ImGui::SetCurrentContext(state.imgui_ctx);
ImGui_ImplOpenGL3_Shutdown();
@ -197,11 +153,16 @@ void imgui_shutdown()
inited = false;
}
void imgui_set_context(void *ctx, const gl_wsi plat)
void imgui_set_context(void *ctx)
{
if (!ctx)
if (!ctx) {
imgui_shutdown();
return;
imgui_create(ctx, plat);
}
#ifndef NDEBUG
std::cerr << __func__ << ": " << ctx << std::endl;
#endif
imgui_create(ctx);
}
void imgui_render(unsigned int width, unsigned int height)
@ -209,37 +170,21 @@ void imgui_render(unsigned int width, unsigned int height)
if (!state.imgui_ctx)
return;
static int control_client = -1;
if (params.control >= 0) {
control_client_check(params.control, control_client, deviceName);
process_control_socket(control_client, params);
}
check_keybinds(params, vendorID);
check_keybinds(params);
update_hud_info(sw_stats, params, vendorID);
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
ImGui::SetCurrentContext(state.imgui_ctx);
ImGui::GetIO().DisplaySize = ImVec2(width, height);
if (HUDElements.colors.update)
HUDElements.convert_colors(params);
if (sw_stats.font_params_hash != params.font_params_hash)
{
sw_stats.font_params_hash = params.font_params_hash;
create_fonts(nullptr, params, sw_stats.font1, sw_stats.font_text);
ImGui_ImplOpenGL3_CreateFontsTexture();
}
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
{
std::lock_guard<std::mutex> lk(notifier.mutex);
overlay_new_frame(params);
position_layer(sw_stats, params, window_size);
render_imgui(sw_stats, params, window_size, false);
overlay_end_frame();
}
ImGui::PopStyleVar(3);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());

@ -0,0 +1,15 @@
#pragma once
#include "overlay.h"
#include "imgui_impl_opengl3.h"
namespace MangoHud { namespace GL {
extern overlay_params params;
void imgui_init();
void imgui_create(void *ctx);
void imgui_shutdown();
void imgui_set_context(void *ctx);
void imgui_render(unsigned int width, unsigned int height);
}} // namespace

@ -64,21 +64,21 @@
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#include <spdlog/spdlog.h>
#include <imgui.h>
#include "gl_renderer.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <stdint.h> // intptr_t
#include <sstream>
#include <spdlog/spdlog.h>
#include <glad/glad.h>
#include "overlay.h"
namespace MangoHud {
namespace MangoHud { namespace GL {
extern overlay_params params;
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2)
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 0
#else
#define IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET 1
#endif
// OpenGL Data
static GLuint g_GlVersion = 0; // Extracted at runtime using GL_MAJOR_VERSION, GL_MINOR_VERSION queries.
@ -91,56 +91,47 @@ static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
static bool g_IsGLES = false;
// Functions
static void ImGui_ImplOpenGL3_DestroyFontsTexture()
static bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->SetTexID(0);
g_FontTexture = 0;
}
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
ImGui_ImplOpenGL3_DestroyFontsTexture();
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
GLint last_texture, last_unpack_buffer;
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGenTextures(1, &g_FontTexture);
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
if ((g_IsGLES && g_GlVersion >= 300) || (!g_IsGLES && g_GlVersion >= 210))
{
glGetIntegerv(GL_PIXEL_UNPACK_BUFFER_BINDING, &last_unpack_buffer);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//#ifdef GL_UNPACK_ROW_LENGTH
if (g_IsGLES || g_GlVersion >= 200)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->SetTexID((ImTextureID)(intptr_t)g_FontTexture);
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
// Restore state
glBindTexture(GL_TEXTURE_2D, last_texture);
if ((g_IsGLES && g_GlVersion >= 300) || (!g_IsGLES && g_GlVersion >= 210))
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, last_unpack_buffer);
return true;
}
static void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
// If you get an error please report on github. You may try different GL context version or GLSL version. See GL<>GLSL version table at the top of this file.
static bool CheckShader(GLuint handle, const char* desc)
{
@ -148,13 +139,13 @@ static bool CheckShader(GLuint handle, const char* desc)
glGetShaderiv(handle, GL_COMPILE_STATUS, &status);
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
SPDLOG_ERROR("ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile {}!", desc);
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to compile %s!\n", desc);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
SPDLOG_ERROR("{}", buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
@ -166,13 +157,13 @@ static bool CheckProgram(GLuint handle, const char* desc)
glGetProgramiv(handle, GL_LINK_STATUS, &status);
glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &log_length);
if ((GLboolean)status == GL_FALSE)
SPDLOG_ERROR("ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link {}! (with GLSL '{}')", desc, g_GlslVersionString);
fprintf(stderr, "ERROR: ImGui_ImplOpenGL3_CreateDeviceObjects: failed to link %s! (with GLSL '%s')\n", desc, g_GlslVersionString);
if (log_length > 1)
{
ImVector<char> buf;
buf.resize((int)(log_length + 1));
glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin());
SPDLOG_ERROR("{}", buf.begin());
fprintf(stderr, "%s\n", buf.begin());
}
return (GLboolean)status == GL_TRUE;
}
@ -189,7 +180,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
// Parse GLSL version string
int glsl_version = 120;
int glsl_version = 130;
sscanf(g_GlslVersionString, "#version %d", &glsl_version);
const GLchar* vertex_shader_glsl_120 =
@ -259,7 +250,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * vec4(1, 1, 1, texture2D(Texture, Frag_UV.st).r);\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
@ -269,7 +260,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * vec4(1, 1, 1, texture(Texture, Frag_UV.st).r);\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
@ -280,7 +271,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * vec4(1, 1, 1, texture(Texture, Frag_UV.st).r);\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
@ -290,11 +281,12 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * vec4(1, 1, 1, texture(Texture, Frag_UV.st).r);\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
SPDLOG_DEBUG("glsl_version: {}", glsl_version);
#ifndef NDEBUG
printf("glsl_version: %d\n", glsl_version);
#endif
// Select shaders matching our GLSL versions
const GLchar* vertex_shader = NULL;
const GLchar* fragment_shader = NULL;
@ -319,25 +311,16 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
fragment_shader = fragment_shader_glsl_130;
}
std::stringstream ss;
ss << g_GlslVersionString << vertex_shader;
std::string shader = ss.str();
// Create shaders
//const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
const GLchar* vertex_shader_with_version[1] = { shader.c_str() };
const GLchar* vertex_shader_with_version[2] = { g_GlslVersionString, vertex_shader };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 1, vertex_shader_with_version, NULL);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
ss.str(""); ss.clear();
ss << g_GlslVersionString << fragment_shader;
shader = ss.str();
const GLchar* fragment_shader_with_version[1] = { shader.c_str() };
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 1, fragment_shader_with_version, NULL);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
@ -371,6 +354,9 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
static void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
#ifndef NDEBUG
printf("%s\n", __func__);
#endif
if (g_VboHandle) { glDeleteBuffers(1, &g_VboHandle); g_VboHandle = 0; }
if (g_ElementsHandle) { glDeleteBuffers(1, &g_ElementsHandle); g_ElementsHandle = 0; }
if (g_ShaderHandle && g_VertHandle) { glDetachShader(g_ShaderHandle, g_VertHandle); }
@ -399,8 +385,8 @@ void GetOpenGLVersion(int& major, int& minor, bool& isGLES)
if (!version)
return;
//if (glGetError() == 0x500)
{
//if (glGetError() == 0x500) {
for (int i = 0; prefixes[i]; i++) {
const size_t length = strlen(prefixes[i]);
if (strncmp(version, prefixes[i], length) == 0) {
@ -411,7 +397,7 @@ void GetOpenGLVersion(int& major, int& minor, bool& isGLES)
}
sscanf(version, "%d.%d", &major, &minor);
}
//}
}
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
@ -419,18 +405,16 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
GLint major = 0, minor = 0;
GetOpenGLVersion(major, minor, g_IsGLES);
SPDLOG_INFO("GL version: {}.{} {}", major, minor, g_IsGLES ? "ES" : "");
printf("Version: %d.%d %s\n", major, minor, g_IsGLES ? "ES" : "");
if (!g_IsGLES) {
// Not GL ES
glsl_version = "#version 120";
glsl_version = "#version 130";
g_GlVersion = major * 100 + minor * 10;
if (major >= 4 && minor >= 1)
glsl_version = "#version 410";
else if (major > 3 || (major == 3 && minor >= 2))
glsl_version = "#version 150";
else if (major == 3)
glsl_version = "#version 130";
else if (major < 2)
glsl_version = "#version 100";
} else {
@ -446,20 +430,20 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
else if (g_GlVersion >= 300)
glsl_version = "#version 300 es";
else
glsl_version = "#version 120";
glsl_version = "#version 130";
}
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "mangohud_opengl3";
io.BackendRendererName = "imgui_impl_opengl3";
//#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320) // GL/GLES 3.2+
if ((!g_IsGLES && g_GlVersion >= 320) || (g_IsGLES && g_GlVersion >= 320))
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (glsl_version == NULL)
glsl_version = "#version 120";
glsl_version = "#version 130";
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
@ -483,49 +467,22 @@ void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
else if (!glIsProgram(g_ShaderHandle)) { // TODO Got created in a now dead context?
SPDLOG_DEBUG("Recreating lost objects");
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
if (!glIsTexture(g_FontTexture)) {
SPDLOG_DEBUG("GL Texture lost? Regenerating.");
g_FontTexture = 0;
ImGui_ImplOpenGL3_CreateFontsTexture();
}
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
if (params.gl_bind_framebuffer >= 0 && (g_IsGLES || g_GlVersion >= 300))
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, params.gl_bind_framebuffer);
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_SCISSOR_TEST);
glDisable(GL_FRAMEBUFFER_SRGB);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
if (!g_IsGLES)
{
//#ifdef GL_POLYGON_MODE
if (g_GlVersion >= 200)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
if (g_GlVersion >= 310)
glDisable(GL_PRIMITIVE_RESTART);
}
bool clip_origin_lower_left = true;
GLenum last_clip_origin = 0;
if (!g_IsGLES && /*g_GlVersion >= 450*/ (glad_glClipControl || glad_glClipControlEXT)) {
glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
if (last_clip_origin == GL_UPPER_LEFT)
clip_origin_lower_left = false;
}
//#ifdef GL_POLYGON_MODE
if (!g_IsGLES && g_GlVersion >= 200)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
@ -534,7 +491,6 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
if (!params.gl_dont_flip && !clip_origin_lower_left) { float tmp = T; T = B; B = tmp; } // Swap top and bottom if origin is upper left
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
@ -573,13 +529,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x);
int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y);
if (fb_width <= 0 || fb_height <= 0 || draw_data->TotalVtxCount == 0)
if (fb_width <= 0 || fb_height <= 0)
return;
// Backup GL state
GLint last_fb = -1;
if (params.gl_bind_framebuffer >= 0 && (g_IsGLES || g_GlVersion >= 300))
glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &last_fb);
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
@ -612,12 +565,21 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_stencil_test = glIsEnabled(GL_STENCIL_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
// Disable and store SRGB state.
GLboolean last_srgb_enabled = glIsEnabled(GL_FRAMEBUFFER_SRGB);
GLboolean last_enable_primitive_restart = (!g_IsGLES && g_GlVersion >= 310) ? glIsEnabled(GL_PRIMITIVE_RESTART) : GL_FALSE;
bool clip_origin_lower_left = true;
GLenum last_clip_origin = 0;
GLenum last_clip_depth_mode = 0;
if (!g_IsGLES && /*g_GlVersion >= 450*/ (glad_glClipControl || glad_glClipControlEXT)) {
glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
glGetIntegerv(GL_CLIP_DEPTH_MODE, (GLint*)&last_clip_depth_mode);
if (last_clip_origin == GL_UPPER_LEFT) {
clip_origin_lower_left = false;
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
}
}
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
@ -664,15 +626,15 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
if (!params.gl_dont_flip)
//if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
else
glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w);
//else
// glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
//#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 320) // OGL and OGL ES
if ((!g_IsGLES && g_GlVersion >= 320) || (g_IsGLES && g_GlVersion >= 320))
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
@ -703,9 +665,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_stencil_test) glEnable(GL_STENCIL_TEST); else glDisable(GL_STENCIL_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
if (!g_IsGLES && g_GlVersion >= 310) { if (last_enable_primitive_restart) glEnable(GL_PRIMITIVE_RESTART); }
if (!g_IsGLES && g_GlVersion >= 200)
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
@ -715,8 +675,10 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
if (last_srgb_enabled)
glEnable(GL_FRAMEBUFFER_SRGB);
if (last_fb >= 0)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, last_fb);
if (!g_IsGLES && /*g_GlVersion >= 450*/ glad_glClipControl)
if (!clip_origin_lower_left)
glClipControl(last_clip_origin, last_clip_depth_mode);
}
}} // namespace
}

@ -22,10 +22,8 @@
// Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp.
#pragma once
#ifndef MANGOHUD_IMGUI_IMPL_OPENGL3_H
#define MANGOHUD_IMGUI_IMPL_OPENGL3_H
namespace MangoHud { namespace GL {
namespace MangoHud {
void GetOpenGLVersion(int& major, int& minor, bool& isGLES);
@ -34,7 +32,6 @@ IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullpt
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
// (Optional) Called by Init/NewFrame/Shutdown
//IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
@ -42,6 +39,4 @@ IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
//IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
//IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
}}
#endif //MANGOHUD_IMGUI_IMPL_OPENGL3_H
}

@ -1,32 +1,27 @@
#include <iostream>
#include <array>
#include <cstring>
#include <dlfcn.h>
#include <chrono>
#include <iomanip>
#include <spdlog/spdlog.h>
#include "real_dlsym.h"
#include "mesa/util/macros.h"
#include "mesa/util/os_time.h"
#include "blacklist.h"
#include "gl_hud.h"
#include <chrono>
#include <iomanip>
#include "imgui_hud.h"
using namespace MangoHud::GL;
#define EXPORT_C_(type) extern "C" __attribute__((__visibility__("default"))) type
EXPORT_C_(void *) eglGetProcAddress(const char* procName);
static void* get_egl_proc_address(const char* name) {
void* get_egl_proc_address(const char* name) {
void *func = nullptr;
static void *(*pfn_eglGetProcAddress)(const char*) = nullptr;
if (!pfn_eglGetProcAddress) {
void *handle = real_dlopen("libEGL.so.1", RTLD_LAZY);
if (!handle) {
SPDLOG_ERROR("Failed to open " MANGOHUD_ARCH " libEGL.so.1: {}", dlerror());
} else {
pfn_eglGetProcAddress = reinterpret_cast<decltype(pfn_eglGetProcAddress)>(real_dlsym(handle, "eglGetProcAddress"));
}
}
if (!pfn_eglGetProcAddress)
pfn_eglGetProcAddress = reinterpret_cast<decltype(pfn_eglGetProcAddress)>(get_proc_address("eglGetProcAddress"));
if (pfn_eglGetProcAddress)
func = pfn_eglGetProcAddress(name);
@ -35,51 +30,47 @@ static void* get_egl_proc_address(const char* name) {
func = get_proc_address( name );
if (!func) {
SPDLOG_ERROR("Failed to get function '{}'", name);
std::cerr << "MANGOHUD: Failed to get function '" << name << "'" << std::endl;
}
return func;
}
EXPORT_C_(unsigned int) eglSwapBuffers( void* dpy, void* surf);
//EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
EXPORT_C_(int) eglMakeCurrent_OFF(void *dpy, void *draw, void *read,void *ctx) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << draw << ", " << ctx << std::endl;
#endif
int ret = 0;
return ret;
}
EXPORT_C_(unsigned int) eglSwapBuffers( void* dpy, void* surf)
{
static int (*pfn_eglSwapBuffers)(void*, void*) = nullptr;
if (!pfn_eglSwapBuffers)
pfn_eglSwapBuffers = reinterpret_cast<decltype(pfn_eglSwapBuffers)>(get_egl_proc_address("eglSwapBuffers"));
pfn_eglSwapBuffers = reinterpret_cast<decltype(pfn_eglSwapBuffers)>(get_proc_address("eglSwapBuffers"));
if (!is_blacklisted()) {
static int (*pfn_eglQuerySurface)(void* dpy, void* surface, int attribute, int *value) = nullptr;
if (!pfn_eglQuerySurface)
pfn_eglQuerySurface = reinterpret_cast<decltype(pfn_eglQuerySurface)>(get_egl_proc_address("eglQuerySurface"));
pfn_eglQuerySurface = reinterpret_cast<decltype(pfn_eglQuerySurface)>(get_proc_address("eglQuerySurface"));
imgui_create(surf, gl_wsi::GL_WSI_EGL);
//std::cerr << __func__ << "\n";
imgui_create(surf);
int width=0, height=0;
if (pfn_eglQuerySurface(dpy, surf, 0x3056, &height) &&
pfn_eglQuerySurface(dpy, surf, 0x3057, &width))
imgui_render(width, height);
using namespace std::chrono_literals;
if (fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_EARLY){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
}
}
int res = pfn_eglSwapBuffers(dpy, surf);
if (!is_blacklisted()) {
using namespace std::chrono_literals;
if (fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_LATE){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
}
//std::cerr << "\t" << width << " x " << height << "\n";
}
return res;
return pfn_eglSwapBuffers(dpy, surf);
}
struct func_ptr {
@ -87,14 +78,12 @@ struct func_ptr {
void *ptr;
};
static std::array<const func_ptr, 2> name_to_funcptr_map = {{
static std::array<const func_ptr, 1> name_to_funcptr_map = {{
#define ADD_HOOK(fn) { #fn, (void *) fn }
ADD_HOOK(eglGetProcAddress),
ADD_HOOK(eglSwapBuffers),
#undef ADD_HOOK
}};
EXPORT_C_(void *) mangohud_find_egl_ptr(const char *name);
EXPORT_C_(void *) mangohud_find_egl_ptr(const char *name)
{
if (is_blacklisted())
@ -109,11 +98,11 @@ EXPORT_C_(void *) mangohud_find_egl_ptr(const char *name)
}
EXPORT_C_(void *) eglGetProcAddress(const char* procName) {
void* real_func = get_egl_proc_address(procName);
//std::cerr << __func__ << ": " << procName << std::endl;
void* func = mangohud_find_egl_ptr(procName);
SPDLOG_TRACE("{}: proc: {}, real: {}, fun: {}", __func__, procName, real_func, func);
if (func && real_func)
if (func)
return func;
return real_func;
return get_egl_proc_address(procName);
}

@ -4,9 +4,7 @@
#include <thread>
#include <vector>
#include <algorithm>
#include <atomic>
#include <cstring>
#include <spdlog/spdlog.h>
#include "real_dlsym.h"
#include "loaders/loader_glx.h"
#include "loaders/loader_x11.h"
@ -17,22 +15,25 @@
#include <chrono>
#include <iomanip>
#include <glad/glad.h>
#include "gl_hud.h"
#include "../config.h"
#include "imgui_hud.h"
using namespace MangoHud::GL;
#define EXPORT_C_(type) extern "C" __attribute__((__visibility__("default"))) type
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName);
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName);
#ifndef GLX_WIDTH
#define GLX_WIDTH 0x801D
#define GLX_HEIGHT 0x801E
#define GLX_HEIGTH 0x801E
#endif
static glx_loader glx;
static std::atomic<int> refcnt (0);
static std::vector<std::thread::id> gl_threads;
static void* get_glx_proc_address(const char* name) {
void* get_glx_proc_address(const char* name) {
glx.Load();
void *func = nullptr;
@ -46,81 +47,52 @@ static void* get_glx_proc_address(const char* name) {
func = get_proc_address( name );
if (!func) {
SPDLOG_ERROR("Failed to get function '{}'", name);
std::cerr << "MANGOHUD: Failed to get function '" << name << "'" << std::endl;
}
return func;
}
bool glx_mesa_queryInteger(int attrib, unsigned int *value);
bool glx_mesa_queryInteger(int attrib, unsigned int *value)
{
static int (*pfn_queryInteger)(int attribute, unsigned int *value) =
reinterpret_cast<decltype(pfn_queryInteger)>(get_glx_proc_address(
"glXQueryCurrentRendererIntegerMESA"));
if (pfn_queryInteger)
return !!pfn_queryInteger(attrib, value);
return false;
}
EXPORT_C_(void *) glXCreateContext(void *dpy, void *vis, void *shareList, int direct)
{
glx.Load();
void *ctx = glx.CreateContext(dpy, vis, shareList, direct);
if (ctx)
refcnt++;
SPDLOG_DEBUG("{}: {}", __func__, ctx);
return ctx;
}
EXPORT_C_(void *) glXCreateContextAttribs(void *dpy, void *config,void *share_context, int direct, const int *attrib_list);
EXPORT_C_(void *) glXCreateContextAttribs(void *dpy, void *config,void *share_context, int direct, const int *attrib_list)
{
glx.Load();
void *ctx = glx.CreateContextAttribs(dpy, config, share_context, direct, attrib_list);
if (ctx)
refcnt++;
SPDLOG_DEBUG("{}: {}", __func__, ctx);
return ctx;
}
EXPORT_C_(void *) glXCreateContextAttribsARB(void *dpy, void *config,void *share_context, int direct, const int *attrib_list)
{
glx.Load();
void *ctx = glx.CreateContextAttribsARB(dpy, config, share_context, direct, attrib_list);
if (ctx)
refcnt++;
SPDLOG_DEBUG("{}: {}", __func__, ctx);
#ifndef NDEBUG
std::cerr << __func__ << ":" << ctx << std::endl;
#endif
return ctx;
}
EXPORT_C_(void) glXDestroyContext(void *dpy, void *ctx)
{
glx.Load();
glx.DestroyContext(dpy, ctx);
refcnt--;
if (refcnt <= 0)
imgui_shutdown();
SPDLOG_DEBUG("{}: {}", __func__, ctx);
}
EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
glx.Load();
SPDLOG_DEBUG("{}: {}, {}", __func__, drawable, ctx);
#ifndef NDEBUG
std::cerr << __func__ << ": " << drawable << ", " << ctx << std::endl;
#endif
int ret = glx.MakeCurrent(dpy, drawable, ctx);
if (!is_blacklisted()) {
if (ret) {
imgui_set_context(ctx, gl_wsi::GL_WSI_GLX);
SPDLOG_DEBUG("GL ref count: {}", refcnt.load());
//TODO might as well just ignore everything here as long as VBOs get recreated anyway
auto it = std::find(gl_threads.begin(), gl_threads.end(), std::this_thread::get_id());
if (!ctx) {
if (it != gl_threads.end())
gl_threads.erase(it);
if (!gl_threads.size())
imgui_set_context(nullptr);
} else {
if (it == gl_threads.end())
gl_threads.push_back(std::this_thread::get_id());
imgui_set_context(ctx);
#ifndef NDEBUG
std::cerr << "MANGOHUD: GL thread count: " << gl_threads.size() << "\n";
#endif
}
}
// Afaik -1 only works with EXT version if it has GLX_EXT_swap_control_tear, maybe EGL_MESA_swap_control_tear someday
if (params.gl_vsync >= -1) {
if (glx.SwapIntervalEXT)
glx.SwapIntervalEXT(dpy, drawable, params.gl_vsync);
}
if (params.gl_vsync >= 0) {
if (glx.SwapIntervalSGI)
glx.SwapIntervalSGI(params.gl_vsync);
if (glx.SwapIntervalMESA)
@ -131,44 +103,20 @@ EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
return ret;
}
#ifndef GLX_SWAP_INTERVAL_EXT
#define GLX_SWAP_INTERVAL_EXT 0x20F1
#endif
static void do_imgui_swap(void *dpy, void *drawable)
{
static auto last_time = std::chrono::steady_clock::now();
auto current_time = std::chrono::steady_clock::now();
std::chrono::duration<double> elapsed_seconds = current_time - last_time;
if (HUDElements.vsync == 10 || elapsed_seconds.count() > 5.0)
glx.QueryDrawable(dpy, drawable, GLX_SWAP_INTERVAL_EXT, &HUDElements.vsync);
GLint vp[4];
if (!is_blacklisted()) {
imgui_create(glx.GetCurrentContext(), gl_wsi::GL_WSI_GLX);
imgui_create(glx.GetCurrentContext());
unsigned int width = -1, height = -1;
switch (params.gl_size_query)
{
case GL_SIZE_VIEWPORT:
glGetIntegerv (GL_VIEWPORT, vp);
width = vp[2];
height = vp[3];
break;
case GL_SIZE_SCISSORBOX:
glGetIntegerv (GL_SCISSOR_BOX, vp);
width = vp[2];
height = vp[3];
break;
default:
glx.QueryDrawable(dpy, drawable, GLX_WIDTH, &width);
glx.QueryDrawable(dpy, drawable, GLX_HEIGHT, &height);
break;
}
glx.QueryDrawable(dpy, drawable, GLX_WIDTH, &width);
glx.QueryDrawable(dpy, drawable, GLX_HEIGTH, &height);
/*GLint vp[4]; glGetIntegerv (GL_VIEWPORT, vp);
width = vp[2];
height = vp[3];*/
SPDLOG_TRACE("swap buffers: {}x{}", width, height);
imgui_render(width, height);
}
}
@ -177,50 +125,35 @@ EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
glx.Load();
do_imgui_swap(dpy, drawable);
using namespace std::chrono_literals;
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_EARLY){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
}
glx.SwapBuffers(dpy, drawable);
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_LATE){
fps_limit_stats.frameStart = Clock::now();
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0){
fps_limit_stats.frameStart = os_time_get_nano();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
fps_limit_stats.frameEnd = os_time_get_nano();
}
}
EXPORT_C_(int64_t) glXSwapBuffersMscOML(void* dpy, void* drawable, int64_t target_msc, int64_t divisor, int64_t remainder)
{
glx.Load();
if (!glx.SwapBuffersMscOML)
return -1;
do_imgui_swap(dpy, drawable);
using namespace std::chrono_literals;
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_EARLY){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
}
int64_t ret = glx.SwapBuffersMscOML(dpy, drawable, target_msc, divisor, remainder);
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s && fps_limit_stats.method == FPS_LIMIT_METHOD_LATE){
fps_limit_stats.frameStart = Clock::now();
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0){
fps_limit_stats.frameStart = os_time_get_nano();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = Clock::now();
fps_limit_stats.frameEnd = os_time_get_nano();
}
return ret;
}
EXPORT_C_(void) glXSwapIntervalEXT(void *dpy, void *draw, int interval) {
SPDLOG_DEBUG("{}: {}", __func__, interval);
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
glx.Load();
if (!glx.SwapIntervalEXT)
return;
if (!is_blacklisted() && params.gl_vsync >= 0)
interval = params.gl_vsync;
@ -229,10 +162,10 @@ EXPORT_C_(void) glXSwapIntervalEXT(void *dpy, void *draw, int interval) {
}
EXPORT_C_(int) glXSwapIntervalSGI(int interval) {
SPDLOG_DEBUG("{}: {}", __func__, interval);
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
glx.Load();
if (!glx.SwapIntervalSGI)
return -1;
if (!is_blacklisted() && params.gl_vsync >= 0)
interval = params.gl_vsync;
@ -241,10 +174,10 @@ EXPORT_C_(int) glXSwapIntervalSGI(int interval) {
}
EXPORT_C_(int) glXSwapIntervalMESA(unsigned int interval) {
SPDLOG_DEBUG("{}: {}", __func__, interval);
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
glx.Load();
if (!glx.SwapIntervalMESA)
return -1;
if (!is_blacklisted() && params.gl_vsync >= 0)
interval = (unsigned int)params.gl_vsync;
@ -254,9 +187,6 @@ EXPORT_C_(int) glXSwapIntervalMESA(unsigned int interval) {
EXPORT_C_(int) glXGetSwapIntervalMESA() {
glx.Load();
if (!glx.GetSwapIntervalMESA)
return 0;
int interval = glx.GetSwapIntervalMESA();
if (!is_blacklisted()) {
@ -271,7 +201,9 @@ EXPORT_C_(int) glXGetSwapIntervalMESA() {
}
}
SPDLOG_DEBUG("{}: {}", __func__, interval);
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
return interval;
}
@ -280,14 +212,11 @@ struct func_ptr {
void *ptr;
};
static std::array<const func_ptr, 13> name_to_funcptr_map = {{
static std::array<const func_ptr, 10> name_to_funcptr_map = {{
#define ADD_HOOK(fn) { #fn, (void *) fn }
ADD_HOOK(glXGetProcAddress),
ADD_HOOK(glXGetProcAddressARB),
ADD_HOOK(glXCreateContextAttribs),
ADD_HOOK(glXCreateContextAttribsARB),
ADD_HOOK(glXCreateContext),
ADD_HOOK(glXDestroyContext),
ADD_HOOK(glXMakeCurrent),
ADD_HOOK(glXSwapBuffers),
ADD_HOOK(glXSwapBuffersMscOML),
@ -299,7 +228,6 @@ static std::array<const func_ptr, 13> name_to_funcptr_map = {{
#undef ADD_HOOK
}};
EXPORT_C_(void *) mangohud_find_glx_ptr(const char *name);
EXPORT_C_(void *) mangohud_find_glx_ptr(const char *name)
{
if (is_blacklisted())
@ -314,22 +242,21 @@ EXPORT_C_(void *) mangohud_find_glx_ptr(const char *name)
}
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
void *real_func = get_glx_proc_address((const char*)procName);
void *func = mangohud_find_glx_ptr( (const char*)procName );
SPDLOG_TRACE("{}: '{}', real: {}, fun: {}", __func__, reinterpret_cast<const char*>(procName), real_func, func);
//std::cerr << __func__ << ":" << procName << std::endl;
if (func && real_func)
void* func = mangohud_find_glx_ptr( (const char*)procName );
if (func)
return func;
return real_func;
return get_glx_proc_address((const char*)procName);
}
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName) {
void *real_func = get_glx_proc_address((const char*)procName);
void *func = mangohud_find_glx_ptr( (const char*)procName );
SPDLOG_TRACE("{}: '{}', real: {}, fun: {}", __func__, reinterpret_cast<const char*>(procName), real_func, func);
if (func && real_func)
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = mangohud_find_glx_ptr( (const char*)procName );
if (func)
return func;
return real_func;
return get_glx_proc_address((const char*)procName);
}

@ -1,67 +1,19 @@
#include "memory.h"
#include "gpu.h"
#include <inttypes.h>
#include <memory>
#include <functional>
#include <thread>
#include <cstring>
#include <spdlog/spdlog.h>
#ifdef HAVE_XNVCTRL
#include "nvctrl.h"
#endif
#include "timing.hpp"
#ifdef HAVE_NVML
#include "nvidia_info.h"
#endif
#include "amdgpu.h"
using namespace std::chrono_literals;
struct gpuInfo gpu_info {};
struct gpuInfo gpu_info;
amdgpu_files amdgpu {};
bool checkNvidia(const char *pci_dev){
bool nvSuccess = false;
#ifdef HAVE_NVML
nvSuccess = checkNVML(pci_dev) && getNVMLInfo({});
#endif
#ifdef HAVE_XNVCTRL
if (!nvSuccess)
nvSuccess = checkXNVCtrl();
#endif
#ifdef _WIN32
if (!nvSuccess)
nvSuccess = checkNVAPI();
#endif
return nvSuccess;
}
void getNvidiaGpuInfo(const struct overlay_params& params){
void getNvidiaGpuInfo(){
#ifdef HAVE_NVML
if (nvmlSuccess){
getNVMLInfo(params);
getNVMLInfo();
gpu_info.load = nvidiaUtilization.gpu;
gpu_info.temp = nvidiaTemp;
gpu_info.memoryUsed = nvidiaMemory.used / (1024.f * 1024.f * 1024.f);
gpu_info.CoreClock = nvidiaCoreClock;
gpu_info.MemClock = nvidiaMemClock;
gpu_info.powerUsage = nvidiaPowerUsage / 1000;
gpu_info.fan_rpm = false;
gpu_info.memoryTotal = nvidiaMemory.total / (1024.f * 1024.f * 1024.f);
gpu_info.fan_speed = nvidiaFanSpeed;
if (params.enabled[OVERLAY_PARAM_ENABLED_throttling_status]){
gpu_info.is_temp_throttled = (nvml_throttle_reasons & 0x0000000000000060LL) != 0;
gpu_info.is_power_throttled = (nvml_throttle_reasons & 0x000000000000008CLL) != 0;
gpu_info.is_other_throttled = (nvml_throttle_reasons & 0x0000000000000112LL) != 0;
}
#ifdef HAVE_XNVCTRL
static bool nvctrl_available = checkXNVCtrl();
if (nvctrl_available) {
gpu_info.fan_rpm = true;
gpu_info.fan_speed = getNvctrlFanSpeed();
}
#endif
return;
}
#endif
@ -74,59 +26,28 @@ void getNvidiaGpuInfo(const struct overlay_params& params){
gpu_info.CoreClock = nvctrl_info.CoreClock;
gpu_info.MemClock = nvctrl_info.MemClock;
gpu_info.powerUsage = 0;
gpu_info.memoryTotal = nvctrl_info.memoryTotal;
gpu_info.fan_rpm = true;
gpu_info.fan_speed = nvctrl_info.fan_speed;
return;
}
#endif
#ifdef _WIN32
nvapi_util();
#endif
}
void getAmdGpuInfo(){
#ifdef __linux__
int64_t value = 0;
if (metrics_path.empty()){
if (amdgpu.busy) {
rewind(amdgpu.busy);
fflush(amdgpu.busy);
int value = 0;
if (fscanf(amdgpu.busy, "%d", &value) != 1)
value = 0;
gpu_info.load = value;
}
if (amdgpu.memory_clock) {
rewind(amdgpu.memory_clock);
fflush(amdgpu.memory_clock);
if (fscanf(amdgpu.memory_clock, "%" PRId64, &value) != 1)
value = 0;
gpu_info.MemClock = value / 1000000;
}
// TODO: on some gpus this will use the power1_input instead
// this value is instantaneous and should be averaged over time
// probably just average everything in this function to be safe
if (amdgpu.power_usage) {
rewind(amdgpu.power_usage);
fflush(amdgpu.power_usage);
if (fscanf(amdgpu.power_usage, "%" PRId64, &value) != 1)
value = 0;
gpu_info.powerUsage = value / 1000000;
}
if (amdgpu.busy) {
rewind(amdgpu.busy);
fflush(amdgpu.busy);
if (fscanf(amdgpu.busy, "%d", &gpu_info.load) != 1)
gpu_info.load = 0;
gpu_info.load = gpu_info.load;
}
if (amdgpu.fan) {
rewind(amdgpu.fan);
fflush(amdgpu.fan);
if (fscanf(amdgpu.fan, "%" PRId64, &value) != 1)
value = 0;
gpu_info.fan_speed = value;
gpu_info.fan_rpm = true;
if (amdgpu.temp) {
rewind(amdgpu.temp);
fflush(amdgpu.temp);
if (fscanf(amdgpu.temp, "%d", &gpu_info.temp) != 1)
gpu_info.temp = 0;
gpu_info.temp /= 1000;
}
if (amdgpu.vram_total) {
@ -144,8 +65,7 @@ void getAmdGpuInfo(){
value = 0;
gpu_info.memoryUsed = float(value) / (1024 * 1024 * 1024);
}
// On some GPUs SMU can sometimes return the wrong temperature.
// As HWMON is way more visible than the SMU metrics, let's always trust it as it is the most likely to work
if (amdgpu.core_clock) {
rewind(amdgpu.core_clock);
fflush(amdgpu.core_clock);
@ -155,47 +75,21 @@ void getAmdGpuInfo(){
gpu_info.CoreClock = value / 1000000;
}
if (amdgpu.temp){
rewind(amdgpu.temp);
fflush(amdgpu.temp);
int value = 0;
if (fscanf(amdgpu.temp, "%d", &value) != 1)
value = 0;
gpu_info.temp = value / 1000;
}
if (amdgpu.junction_temp){
rewind(amdgpu.junction_temp);
fflush(amdgpu.junction_temp);
int value = 0;
if (fscanf(amdgpu.junction_temp, "%d", &value) != 1)
if (amdgpu.memory_clock) {
rewind(amdgpu.memory_clock);
fflush(amdgpu.memory_clock);
if (fscanf(amdgpu.memory_clock, "%" PRId64, &value) != 1)
value = 0;
gpu_info.junction_temp = value / 1000;
}
if (amdgpu.memory_temp){
rewind(amdgpu.memory_temp);
fflush(amdgpu.memory_temp);
int value = 0;
if (fscanf(amdgpu.memory_temp, "%d", &value) != 1)
value = 0;
gpu_info.memory_temp = value / 1000;
gpu_info.MemClock = value / 1000000;
}
if (amdgpu.gtt_used) {
rewind(amdgpu.gtt_used);
fflush(amdgpu.gtt_used);
if (fscanf(amdgpu.gtt_used, "%" PRId64, &value) != 1)
if (amdgpu.power_usage) {
rewind(amdgpu.power_usage);
fflush(amdgpu.power_usage);
if (fscanf(amdgpu.power_usage, "%" PRId64, &value) != 1)
value = 0;
gpu_info.gtt_used = float(value) / (1024 * 1024 * 1024);
}
if (amdgpu.gpu_voltage_soc) {
rewind(amdgpu.gpu_voltage_soc);
fflush(amdgpu.gpu_voltage_soc);
if (fscanf(amdgpu.gpu_voltage_soc, "%" PRId64, &value) != 1)
value = 0;
gpu_info.voltage = value;
gpu_info.powerUsage = value / 1000000;
}
#endif
}

@ -1,26 +1,21 @@
#pragma once
#ifndef MANGOHUD_GPU_H
#define MANGOHUD_GPU_H
#include <cstdio>
#include <cstdint>
#include "overlay_params.h"
#include <thread>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include "nvctrl.h"
#ifdef HAVE_NVML
#include "nvidia_info.h"
#endif
struct amdgpu_files
{
FILE *vram_total;
FILE *vram_used;
/* The following can be NULL, in that case we're using the gpu_metrics node */
FILE *busy;
FILE *temp;
FILE *junction_temp;
FILE *memory_temp;
FILE *vram_total;
FILE *vram_used;
FILE *core_clock;
FILE *memory_clock;
FILE *power_usage;
FILE *gtt_used;
FILE *fan;
FILE *gpu_voltage_soc;
};
extern amdgpu_files amdgpu;
@ -28,31 +23,14 @@ extern amdgpu_files amdgpu;
struct gpuInfo{
int load;
int temp;
int junction_temp {-1};
int memory_temp {-1};
float memoryUsed;
float memoryTotal;
int MemClock;
int CoreClock;
float powerUsage;
float apu_cpu_power;
int apu_cpu_temp;
bool is_power_throttled;
bool is_current_throttled;
bool is_temp_throttled;
bool is_other_throttled;
float gtt_used;
int fan_speed;
int voltage;
bool fan_rpm;
int powerUsage;
};
extern struct gpuInfo gpu_info;
void getNvidiaGpuInfo(const struct overlay_params& params);
void getNvidiaGpuInfo(void);
void getAmdGpuInfo(void);
void getIntelGpuInfo();
bool checkNvidia(const char *pci_dev);
extern void nvapi_util();
extern bool checkNVAPI();
#endif //MANGOHUD_GPU_H

@ -5,8 +5,8 @@
EXPORT_C_(void*) dlsym(void * handle, const char * name)
{
static void *(*find_glx_ptr)(const char *name) = nullptr;
static void *(*find_egl_ptr)(const char *name) = nullptr;
void *(*find_glx_ptr)(const char *name) = nullptr;
void *(*find_egl_ptr)(const char *name) = nullptr;
if (!find_glx_ptr)
find_glx_ptr = reinterpret_cast<decltype(find_glx_ptr)> (real_dlsym(RTLD_NEXT, "mangohud_find_glx_ptr"));
@ -15,10 +15,8 @@ EXPORT_C_(void*) dlsym(void * handle, const char * name)
find_egl_ptr = reinterpret_cast<decltype(find_egl_ptr)> (real_dlsym(RTLD_NEXT, "mangohud_find_egl_ptr"));
void* func = nullptr;
bool is_angle = real_dlsym(handle, "eglStreamPostD3DTextureANGLE");
void* real_func = real_dlsym(handle, name);
if (find_glx_ptr && real_func) {
if (find_glx_ptr) {
func = find_glx_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
@ -26,7 +24,7 @@ EXPORT_C_(void*) dlsym(void * handle, const char * name)
}
}
if (find_egl_ptr && real_func && !is_angle) {
if (find_egl_ptr) {
func = find_egl_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
@ -35,5 +33,5 @@ EXPORT_C_(void*) dlsym(void * handle, const char * name)
}
//fprintf(stderr,"%s: foreign: %s\n", __func__ , name);
return real_func;
return real_dlsym(handle, name);
}

File diff suppressed because it is too large Load Diff

@ -1,168 +0,0 @@
#pragma once
#include <vector>
#include <string>
#include <utility>
#include <imgui.h>
#include "timing.hpp"
#include <functional>
#include "winesync.h"
#include "vulkan/vulkan.h"
#include <array>
#include "net.h"
#include "overlay_params.h"
#include "shell.h"
struct Function {
std::function<void()> run; // Using std::function instead of a raw function pointer for more flexibility
std::string name;
std::string value;
};
struct overlay_params;
class HudElements{
public:
struct swapchain_stats *sw_stats;
struct overlay_params *params;
struct exec_entry {
int pos;
std::string value;
std::string ret;
};
float ralign_width;
float old_scale;
float res_width, res_height;
bool is_vulkan = true, gamemode_bol = false, vkbasalt_bol = false;
int place;
int text_column = 1;
int table_columns_count = 0;
int g_fsrUpscale = -1;
int g_fsrSharpness = -1;
Clock::time_point last_exec;
std::vector<std::pair<std::string, std::string>> options;
std::vector<Function> ordered_functions;
std::vector<float> gamescope_debug_latency {};
std::vector<float> gamescope_debug_app {};
int min, max, gpu_core_max, gpu_mem_max, cpu_temp_max, gpu_temp_max;
const std::vector<std::string> permitted_params = {
"gpu_load", "cpu_load", "gpu_core_clock", "gpu_mem_clock",
"vram", "ram", "cpu_temp", "gpu_temp"
};
std::vector<exec_entry> exec_list;
std::chrono::steady_clock::time_point overlay_start = std::chrono::steady_clock::now();
uint32_t vendorID;
int hdr_status = 0;
int refresh = 0;
unsigned int vsync = 10;
std::unique_ptr<WineSync> winesync_ptr = nullptr;
std::unique_ptr<Net> net = nullptr;
#ifdef __linux__
std::unique_ptr<Shell> shell = nullptr;
#endif
void sort_elements(const std::pair<std::string, std::string>& option);
void legacy_elements();
void update_exec();
int convert_to_fahrenheit(int celsius);
static void version();
static void time();
static void gpu_stats();
static void cpu_stats();
static void core_load();
static void io_stats();
static void vram();
static void ram();
static void procmem();
static void fps();
static void engine_version();
static void gpu_name();
static void vulkan_driver();
static void arch();
static void wine();
static void frame_timing();
static void media_player();
static void resolution();
static void show_fps_limit();
static void custom_text_center();
static void custom_text();
static void vkbasalt();
static void gamemode();
static void graphs();
static void _exec();
static void battery();
static void fps_only();
static void gamescope_fsr();
static void gamescope_frame_timing();
static void device_battery();
static void frame_count();
static void fan();
static void throttling_status();
static void exec_name();
static void duration();
static void fps_metrics();
static void hdr();
static void refresh_rate();
static void winesync();
static void present_mode();
static void network();
void convert_colors(const struct overlay_params& params);
void convert_colors(bool do_conv, const struct overlay_params& params);
struct hud_colors {
bool convert, update;
ImVec4 cpu,
gpu,
vram,
ram,
swap,
engine,
io,
frametime,
background,
text,
media_player,
wine,
battery,
gpu_load_low,
gpu_load_med,
gpu_load_high,
cpu_load_low,
cpu_load_med,
cpu_load_high,
fps_value_low,
fps_value_med,
fps_value_high,
text_outline,
network;
} colors {};
void TextColored(ImVec4 col, const char *fmt, ...);
std::array<VkPresentModeKHR, 6> presentModes = {
VK_PRESENT_MODE_FIFO_RELAXED_KHR,
VK_PRESENT_MODE_IMMEDIATE_KHR,
VK_PRESENT_MODE_MAILBOX_KHR,
VK_PRESENT_MODE_FIFO_KHR,
VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR,
VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR};
std::map<VkPresentModeKHR, std::string> presentModeMap = {
{VK_PRESENT_MODE_IMMEDIATE_KHR, "IMMEDIATE"},
{VK_PRESENT_MODE_MAILBOX_KHR, "MAILBOX"},
{VK_PRESENT_MODE_FIFO_KHR, "FIFO"},
{VK_PRESENT_MODE_FIFO_RELAXED_KHR, "FIFO Relaxed"},
{VK_PRESENT_MODE_SHARED_DEMAND_REFRESH_KHR, "DEMAND"},
{VK_PRESENT_MODE_SHARED_CONTINUOUS_REFRESH_KHR, "CONTINUOUS"}
};
VkPresentModeKHR cur_present_mode;
std::string get_present_mode(){
if (is_vulkan)
return presentModeMap[cur_present_mode];
else
return vsync == 0 ? "OFF" : "ON";
}
};
extern HudElements HUDElements;

@ -1,132 +0,0 @@
#include "intel.h"
std::unique_ptr<Intel> intel;
void Intel::intel_gpu_thread(){
init = true;
static char stdout_buffer[1024];
static FILE* intel_gpu_top;
if (runtime)
intel_gpu_top = popen("steam-runtime-launch-client --alongside-steam --host -- intel_gpu_top -J -s 500", "r");
else
intel_gpu_top = popen("intel_gpu_top -J -s 500", "r");
int num_line = 0;
std::string buf;
int num_iterations = 0;
while (fgets(stdout_buffer, sizeof(stdout_buffer), intel_gpu_top)) {
if (num_line > 0)
buf += stdout_buffer;
num_line++;
if (strlen(stdout_buffer) < 4 && !strchr(stdout_buffer, '{') && !strchr(stdout_buffer, ',')) {
if (buf[0] != '{')
buf = "{\n" + buf;
if (num_iterations > 0){
buf += "\n}";
json j = json::parse(buf);
if (j.contains("engines"))
if (j["engines"].contains("Render/3D/0"))
if (j["engines"]["Render/3D/0"].contains("busy"))
gpu_info_intel.load = j["engines"]["Render/3D/0"]["busy"].get<int>();
if (j.contains("engines"))
if (j["engines"].contains("Render/3D"))
if (j["engines"]["Render/3D"].contains("busy"))
gpu_info_intel.load = j["engines"]["Render/3D"]["busy"].get<int>();
if (j.contains("frequency"))
if (j["frequency"].contains("actual"))
gpu_info_intel.CoreClock = j["frequency"]["actual"].get<int>();
if (j.contains("power")){
if (j["power"].contains("GPU"))
gpu_info_intel.powerUsage = j["power"]["GPU"].get<float>();
if (j["power"].contains("Package"))
gpu_info_intel.apu_cpu_power = j["power"]["Package"].get<float>();
}
}
buf = "";
num_line = 0;
}
num_iterations++;
if (stop)
break;
}
int exitcode = pclose(intel_gpu_top) / 256;
if (exitcode > 0){
if (exitcode == 127)
SPDLOG_INFO("Failed to open '{}'", "intel_gpu_top");
if (exitcode == 1)
SPDLOG_INFO("Missing permissions for '{}'", "intel_gpu_top");
}
}
uint64_t Intel::get_gpu_time() {
rewind(fdinfo);
fflush(fdinfo);
char line[256];
uint64_t val;
while (fgets(line, sizeof(line), fdinfo)){
if(strstr(line, "drm-engine-render"))
sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val);
}
return val;
}
FILE* Intel::find_fd() {
DIR* dir = opendir("/proc/self/fdinfo");
if (!dir) {
perror("Failed to open directory");
return NULL;
}
static uint64_t val;
static bool found_driver;
for (const auto& entry : fs::directory_iterator("/proc/self/fdinfo")){
FILE* file = fopen(entry.path().string().c_str(), "r");
if (file) {
char line[256];
while (fgets(line, sizeof(line), file)) {
if (strstr(line, "i915") != NULL)
found_driver = true;
if (found_driver){
if(strstr(line, "drm-engine-render")){
sscanf(line, "drm-engine-render: %" SCNu64 " ns", &val);
return file;
}
}
}
}
fclose(file);
}
return NULL; // Return NULL if no matching file is found
}
void Intel::get_fdinfo(){
static uint64_t previous_gpu_time, previous_time, now, gpu_time_now;
gpu_time_now = get_gpu_time();
now = os_time_get_nano();
if (previous_time && previous_gpu_time && gpu_time_now > previous_gpu_time){
float time_since_last = now - previous_time;
float gpu_since_last = gpu_time_now - previous_gpu_time;
auto result = int((gpu_since_last / time_since_last) * 100);
if (result > 100)
result = 100;
gpu_info_intel.load = result;
previous_gpu_time = gpu_time_now;
previous_time = now;
} else {
previous_gpu_time = gpu_time_now;
previous_time = now;
}
}

@ -1,51 +0,0 @@
#include <sys/stat.h>
#include <thread>
#include <nlohmann/json.hpp>
#include <filesystem.h>
#include <inttypes.h>
#include <mesa/util/os_time.h>
#include <spdlog/spdlog.h>
#include "gpu.h"
#include "hud_elements.h"
using json = nlohmann::json;
namespace fs = ghc::filesystem;
class Intel {
private:
bool init = false;
bool runtime = false;
bool stop = false;
struct gpuInfo gpu_info_intel {};
FILE* fdinfo;
struct stat stat_buffer;
std::thread thread;
FILE* find_fd();
void intel_gpu_thread();
uint64_t get_gpu_time();
void get_fdinfo();
public:
Intel() {
if (stat("/run/pressure-vessel", &stat_buffer) == 0)
runtime = true;
fdinfo = find_fd();
// thread = std::thread(&Intel::intel_gpu_thread, this);
}
void update() {
if (fdinfo)
get_fdinfo();
gpu_info = gpu_info_intel;
}
// ~Intel(){
// stop = true;
// thread.join();
// }
};
extern std::unique_ptr<Intel> intel;

@ -2,33 +2,23 @@
#include "string_utils.h"
#include <fstream>
struct iostats g_io_stats;
void getIoStats(void *args) {
iostats *io = reinterpret_cast<iostats *>(args);
if (io) {
io->prev.read_bytes = io->curr.read_bytes;
io->prev.write_bytes = io->curr.write_bytes;
void getIoStats(iostats& io) {
Clock::time_point now = Clock::now(); /* ns */
std::chrono::duration<float> time_diff = now - io.last_update;
io.prev.read_bytes = io.curr.read_bytes;
io.prev.write_bytes = io.curr.write_bytes;
std::string line;
std::ifstream f("/proc/self/io");
while (std::getline(f, line)) {
if (starts_with(line, "read_bytes:")) {
try_stoull(io.curr.read_bytes, line.substr(12));
}
else if (starts_with(line, "write_bytes:")) {
try_stoull(io.curr.write_bytes, line.substr(13));
std::string line;
std::ifstream f("/proc/self/io");
while (std::getline(f, line)) {
if (starts_with(line, "read_bytes:")) {
try_stoull(io->curr.read_bytes, line.substr(12));
}
else if (starts_with(line, "write_bytes:")) {
try_stoull(io->curr.write_bytes, line.substr(13));
}
}
io->diff.read = (io->curr.read_bytes - io->prev.read_bytes) / (1024.f * 1024.f);
io->diff.write = (io->curr.write_bytes - io->prev.write_bytes) / (1024.f * 1024.f);
}
if (io.last_update.time_since_epoch().count()) {
io.diff.read = (io.curr.read_bytes - io.prev.read_bytes) / (1024.f * 1024.f);
io.diff.write = (io.curr.write_bytes - io.prev.write_bytes) / (1024.f * 1024.f);
io.per_second.read = io.diff.read / time_diff.count();
io.per_second.write = io.diff.write / time_diff.count();
}
io.last_update = now;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save