Merge branch 'benchmark-percentages'

pull/286/head^2
FlightlessMango 4 years ago
commit 27df2ef24b

1
.gitignore vendored

@ -38,7 +38,6 @@ lib32-mangohud*.tar.*
# subprojects
subprojects/packagecache/
subprojects/imgui-*/
subprojects/Vulkan-Headers-*/
#GNU Global Metadata

3
.gitmodules vendored

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

@ -136,6 +136,11 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `ram`<br>`vram` | Displays system RAM/VRAM usage |
| `full` | Enables most of the toggleable parameters (currently excludes `histogram`) |
| `font_size=` | Customizeable font size (default=24) |
| `font_size_text=` | Customizeable font size for other text like media metadata (default=24) |
| `font_scale=` | Set global font scale (default=1.0) |
| `font_file` | Change default font (set location to .TTF/.OTF file ) |
| `font_file_text` | Change text font. Otherwise `font_file` is used |
| `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. |
| `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 |
@ -148,12 +153,12 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `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) |
| `vsync`<br> `gl_vsync` | Set vsync for OpenGL or Vulkan |
| `media_player` | Show media player metadata |
| `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 |
| `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. |
| `media_player_order` | Media player metadata field order. Defaults to `title,artist,album`. |
| `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 |
| `version` | Shows current mangohud version |
@ -166,6 +171,9 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `gpu_name` | Displays GPU name from pci.ids |
| `gpu_power` | Display GPU draw in watts |
| `engine_version` | Display OpenGL or vulkan and vulkan-based render engine's version |
| `permit_upload` | Allow uploading of logs to Flightlessmango.com |
| `upload_log` | Change keybind for uploading log |
| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97+AVG+1+0.1` |
Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32`

@ -48,8 +48,22 @@ frame_timing
### Change the hud font size (default is 24)
font_size=24
# font_scale=1.0
# font_size_text=24
# font_scale_media_player = 0.55
### 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/text_font_file 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
### Change the hud position (default is top-left)
position=top-left
@ -91,12 +105,10 @@ background_alpha=0.5
# background_color=020202
# media_player_color=FFFFFF
### Change default font (set location to .TTF/.OTF file )
# font_file
### Show media player metadata
# media_player
# media_player_name = spotify
# media_player_order = title,artist,album
### Specify gpu with pci bus id for amdgpu and NVML stats.
### Set to 'domain:bus:slot.function'
@ -108,6 +120,7 @@ background_alpha=0.5
#toggle_hud=Shift_R+F12
#toggle_logging=Shift_L+F2
#reload_cfg=Shift_L+F4
#upload_log=Shift+F3
################## LOG #################
@ -115,3 +128,8 @@ background_alpha=0.5
# log_duration
### Define name and location of the output file (Required for logging)
# output_file
### Permit uploading logs directly to Flightlessmango.com
# permit_upload=1
### 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
# benchmark_percentiles=

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

@ -1,3 +1,4 @@
#!/usr/bin/env bash
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
MANGOHUD_CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
SU_CMD=$(command -v sudo || command -v doas)

@ -3,12 +3,8 @@
VERSION=$(git describe --tags --dirty)
NAME=MangoHud-$VERSION-Source
# ensure that submodules are present
git submodule update --init
# get everything except submodules
# create archive via git
git archive HEAD --format=tar --prefix=${NAME}/ --output=${NAME}.tar
# add imgui submodule
tar -rf ${NAME}.tar --exclude-vcs --transform="s,^modules/ImGui/src,${NAME}/modules/ImGui/src," modules/ImGui/src
# create DFSG compliant version which excludes NVML
cp ${NAME}.tar ${NAME}-DFSG.tar
tar -f ${NAME}-DFSG.tar --delete ${NAME}/include/nvml.h

@ -0,0 +1,185 @@
#!/usr/bin/env bash
set -e
# Specialized build script for Steam Runtime SDK docker
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//')
dependencies() {
if [[ ! -f build/release/usr/lib64/libMangoHud.so ]]; then
install() {
set +e
for i in $(eval echo $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="{gcc-5-multilib,g++-5-multilib,unzip}"
install
# py3.2 is weird
ln -sf python3.5 /usr/bin/python3
if [[ ! -f ./bin/get-pip.py ]]; then
curl https://bootstrap.pypa.io/get-pip.py -o bin/get-pip.py
python3.5 ./bin/get-pip.py
fi
if [[ $(pip3.5 show meson; echo $?) == 1 || $(pip3.5 show mako; echo $?) == 1 ]]; then
pip3.5 install meson mako
fi
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/meson64/build.ninja" ]]; then
export CC="gcc-5"
export CXX="g++-5"
meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
fi
if [[ ! -f "build/meson32/build.ninja" ]]; then
export CC="gcc-5 -m32"
export CXX="g++-5 -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"
meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
fi
}
build() {
if [[ ! -f "build/meson64/build.ninja" || ! -f "build/meson32/build.ninja" ]]; then
configure
fi
DESTDIR="$PWD/build/release" ninja -C build/meson32 install
DESTDIR="$PWD/build/release" ninja -C build/meson64 install
}
package() {
LIB="build/release/usr/lib/mangohud/lib64/libMangoHud.so"
LIB32="build/release/usr/lib/mangohud/lib32/libMangoHud.so"
if [[ ! -f "$LIB" || "$LIB" -ot "build/meson64/src/libMangoHud.so" ]]; then
build
fi
tar --numeric-owner --owner=0 --group=0 \
-C build/release -cvf "build/MangoHud-package.tar" .
}
release() {
rm build/MangoHud-package.tar
mkdir -p build/MangoHud
package
cp --preserve=mode bin/mangohud-setup.sh build/MangoHud/mangohud-setup.sh
cp build/MangoHud-package.tar build/MangoHud/MangoHud-package.tar
tar --numeric-owner --owner=0 --group=0 \
-C build -czvf build/MangoHud-$VERSION.tar.gz MangoHud
}
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 ] || exec sudo bash "$0" install
/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
/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"
}
clean() {
rm -rf "build"
}
uninstall() {
[ "$UID" -eq 0 ] || exec sudo 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: $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 "\tinstall\t\tInstall MangoHud onto your system"
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"
}
for a in $@; do
case $a in
"") build;;
"pull") git pull;;
"configure") configure;;
"build") build;;
"package") package;;
"install") install;;
"clean") clean;;
"uninstall") uninstall;;
"release") release;;
*)
usage
esac
done
if [[ -z $@ ]]; then
usage no-args
fi

@ -62,7 +62,7 @@ dependencies() {
"Fedora")
MANAGER_QUERY="dnf list installed"
MANAGER_INSTALL="dnf install"
DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel}"
DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel}"
dep_install
unset INSTALL
@ -237,6 +237,7 @@ 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"

@ -120,11 +120,6 @@ foreach a : ['missing-field-initializers', 'format-truncation']
endif
endforeach
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',
@ -151,11 +146,6 @@ foreach a : ['override-init', 'initializer-overrides']
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
@ -173,6 +163,13 @@ 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
if dep_vulkan.found()
datadir = get_option('datadir')
if not datadir.startswith('/')
@ -207,5 +204,7 @@ elif sizeof_ptr == 4
pre_args += '-DMANGOHUD_ARCH="32bit"'
endif
subdir('modules/ImGui')
dearimgui_sp = subproject('dearimgui')
dearimgui_dep = dearimgui_sp.get_variable('dearimgui_dep')
subdir('src')

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

@ -1,11 +0,0 @@
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')
)

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

@ -53,7 +53,9 @@ static bool check_blacklisted() {
return blacklisted;
}
bool is_blacklisted() {
bool is_blacklisted(bool force_recheck) {
static bool blacklisted = check_blacklisted();
if (force_recheck)
blacklisted = check_blacklisted();
return blacklisted;
}

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_BLACKLIST_H
#define MANGOHUD_BLACKLIST_H
bool is_blacklisted();
bool is_blacklisted(bool force_recheck = false);
#endif //MANGOHUD_BLACKLIST_H

@ -76,6 +76,7 @@ void parseConfigFile(overlay_params& params) {
continue;
}
stream.imbue(std::locale::classic());
std::cerr << "parsing config: " << *p;
while (std::getline(stream, line))
{

@ -1,2 +1,8 @@
#pragma once
#ifndef MANGOHUD_CONFIG_H
#define MANGOHUD_CONFIG_H
#include "overlay_params.h"
void parseConfigFile(overlay_params& p);
#endif //MANGOHUD_CONFIG_H

@ -291,12 +291,15 @@ bool CPUStats::GetCpuFile() {
#ifndef NDEBUG
std::cerr << "hwmon: sensor name: " << name << std::endl;
#endif
if (name == "coretemp" && find_temp_input(path, input, "Package id 0")) {
if (name == "coretemp") {
find_temp_input(path, input, "Package id 0");
break;
}
else if ((name == "zenpower" || name == "k10temp") && find_temp_input(path, input, "Tdie")) {
else if ((name == "zenpower" || name == "k10temp")) {
find_temp_input(path, input, "Tdie");
break;
} else if (name == "atk0110" && find_temp_input(path, input, "CPU Temperature")){
} else if (name == "atk0110") {
find_temp_input(path, input, "CPU Temperature");
break;
}
}

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_CPU_H
#define MANGOHUD_CPU_H
#include <vector>
#include <cstdint>
#include <cstdio>
@ -68,3 +72,5 @@ private:
};
extern CPUStats cpuStats;
#endif //MANGOHUD_CPU_H

File diff suppressed because it is too large Load Diff

@ -0,0 +1,363 @@
#pragma once
#ifndef MANGOHUD_DBUS_HELPERS
#define MANGOHUD_DBUS_HELPERS
#include <vector>
#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_fiexd;
} // 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 (requested_type != type()) {
std::cerr << "Type mismatch: '" << ((char)requested_type) << "' vs '"
<< (char)type() << "'\n";
#ifndef NDEBUG
exit(-1);
#else
return T();
#endif
}
T ret;
m_DBus->message_iter_get_basic(&m_resolved_iter, &ret);
return ret;
}
template <>
auto DBusMessageIter_wrap::get_primitive<std::string>() -> std::string {
return std::string(get_primitive<const char*>());
}
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>());
std::cerr << "stringify failed\n";
return std::string();
}
auto DBusMessageIter_wrap::get_array_iter() -> DBusMessageIter_wrap {
if (not is_array()) {
std::cerr << "Not an array; " << (char)type() << "\n";
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) {
std::cerr << "Not a dict entry" << (char)type() << "\n";
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 (not *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 (not m_msg) return *this;
if (not 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 (not 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) {
std::cerr << "MangoHud[" << __func__ << "]: " << err.message << "\n";
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,137 +1,129 @@
#pragma once
#ifndef MANGOHUD_DBUS_INFO_H
#define MANGOHUD_DBUS_INFO_H
#include <array>
#include <stdexcept>
#include <thread>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
#include <mutex>
#include <stdexcept>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#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;
struct {
float pos;
float longest;
int dir = -1;
bool needs_recalc;
float tw0;
float tw1;
float tw2;
} ticker;
bool valid = false;
std::mutex mutex;
bool got_song_data = false;
bool got_playback_data = false;
void clear()
{
void clear() {
artists.clear();
title.clear();
album.clear();
artUrl.clear();
ticker = {};
ticker.dir = -1;
valid = false;
}
};
enum SignalType
{
ST_NAMEOWNERCHANGED,
ST_PROPERTIESCHANGED,
struct mutexed_metadata {
std::mutex mtx;
metadata meta;
struct {
float pos;
float longest;
int dir = -1;
bool needs_recalc = true;
float tw0;
float tw1;
float tw2;
} ticker;
};
struct DBusSignal
{
const char * intf;
const char * signal;
SignalType type;
enum SignalType {
ST_NAMEOWNERCHANGED,
ST_PROPERTIESCHANGED,
};
extern struct metadata main_metadata;
extern struct metadata generic_mpris;
extern struct mutexed_metadata main_metadata;
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;
}
class dbus_manager;
using signal_handler_func = bool (dbus_manager::*)(DBusMessage*, const char*);
struct DBusSignal {
const char* intf;
const char* signal;
signal_handler_func handler;
};
class dbus_manager {
public:
dbus_manager() {}
~dbus_manager();
bool init(const std::string& requested_player);
void deinit();
bool get_media_player_metadata(metadata& meta, std::string name = "");
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();
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*);
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;
const std::array<DBusSignal, 2> m_signals{{
{"org.freedesktop.DBus", "NameOwnerChanged",
&dbus_manager::handle_name_owner_changed},
{"org.freedesktop.DBus.Properties", "PropertiesChanged",
&dbus_manager::handle_properties_changed},
}};
};
extern dbus_manager dbus_mgr;
} // namespace dbusmgr
bool get_media_player_metadata(dbusmgr::dbus_manager& dbus, const std::string& name, metadata& meta);
#endif //MANGOHUD_DBUS_INFO_H

@ -4,7 +4,7 @@
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <linux/limits.h>
#include <limits.h>
#include <iostream>
#include <fstream>
#include <cstring>
@ -100,7 +100,7 @@ bool dir_exists(const std::string& path)
return !stat(path.c_str(), &s) && S_ISDIR(s.st_mode);
}
std::string readlink(const char * link)
std::string read_symlink(const char * link)
{
char result[PATH_MAX] {};
ssize_t count = readlink(link, result, PATH_MAX);
@ -109,7 +109,7 @@ std::string readlink(const char * link)
std::string get_exe_path()
{
return readlink("/proc/self/exe");
return read_symlink("/proc/self/exe");
}
bool get_wine_exe_name(std::string& name, bool keep_ext)

@ -1,4 +1,7 @@
#pragma once
#ifndef MANGOHUD_FILE_UTILS_H
#define MANGOHUD_FILE_UTILS_H
#include <string>
#include <vector>
@ -15,9 +18,11 @@ bool find_folder(const std::string& root, const std::string& prefix, std::string
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 readlink(const char * link);
std::string read_symlink(const char * link);
std::string get_exe_path();
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();
#endif //MANGOHUD_FILE_UTILS_H

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

@ -1,8 +1,10 @@
#pragma once
#ifndef MANGOHUD_GL_GL_H
#define MANGOHUD_GL_GL_H
#ifdef __cplusplus
extern "C" {
#endif
#endif //__cplusplus
void * glXCreateContext(void *, void *, void *, int);
void glXDestroyContext(void *, void*);
@ -24,4 +26,6 @@ unsigned int eglSwapBuffers( void*, void* );
#ifdef __cplusplus
}
#endif
#endif //__cplusplus
#endif //MANGOHUD_GL_GL_H

@ -10,6 +10,7 @@
#include "file_utils.h"
#include "imgui_hud.h"
#include "notify.h"
#include "blacklist.h"
#ifdef HAVE_DBUS
#include "dbus_info.h"
@ -44,8 +45,6 @@ struct GLVec
struct state {
ImGuiContext *imgui_ctx = nullptr;
ImFont* font = nullptr;
ImFont* font1 = nullptr;
};
static GLVec last_vp {}, last_sb {};
@ -68,6 +67,7 @@ void imgui_init()
{
if (cfg_inited)
return;
is_blacklisted(true);
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
notifier.params = &params;
start_notifier(notifier);
@ -131,8 +131,7 @@ void imgui_create(void *ctx)
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
create_fonts(params, state.font, state.font1);
sw_stats.font1 = state.font1;
create_fonts(params, sw_stats.font1, sw_stats.font_text);
// Restore global context or ours might clash with apps that use Dear ImGui
ImGui::SetCurrentContext(saved_ctx);
@ -170,7 +169,7 @@ void imgui_render(unsigned int width, unsigned int height)
if (!state.imgui_ctx)
return;
check_keybinds(params);
check_keybinds(sw_stats, params, vendorID);
update_hud_info(sw_stats, params, vendorID);
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();

@ -1,4 +1,6 @@
#pragma once
#ifndef MANGOHUD_GL_IMGUI_HUD_H
#define MANGOHUD_GL_IMGUI_HUD_H
#include "overlay.h"
#include "imgui_impl_opengl3.h"
@ -13,3 +15,5 @@ void imgui_set_context(void *ctx);
void imgui_render(unsigned int width, unsigned int height);
}} // namespace
#endif //MANGOHUD_GL_IMGUI_HUD_H

@ -68,9 +68,12 @@
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <stdint.h> // intptr_t
#include <sstream>
#include <glad/glad.h>
#include "overlay.h"
namespace MangoHud {
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
@ -91,13 +94,25 @@ static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
static bool g_IsGLES = false;
// Functions
static void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
ImGuiIO& io = ImGui::GetIO();
glDeleteTextures(1, &g_FontTexture);
io.Fonts->TexID = 0;
g_FontTexture = 0;
}
}
static bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
ImGui_ImplOpenGL3_DestroyFontsTexture();
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
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.
io.Fonts->GetTexDataAsAlpha8(&pixels, &width, &height);
// Upload texture to graphics system
GLint last_texture;
@ -110,7 +125,7 @@ static bool ImGui_ImplOpenGL3_CreateFontsTexture()
if (g_IsGLES || g_GlVersion >= 200)
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
@ -121,17 +136,6 @@ static bool ImGui_ImplOpenGL3_CreateFontsTexture()
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)
{
@ -250,7 +254,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
" gl_FragColor = Frag_Color * vec4(1, 1, 1, texture2D(Texture, Frag_UV.st).r);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
@ -260,7 +264,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
" Out_Color = Frag_Color * vec4(1, 1, 1, texture(Texture, Frag_UV.st).r);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
@ -271,7 +275,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
" Out_Color = Frag_Color * vec4(1, 1, 1, texture(Texture, Frag_UV.st).r);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
@ -281,7 +285,7 @@ static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"layout (location = 0) out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
" Out_Color = Frag_Color * vec4(1, 1, 1, texture(Texture, Frag_UV.st).r);\n"
"}\n";
#ifndef NDEBUG
@ -311,16 +315,25 @@ 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[2] = { g_GlslVersionString, vertex_shader };
const GLchar* vertex_shader_with_version[1] = { shader.c_str() };
g_VertHandle = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(g_VertHandle, 2, vertex_shader_with_version, NULL);
glShaderSource(g_VertHandle, 1, vertex_shader_with_version, NULL);
glCompileShader(g_VertHandle);
CheckShader(g_VertHandle, "vertex shader");
const GLchar* fragment_shader_with_version[2] = { g_GlslVersionString, fragment_shader };
ss.str(""); ss.clear();
ss << g_GlslVersionString << fragment_shader;
shader = ss.str();
const GLchar* fragment_shader_with_version[1] = { shader.c_str() };
g_FragHandle = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(g_FragHandle, 2, fragment_shader_with_version, NULL);
glShaderSource(g_FragHandle, 1, fragment_shader_with_version, NULL);
glCompileShader(g_FragHandle);
CheckShader(g_FragHandle, "fragment shader");
@ -467,6 +480,13 @@ void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
if (!glIsTexture(g_FontTexture)) {
#ifndef NDEBUG
fprintf(stderr, "MANGOHUD: GL Texture lost? Regenerating.\n");
#endif
g_FontTexture = 0;
ImGui_ImplOpenGL3_CreateFontsTexture();
}
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
@ -529,7 +549,7 @@ 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)
if (fb_width <= 0 || fb_height <= 0 || draw_data->TotalVtxCount == 0)
return;
// Backup GL state
@ -634,7 +654,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
//#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if ((!g_IsGLES && g_GlVersion >= 320) || (g_IsGLES && g_GlVersion >= 320))
if (g_GlVersion >= 320) // OGL and OGL ES
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)));

@ -22,6 +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 {
@ -40,3 +42,5 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
//IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
#endif //MANGOHUD_IMGUI_IMPL_OPENGL3_H

@ -127,10 +127,11 @@ EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
do_imgui_swap(dpy, drawable);
glx.SwapBuffers(dpy, drawable);
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0){
fps_limit_stats.frameStart = os_time_get_nano();
using namespace std::chrono_literals;
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = os_time_get_nano();
fps_limit_stats.frameEnd = Clock::now();
}
}
@ -141,10 +142,11 @@ EXPORT_C_(int64_t) glXSwapBuffersMscOML(void* dpy, void* drawable, int64_t targe
do_imgui_swap(dpy, drawable);
int64_t ret = glx.SwapBuffersMscOML(dpy, drawable, target_msc, divisor, remainder);
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0){
fps_limit_stats.frameStart = os_time_get_nano();
using namespace std::chrono_literals;
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0s){
fps_limit_stats.frameStart = Clock::now();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = os_time_get_nano();
fps_limit_stats.frameEnd = Clock::now();
}
return ret;
}

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_GPU_H
#define MANGOHUD_GPU_H
#include <thread>
#include <inttypes.h>
#include <stdlib.h>
@ -34,3 +38,5 @@ extern struct gpuInfo gpu_info;
void getNvidiaGpuInfo(void);
void getAmdGpuInfo(void);
#endif //MANGOHUD_GPU_H

@ -5,8 +5,8 @@
EXPORT_C_(void*) dlsym(void * handle, const char * name)
{
void *(*find_glx_ptr)(const char *name) = nullptr;
void *(*find_egl_ptr)(const char *name) = nullptr;
static void *(*find_glx_ptr)(const char *name) = nullptr;
static 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"));

@ -1,4 +1,7 @@
#pragma once
#ifndef MANGOHUD_IOSTATS_H
#define MANGOHUD_IOSTATS_H
#include <pthread.h>
#include <inttypes.h>
@ -18,3 +21,5 @@ struct iostats {
};
void getIoStats(void *args);
#endif //MANGOHUD_IOSTATS_H

@ -1,4 +1,7 @@
#pragma once
#ifndef MANGOHUD_KEYBINDS_H
#define MANGOHUD_KEYBINDS_H
#include "shared_x11.h"
#include "loaders/loader_x11.h"
@ -6,8 +9,7 @@
typedef unsigned long KeySym;
#endif
double elapsedF2, elapsedF12, elapsedReloadCfg;
uint64_t last_f2_press, last_f12_press, reload_cfg_press;
Clock::time_point last_f2_press, last_f12_press, reload_cfg_press, last_upload_press;
#ifdef HAVE_X11
bool keys_are_pressed(const std::vector<KeySym>& keys) {
@ -35,4 +37,6 @@ bool keys_are_pressed(const std::vector<KeySym>& keys) {
return false;
}
#endif
#endif //HAVE_X11
#endif //MANGOHUD_KEYBINDS_H

@ -63,6 +63,14 @@ bool libdbus_loader::Load(const std::string& library_name) {
return false;
}
connection_add_filter =
reinterpret_cast<decltype(this->connection_add_filter)>(
dlsym(library_, "dbus_connection_add_filter"));
if (!connection_add_filter) {
CleanUp(true);
return false;
}
connection_pop_message =
reinterpret_cast<decltype(this->connection_pop_message)>(
dlsym(library_, "dbus_connection_pop_message"));
@ -79,6 +87,22 @@ bool libdbus_loader::Load(const std::string& library_name) {
return false;
}
connection_read_write_dispatch =
reinterpret_cast<decltype(this->connection_read_write)>(
dlsym(library_, "dbus_connection_read_write_dispatch"));
if (!connection_read_write_dispatch) {
CleanUp(true);
return false;
}
connection_remove_filter =
reinterpret_cast<decltype(this->connection_remove_filter)>(
dlsym(library_, "dbus_connection_remove_filter"));
if (!connection_remove_filter) {
CleanUp(true);
return false;
}
connection_send_with_reply_and_block =
reinterpret_cast<decltype(this->connection_send_with_reply_and_block)>(
dlsym(library_, "dbus_connection_send_with_reply_and_block"));
@ -127,6 +151,22 @@ bool libdbus_loader::Load(const std::string& library_name) {
return false;
}
message_get_interface =
reinterpret_cast<decltype(this->message_get_interface)>(
dlsym(library_, "dbus_message_get_interface"));
if (!message_get_interface) {
CleanUp(true);
return false;
}
message_get_member =
reinterpret_cast<decltype(this->message_get_member)>(
dlsym(library_, "dbus_message_get_member"));
if (!message_get_member) {
CleanUp(true);
return false;
}
message_is_signal =
reinterpret_cast<decltype(this->message_is_signal)>(
dlsym(library_, "dbus_message_is_signal"));

@ -24,14 +24,20 @@ class libdbus_loader {
decltype(&::dbus_bus_get) bus_get;
decltype(&::dbus_bus_get_unique_name) bus_get_unique_name;
decltype(&::dbus_bus_remove_match) bus_remove_match;
decltype(&::dbus_connection_add_filter) connection_add_filter;
decltype(&::dbus_connection_pop_message) connection_pop_message;
decltype(&::dbus_connection_read_write) connection_read_write;
decltype(&::dbus_connection_read_write_dispatch) connection_read_write_dispatch;
decltype(&::dbus_connection_remove_filter) connection_remove_filter;
decltype(&::dbus_connection_send_with_reply_and_block) connection_send_with_reply_and_block;
decltype(&::dbus_connection_unref) connection_unref;
decltype(&::dbus_error_free) error_free;
decltype(&::dbus_error_init) error_init;
decltype(&::dbus_error_is_set) error_is_set;
decltype(&::dbus_message_append_args) message_append_args;
decltype(&::dbus_message_get_sender) message_get_sender;
decltype(&::dbus_message_get_interface) message_get_interface;
decltype(&::dbus_message_get_member) message_get_member;
decltype(&::dbus_message_is_signal) message_is_signal;
decltype(&::dbus_message_iter_get_arg_type) message_iter_get_arg_type;
decltype(&::dbus_message_iter_get_basic) message_iter_get_basic;
@ -42,7 +48,6 @@ class libdbus_loader {
decltype(&::dbus_message_unref) message_unref;
decltype(&::dbus_move_error) move_error;
decltype(&::dbus_threads_init_default) threads_init_default;
decltype(&::dbus_message_get_sender) message_get_sender;
private:

@ -0,0 +1,159 @@
#include "logging.h"
#include "overlay.h"
#include <sstream>
#include <iomanip>
string os, cpu, gpu, ram, kernel, driver;
bool sysInfoFetched = false;
double fps;
logData currentLogData = {};
std::unique_ptr<Logger> logger;
string exec(string command) {
char buffer[128];
string result = "";
// Open pipe to file
FILE* pipe = popen(command.c_str(), "r");
if (!pipe) {
return "popen failed!";
}
// read till end of process:
while (!feof(pipe)) {
// use buffer to read and add to result
if (fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
void upload_file(std::string logFile){
std::string command = "curl --include --request POST https://flightlessmango.com/logs -F 'log[game_id]=26506' -F 'log[user_id]=176' -F 'attachment=true' -A 'mangohud' ";
command += " -F 'log[uploads][]=@" + logFile + "'";
command += " | grep Location | cut -c11-";
std::string url = exec(command);
exec("xdg-open " + url);
}
void upload_files(const std::vector<std::string>& logFiles){
std::string command = "curl --include --request POST https://flightlessmango.com/logs -F 'log[game_id]=26506' -F 'log[user_id]=176' -F 'attachment=true' -A 'mangohud' ";
for (auto& file : logFiles)
command += " -F 'log[uploads][]=@" + file + "'";
command += " | grep Location | cut -c11-";
std::string url = exec(command);
exec("xdg-open " + url);
}
void writeFile(string filename){
auto& logArray = logger->get_log_data();
#ifndef NDEBUG
std::cerr << "Writing log file [" << filename << "], " << logArray.size() << " entries\n";
#endif
std::ofstream out(filename, ios::out | ios::app);
out << "os," << "cpu," << "gpu," << "ram," << "kernel," << "driver" << endl;
out << os << "," << cpu << "," << gpu << "," << ram << "," << kernel << "," << driver << endl;
out << "fps," << "cpu_load," << "gpu_load," << "cpu_temp," << "gpu_temp," << "gpu_core_clock," << "gpu_mem_clock," << "gpu_vram_used," << "ram_used," << "elapsed" << endl;
for (size_t i = 0; i < logArray.size(); i++){
out << logArray[i].fps << ",";
out << logArray[i].cpu_load << ",";
out << logArray[i].gpu_load << ",";
out << logArray[i].cpu_temp << ",";
out << logArray[i].gpu_temp << ",";
out << logArray[i].gpu_core_clock << ",";
out << logArray[i].gpu_mem_clock << ",";
out << logArray[i].gpu_vram_used << ",";
out << logArray[i].ram_used << ",";
out << std::chrono::duration_cast<std::chrono::microseconds>(logArray[i].previous).count() << "\n";
}
logger->clear_log_data();
}
string get_log_suffix(){
time_t now_log = time(0);
tm *log_time = localtime(&now_log);
std::ostringstream buffer;
buffer << std::put_time(log_time, "%Y-%m-%d_%H-%M-%S") << ".csv";
string log_name = buffer.str();
return log_name;
}
void logging(void *params_void){
overlay_params *params = reinterpret_cast<overlay_params *>(params_void);
logger->wait_until_data_valid();
while (logger->is_active()){
logger->try_log();
this_thread::sleep_for(chrono::milliseconds(params->log_interval));
}
}
Logger::Logger(overlay_params* in_params)
: m_logging_on(false),
m_values_valid(false),
m_params(in_params)
{
#ifndef NDEBUG
std::cerr << "Logger constructed!\n";
#endif
}
void Logger::start_logging() {
if(m_logging_on) return;
m_values_valid = false;
m_logging_on = true;
m_log_start = Clock::now();
if((not m_params->output_file.empty()) and (m_params->log_interval != 0)){
std::thread(logging, m_params).detach();
}
}
void Logger::stop_logging() {
if(not m_logging_on) return;
m_logging_on = false;
m_log_end = Clock::now();
std::thread(calculate_benchmark_data, m_params).detach();
if(not m_params->output_file.empty()) {
m_log_files.emplace_back(m_params->output_file + get_log_suffix());
std::thread(writeFile, m_log_files.back()).detach();
}
}
void Logger::try_log() {
if(not is_active()) return;
if(not m_values_valid) return;
auto now = Clock::now();
auto elapsedLog = now - m_log_start;
currentLogData.previous = elapsedLog;
currentLogData.fps = fps;
m_log_array.push_back(currentLogData);
if(m_params->log_duration and (elapsedLog >= std::chrono::seconds(m_params->log_duration))){
stop_logging();
}
}
void Logger::wait_until_data_valid() {
std::unique_lock<std::mutex> lck(m_values_valid_mtx);
while(! m_values_valid) m_values_valid_cv.wait(lck);
}
void Logger::notify_data_valid() {
std::unique_lock<std::mutex> lck(m_values_valid_mtx);
m_values_valid = true;
m_values_valid_cv.notify_all();
}
void Logger::upload_last_log() {
if(m_log_files.empty()) return;
std::thread(upload_file, m_log_files.back()).detach();
}

@ -1,65 +1,75 @@
#pragma once
#ifndef MANGOHUD_LOGGING_H
#define MANGOHUD_LOGGING_H
#include <iostream>
#include <vector>
#include <fstream>
#include <chrono>
#include <thread>
#include <condition_variable>
#include "mesa/util/os_time.h"
using namespace std;
#include "timing.hpp"
string os, cpu, gpu, ram, kernel, driver;
bool sysInfoFetched = false;
int gpuLoadLog = 0, cpuLoadLog = 0;
uint64_t elapsedLog;
#include "overlay_params.h"
using namespace std;
struct logData{
double fps;
int cpu;
int gpu;
uint64_t previous;
int cpu_load;
int gpu_load;
int cpu_temp;
int gpu_temp;
int gpu_core_clock;
int gpu_mem_clock;
float gpu_vram_used;
float ram_used;
Clock::duration previous;
};
double fps;
std::vector<logData> logArray;
ofstream out;
int num;
bool loggingOn;
uint64_t log_start, log_end;
void writeFile(string filename){
out.open(filename, ios::out | ios::app);
out << "os," << "cpu," << "gpu," << "ram," << "kernel," << "driver" << endl;
out << os << "," << cpu << "," << gpu << "," << ram << "," << kernel << "," << driver << endl;
for (size_t i = 0; i < logArray.size(); i++)
out << logArray[i].fps << "," << logArray[i].cpu << "," << logArray[i].gpu << "," << logArray[i].previous << endl;
out.close();
logArray.clear();
}
string get_current_time(){
time_t now_log = time(0);
tm *log_time = localtime(&now_log);
std::ostringstream buffer;
buffer << std::put_time(log_time, "%Y-%m-%d_%H-%M-%S");
string date = buffer.str();
return date;
}
void logging(void *params_void){
overlay_params *params = reinterpret_cast<overlay_params *>(params_void);
while (loggingOn){
uint64_t now = os_time_get();
elapsedLog = now - log_start;
logArray.push_back({fps, cpuLoadLog, gpuLoadLog, elapsedLog});
if (params->log_duration && (elapsedLog) >= params->log_duration * 1000000)
loggingOn = false;
else
this_thread::sleep_for(chrono::milliseconds(params->log_interval));
}
writeFile(params->output_file + get_current_time());
}
class Logger {
public:
Logger(overlay_params* in_params);
void start_logging();
void stop_logging();
void try_log();
bool is_active() const { return m_logging_on; }
void wait_until_data_valid();
void notify_data_valid();
auto last_log_end() const noexcept { return m_log_end; }
auto last_log_begin() const noexcept { return m_log_start; }
const std::vector<logData>& get_log_data() const noexcept { return m_log_array; }
void clear_log_data() noexcept { m_log_array.clear(); }
void upload_last_log();
private:
std::vector<logData> m_log_array;
std::vector<std::string> m_log_files;
Clock::time_point m_log_start;
Clock::time_point m_log_end;
bool m_logging_on;
std::mutex m_values_valid_mtx;
std::condition_variable m_values_valid_cv;
bool m_values_valid;
overlay_params* m_params;
};
extern std::unique_ptr<Logger> logger;
extern string os, cpu, gpu, ram, kernel, driver;
extern bool sysInfoFetched;
extern double fps;
extern logData currentLogData;
string exec(string command);
#endif //MANGOHUD_LOGGING_H

@ -0,0 +1,12 @@
# in base
{
global:
overlay_GetInstanceProcAddr;
overlay_GetDeviceProcAddr;
glX*;
egl*;
dlsym;
mangohud_find_glx_ptr;
mangohud_find_egl_ptr;
local: *;
};

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_MEMORY_H
#define MANGOHUD_MEMORY_H
#include <stdio.h>
#include <thread>
@ -13,3 +17,5 @@ struct memory_information {
void update_meminfo(void);
FILE *open_file(const char *file, int *reported);
#endif //MANGOHUD_MEMORY_H

@ -33,6 +33,7 @@ vklayer_files = files(
'elfhacks.cpp',
'real_dlsym.cpp',
'pci_ids.cpp',
'logging.cpp',
)
opengl_files = files(
@ -103,6 +104,10 @@ if dbus_dep.found() and get_option('with_dbus').enabled()
)
endif
link_args = cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL'])
# meson fails to check version-script so just force add
link_args += '-Wl,--version-script,@0@'.format(join_paths(meson.current_source_dir(), 'mangohud.version'))
vklayer_mesa_overlay = shared_library(
'MangoHud',
mangohud_version,
@ -113,24 +118,24 @@ vklayer_mesa_overlay = shared_library(
overlay_spv,
c_args : [
pre_args,
c_vis_args,
no_override_init_args,
vulkan_wsi_args
],
cpp_args : [
pre_args,
cpp_vis_args,
vulkan_wsi_args
],
gnu_symbol_visibility : 'hidden',
dependencies : [
vulkan_wsi_deps,
libimgui_core_dep,
dearimgui_dep,
dbus_dep,
dep_dl,
dep_rt,
dep_pthread,
dep_vulkan],
include_directories : [inc_common],
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL']),
link_args : link_args,
install_dir : libdir_mangohud,
install : true
)
@ -144,16 +149,15 @@ mangohud_dlsym = shared_library(
),
c_args : [
pre_args,
c_vis_args,
no_override_init_args,
],
cpp_args : [
pre_args,
cpp_vis_args,
],
gnu_symbol_visibility : 'hidden',
dependencies : [dep_dl],
include_directories : [inc_common],
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL']),
link_args : link_args,
install_dir : libdir_mangohud,
install : true
)

@ -21,14 +21,19 @@ static void fileChanged(void *params_void) {
struct inotify_event *event =
(struct inotify_event *) &buffer[i];
i += EVENT_SIZE + event->len;
if (event->mask & IN_MODIFY /*|| event->mask & IN_IGNORED*/) {
if (event->mask & IN_MODIFY || event->mask & IN_DELETE_SELF) {
// In the case of IN_DELETE_SELF, some editors may to a save-to-temp-file/delete-original/move-temp-file
// so sleep a little to let file to be replaced
std::this_thread::sleep_for(std::chrono::milliseconds(100));
parse_overlay_config(&local_params, getenv("MANGOHUD_CONFIG"));
std::lock_guard<std::mutex> lk(nt->mutex);
/*if (nt->params->config_file_path != local_params.config_file_path) {
if ((event->mask & IN_DELETE_SELF) || (nt->params->config_file_path != local_params.config_file_path)) {
#ifndef NDEBUG
fprintf(stderr, "MANGOHUD: watching config file: %s\n", local_params.config_file_path.c_str());
#endif
inotify_rm_watch(nt->fd, nt->wd);
nt->wd = inotify_add_watch(nt->fd, local_params.config_file_path.c_str(), IN_MODIFY | IN_DELETE | IN_DELETE_SELF);
}*/
nt->wd = inotify_add_watch(nt->fd, local_params.config_file_path.c_str(), IN_MODIFY | IN_DELETE_SELF);
}
std::lock_guard<std::mutex> lk(nt->mutex);
*nt->params = local_params;
}
}
@ -39,13 +44,13 @@ static void fileChanged(void *params_void) {
bool start_notifier(notify_thread& nt)
{
nt.fd = inotify_init();
nt.wd = inotify_add_watch( nt.fd, nt.params->config_file_path.c_str(), IN_MODIFY);
int flags = fcntl(nt.fd, F_GETFL, 0);
if (fcntl(nt.fd, F_SETFL, flags | O_NONBLOCK))
perror("Set non-blocking failed");
nt.fd = inotify_init1(IN_NONBLOCK);
if (nt.fd < 0) {
perror("MANGOHUD: inotify_init1");
return false;
}
nt.wd = inotify_add_watch(nt.fd, nt.params->config_file_path.c_str(), IN_MODIFY | IN_DELETE_SELF);
if (nt.wd < 0) {
close(nt.fd);
nt.fd = -1;

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_NOTIFY_H
#define MANGOHUD_NOTIFY_H
#include <thread>
#include <mutex>
#include "overlay_params.h"
@ -13,3 +17,5 @@ struct notify_thread
bool start_notifier(notify_thread& nt);
void stop_notifier(notify_thread& nt);
#endif //MANGOHUD_NOTIFY_H

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_NVCTRL_H
#define MANGOHUD_NVCTRL_H
struct nvctrlInfo{
int load;
int temp;
@ -12,3 +16,5 @@ extern bool nvctrlSuccess;
bool checkXNVCtrl(void);
void getNvctrlInfo(void);
char *get_attr_target_string(int attr, int target_type, int target_id);
#endif //MANGOHUD_NVCTRL_H

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_NVIDIA_INFO_H
#define MANGOHUD_NVIDIA_INFO_H
#include <nvml.h>
extern nvmlReturn_t result;
@ -9,3 +13,5 @@ extern bool nvmlSuccess;
bool checkNVML(const char* pciBusId);
bool getNVMLInfo(void);
#endif //MANGOHUD_NVIDIA_INFO_H

File diff suppressed because it is too large Load Diff

@ -10,5 +10,5 @@ layout(location = 0) in struct{
void main()
{
fColor = In.Color * texture(sTexture, In.UV.st);
fColor = In.Color * vec4(1, 1, 1, texture(sTexture, In.UV.st).r);
}

@ -1,9 +1,14 @@
#pragma once
#ifndef MANGOHUD_OVERLAY_H
#define MANGOHUD_OVERLAY_H
#include <string>
#include <stdint.h>
#include <vector>
#include "imgui.h"
#include "overlay_params.h"
#include "iostats.h"
#include "timing.hpp"
struct frame_stat {
uint64_t stats[OVERLAY_PLOTS_MAX];
@ -17,6 +22,7 @@ struct swapchain_stats {
struct frame_stat frames_stats[200];
ImFont* font1 = nullptr;
ImFont* font_text = nullptr;
std::string time;
double fps;
struct iostats io;
@ -44,20 +50,17 @@ struct swapchain_stats {
};
struct fps_limit {
int64_t frameStart;
int64_t frameEnd;
int64_t targetFrameTime;
int64_t frameOverhead;
int64_t sleepTime;
Clock::time_point frameStart;
Clock::time_point frameEnd;
Clock::duration targetFrameTime;
Clock::duration frameOverhead;
Clock::duration sleepTime;
};
struct benchmark_stats {
float ninety;
float avg;
float oneP;
float pointOneP;
float total;
std::vector<float> fps_data;
std::vector<std::pair<std::string, float>> percentile_data;
};
extern struct fps_limit fps_limit_stats;
@ -70,10 +73,12 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
void update_hud_info(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID);
void init_gpu_stats(uint32_t& vendorID, overlay_params& params);
void init_cpu_stats(overlay_params& params);
void check_keybinds(struct overlay_params& params);
void check_keybinds(struct swapchain_stats& sw_stats, struct overlay_params& params, uint32_t vendorID);
void init_system_info(void);
void FpsLimiter(struct fps_limit& stats);
void imgui_custom_style(struct overlay_params& params);
void get_device_name(int32_t vendorID, int32_t deviceID, struct swapchain_stats& sw_stats);
void calculate_benchmark_data(void);
void create_fonts(const overlay_params& params, ImFont*& default_font, ImFont*& small_font);
void calculate_benchmark_data(void *params_void);
void create_fonts(const overlay_params& params, ImFont*& small_font, ImFont*& text_font);
#endif //MANGOHUD_OVERLAY_H

@ -8,6 +8,8 @@
#include <iostream>
#include <string>
#include <sstream>
#include <algorithm>
#include <cctype>
#include "overlay_params.h"
#include "overlay.h"
@ -26,7 +28,6 @@
#endif
static enum overlay_param_position
parse_position(const char *str)
{
if (!str || !strcmp(str, "top-left"))
@ -58,21 +59,13 @@ parse_control(const char *str)
}
static float
parse_font_size(const char *str)
{
return strtof(str, NULL);
}
static float
parse_background_alpha(const char *str)
{
return strtof(str, NULL);
}
static float
parse_alpha(const char *str)
parse_float(const char *str)
{
return strtof(str, NULL);
float val = 0;
std::stringstream ss(str);
ss.imbue(std::locale::classic());
ss >> val;
return val;
}
#ifdef HAVE_X11
@ -113,10 +106,18 @@ parse_reload_cfg(const char *str)
{
return parse_string_to_keysym_vec(str);
}
static std::vector<KeySym>
parse_upload_log(const char *str)
{
return parse_string_to_keysym_vec(str);
}
#else
#define parse_toggle_hud(x) {}
#define parse_toggle_logging(x) {}
#define parse_reload_cfg(x) {}
#define parse_upload_log(x) {}
#endif
static uint32_t
@ -182,6 +183,100 @@ parse_path(const char *str)
return str;
}
static std::vector<media_player_order>
parse_media_player_order(const char *str)
{
std::vector<media_player_order> order;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, ',')) {
trim(token);
std::transform(token.begin(), token.end(), token.begin(), ::tolower);
if (token == "title")
order.push_back(MP_ORDER_TITLE);
else if (token == "artist")
order.push_back(MP_ORDER_ARTIST);
else if (token == "album")
order.push_back(MP_ORDER_ALBUM);
}
return order;
}
static std::vector<std::string>
parse_benchmark_percentiles(const char *str)
{
std::vector<std::string> percentiles;
std::stringstream percent_strings(str);
std::string value;
while (std::getline(percent_strings, value, '+')) {
trim(value);
if (value == "AVG") {
percentiles.push_back(value);
continue;
}
float as_float;
size_t float_len = 0;
try {
as_float = parse_float(value, &float_len);
} catch (const std::invalid_argument&) {
std::cerr << "MANGOHUD: invalid benchmark percentile: '" << value << "'\n";
continue;
}
if (float_len != value.length()) {
std::cerr << "MANGOHUD: invalid benchmark percentile: '" << value << "'\n";
continue;
}
if (as_float > 100 || as_float < 0) {
std::cerr << "MANGOHUD: benchmark percentile is not between 0 and 100 (" << value << ")\n";
continue;
}
percentiles.push_back(value);
}
return percentiles;
}
static uint32_t
parse_font_glyph_ranges(const char *str)
{
uint32_t fg = 0;
std::stringstream ss(str);
std::string token;
while (std::getline(ss, token, ',')) {
trim(token);
std::transform(token.begin(), token.end(), token.begin(), ::tolower);
if (token == "korean")
fg |= FG_KOREAN;
else if (token == "chinese")
fg |= FG_CHINESE_FULL;
else if (token == "chinese_simplified")
fg |= FG_CHINESE_SIMPLIFIED;
else if (token == "japanese")
fg |= FG_JAPANESE;
else if (token == "cyrillic")
fg |= FG_CYRILLIC;
else if (token == "thai")
fg |= FG_THAI;
else if (token == "vietnamese")
fg |= FG_VIETNAMESE;
else if (token == "latin_ext_a")
fg |= FG_LATIN_EXT_A;
else if (token == "latin_ext_b")
fg |= FG_LATIN_EXT_B;
}
return fg;
}
#define parse_width(s) parse_unsigned(s)
#define parse_height(s) parse_unsigned(s)
#define parse_vsync(s) parse_unsigned(s)
@ -192,14 +287,21 @@ parse_path(const char *str)
#define parse_time_format(s) parse_str(s)
#define parse_output_file(s) parse_path(s)
#define parse_font_file(s) parse_path(s)
#define parse_font_file_text(s) parse_path(s)
#define parse_io_read(s) parse_unsigned(s)
#define parse_io_write(s) parse_unsigned(s)
#define parse_pci_dev(s) parse_str(s)
#define parse_media_player_name(s) parse_str(s)
#define parse_font_scale_media_player(s) parse_font_size(s)
#define parse_font_scale_media_player(s) parse_float(s)
#define parse_cpu_text(s) parse_str(s)
#define parse_gpu_text(s) parse_str(s)
#define parse_log_interval(s) parse_unsigned(s)
#define parse_font_size(s) parse_float(s)
#define parse_font_size_text(s) parse_float(s)
#define parse_font_scale(s) parse_float(s)
#define parse_background_alpha(s) parse_float(s)
#define parse_alpha(s) parse_float(s)
#define parse_permit_upload(s) parse_unsigned(s)
#define parse_cpu_color(s) parse_color(s)
#define parse_gpu_color(s) parse_color(s)
@ -364,14 +466,19 @@ parse_overlay_config(struct overlay_params *params,
params->background_color = 0x020202;
params->text_color = 0xffffff;
params->media_player_color = 0xffffff;
params->media_player_name = "spotify";
params->media_player_name = "";
params->font_scale = 1.0f;
params->font_scale_media_player = 0.55f;
params->log_interval = 100;
params->media_player_order = { MP_ORDER_TITLE, MP_ORDER_ARTIST, MP_ORDER_ALBUM };
params->permit_upload = 0;
params->benchmark_percentiles = { "97", "AVG", "1", "0.1" };
#ifdef HAVE_X11
params->toggle_hud = { XK_Shift_R, XK_F12 };
params->toggle_logging = { XK_Shift_L, XK_F2 };
params->reload_cfg = { XK_Shift_L, XK_F4 };
params->upload_log = { XK_Shift_L, XK_F3 };
#endif
// first pass with env var
@ -417,6 +524,9 @@ parse_overlay_config(struct overlay_params *params,
if (env && read_cfg)
parse_overlay_env(params, env);
if (params->font_scale_media_player <= 0.f)
params->font_scale_media_player = 0.55f;
// Convert from 0xRRGGBB to ImGui's format
std::array<unsigned *, 10> colors = {
&params->cpu_color,
@ -448,35 +558,30 @@ parse_overlay_config(struct overlay_params *params,
//increase hud width if io read and write
if (!params->width) {
if ((params->enabled[OVERLAY_PARAM_ENABLED_io_read] || params->enabled[OVERLAY_PARAM_ENABLED_io_write])) {
params->width = 13 * params->font_size;
params->width = 13 * params->font_size * params->font_scale;
} else {
params->width = params->font_size * 11.7;
params->width = params->font_size * params->font_scale * 11.7;
}
}
// set frametime limit
if (params->fps_limit >= 0)
fps_limit_stats.targetFrameTime = int64_t(1000000000.0 / params->fps_limit);
using namespace std::chrono;
if (params->fps_limit > 0)
fps_limit_stats.targetFrameTime = duration_cast<Clock::duration>(duration<double>(1) / params->fps_limit);
else
fps_limit_stats.targetFrameTime = {};
#ifdef HAVE_DBUS
if (params->enabled[OVERLAY_PARAM_ENABLED_media_player]) {
// lock mutexes for config file change notifier thread
{
std::lock_guard<std::mutex> lk(main_metadata.mutex);
main_metadata.clear();
}
{
std::lock_guard<std::mutex> lk(generic_mpris.mutex);
generic_mpris.clear();
}
if (dbusmgr::dbus_mgr.init(params->media_player_name)) {
if (!get_media_player_metadata(dbusmgr::dbus_mgr, params->media_player_name, main_metadata))
std::cerr << "MANGOHUD: Failed to get initial media player metadata." << std::endl;
std::lock_guard<std::mutex> lk(main_metadata.mtx);
main_metadata.meta.clear();
}
dbusmgr::dbus_mgr.init(params->media_player_name);
} else {
dbusmgr::dbus_mgr.deinit();
main_metadata.valid = false;
generic_mpris.valid = false;
main_metadata.meta.valid = false;
}
#endif

@ -1,5 +1,6 @@
#ifndef OVERLAY_PARAMS_H
#define OVERLAY_PARAMS_H
#pragma once
#ifndef MANGOHUD_OVERLAY_PARAMS_H
#define MANGOHUD_OVERLAY_PARAMS_H
#include <string>
#include <vector>
@ -51,6 +52,12 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(fps_sampling_period) \
OVERLAY_PARAM_CUSTOM(output_file) \
OVERLAY_PARAM_CUSTOM(font_file) \
OVERLAY_PARAM_CUSTOM(font_file_text) \
OVERLAY_PARAM_CUSTOM(font_glyph_ranges) \
OVERLAY_PARAM_CUSTOM(font_size) \
OVERLAY_PARAM_CUSTOM(font_size_text) \
OVERLAY_PARAM_CUSTOM(font_scale) \
OVERLAY_PARAM_CUSTOM(font_scale_media_player) \
OVERLAY_PARAM_CUSTOM(position) \
OVERLAY_PARAM_CUSTOM(width) \
OVERLAY_PARAM_CUSTOM(height) \
@ -59,11 +66,10 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(fps_limit) \
OVERLAY_PARAM_CUSTOM(vsync) \
OVERLAY_PARAM_CUSTOM(gl_vsync) \
OVERLAY_PARAM_CUSTOM(font_size) \
OVERLAY_PARAM_CUSTOM(font_scale_media_player) \
OVERLAY_PARAM_CUSTOM(toggle_hud) \
OVERLAY_PARAM_CUSTOM(toggle_logging) \
OVERLAY_PARAM_CUSTOM(reload_cfg) \
OVERLAY_PARAM_CUSTOM(upload_log) \
OVERLAY_PARAM_CUSTOM(offset_x) \
OVERLAY_PARAM_CUSTOM(offset_y) \
OVERLAY_PARAM_CUSTOM(background_alpha) \
@ -84,9 +90,12 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_CUSTOM(pci_dev) \
OVERLAY_PARAM_CUSTOM(media_player_name) \
OVERLAY_PARAM_CUSTOM(media_player_color) \
OVERLAY_PARAM_CUSTOM(media_player_order) \
OVERLAY_PARAM_CUSTOM(cpu_text) \
OVERLAY_PARAM_CUSTOM(gpu_text) \
OVERLAY_PARAM_CUSTOM(log_interval) \
OVERLAY_PARAM_CUSTOM(permit_upload) \
OVERLAY_PARAM_CUSTOM(benchmark_percentiles) \
OVERLAY_PARAM_CUSTOM(help)
enum overlay_param_position {
@ -102,6 +111,24 @@ enum overlay_plots {
OVERLAY_PLOTS_MAX,
};
enum media_player_order {
MP_ORDER_TITLE,
MP_ORDER_ARTIST,
MP_ORDER_ALBUM,
};
enum font_glyph_ranges {
FG_KOREAN = (1u << 0),
FG_CHINESE_FULL = (1u << 1),
FG_CHINESE_SIMPLIFIED = (1u << 2),
FG_JAPANESE = (1u << 3),
FG_CYRILLIC = (1u << 4),
FG_THAI = (1u << 5),
FG_VIETNAMESE = (1u << 6),
FG_LATIN_EXT_A = (1u << 7),
FG_LATIN_EXT_B = (1u << 8),
};
enum overlay_param_enabled {
#define OVERLAY_PARAM_BOOL(name) OVERLAY_PARAM_ENABLED_##name,
#define OVERLAY_PARAM_CUSTOM(name)
@ -130,21 +157,28 @@ struct overlay_params {
unsigned cpu_color, gpu_color, vram_color, ram_color, engine_color, io_color, frametime_color, background_color, text_color;
unsigned media_player_color;
unsigned tableCols;
float font_size;
float font_size, font_scale;
float font_size_text;
float font_scale_media_player;
float background_alpha, alpha;
std::vector<KeySym> toggle_hud;
std::vector<KeySym> toggle_logging;
std::vector<KeySym> reload_cfg;
std::string time_format, output_file, font_file;
std::vector<KeySym> upload_log;
std::string time_format, output_file;
std::string pci_dev;
std::string media_player_name;
std::string cpu_text, gpu_text;
unsigned log_interval;
std::vector<media_player_order> media_player_order;
std::vector<std::string> benchmark_percentiles;
std::string font_file, font_file_text;
uint32_t font_glyph_ranges;
std::string config_file_path;
std::unordered_map<std::string,std::string> options;
int permit_upload;
};
const extern char *overlay_param_names[];
@ -158,4 +192,4 @@ void parse_overlay_config(struct overlay_params *params,
}
#endif
#endif /* OVERLAY_PARAMS_H */
#endif /* MANGOHUD_OVERLAY_PARAMS_H */

@ -23,8 +23,13 @@ std::istream& get_uncommented_line(std::istream& is, std::string &line)
void parse_pciids()
{
std::ifstream file("/usr/share/hwdata/pci.ids");
if(file.fail())
printf("MANGOHUD: can't find file pci.ids\n");
if(file.fail()){
std::ifstream file("/usr/share/misc/pci.ids");
if (file.fail())
printf("MANGOHUD: can't find file pci.ids\n");
}
std::string line;
size_t tabs = 0;

@ -1,3 +1,7 @@
#pragma once
#ifndef MANGOHUD_PCI_IDS_H
#define MANGOHUD_PCI_IDS_H
#include <map>
#include <vector>
@ -17,3 +21,5 @@ struct device
extern std::map<uint32_t /*vendor id*/, std::pair<std::string /*vendor desc*/, std::map<uint32_t /*device id*/, device>>> pci_ids;
void parse_pciids(void);
#endif //MANGOHUD_PCI_IDS_H

@ -1,7 +1,11 @@
#pragma once
#ifndef MANGOHUD_REAL_DLSYM_H
#define MANGOHUD_REAL_DLSYM_H
#define EXPORT_C_(type) extern "C" __attribute__((__visibility__("default"))) type
void *real_dlopen(const char *filename, int flag);
void* real_dlsym( void*, const char* );
void* get_proc_address(const char* name);
#endif //MANGOHUD_REAL_DLSYM_H

@ -1,5 +1,10 @@
#pragma once
#ifndef MANGOHUD_SHARED_X11_H
#define MANGOHUD_SHARED_X11_H
#include <X11/Xlib.h>
Display* get_xdisplay();
bool init_x11();
#endif //MANGOHUD_SHARED_X11_H

@ -1,4 +1,7 @@
#pragma once
#ifndef MANGOHUD_STRING_UTILS_H
#define MANGOHUD_STRING_UTILS_H
#include <string>
#include <iomanip>
#include <iostream>
@ -78,30 +81,34 @@ static std::string itox(T i) {
return ss.str();
}
static bool try_stoi(int& val, const std::string& str, std::size_t* pos = 0, int base = 10)
static bool try_stoi(int& val, const std::string& str)
{
try {
val = std::stoi(str, pos, base);
if (sscanf(str.c_str(), "%d", &val) == 1)
return true;
} catch (std::invalid_argument& e) {
#ifndef NDEBUG
std::cerr << __func__ << ": invalid argument: '" << str << "'" << std::endl;
#endif
}
return false;
}
static bool try_stoull(unsigned long long& val, const std::string& str, std::size_t* pos = 0, int base = 10)
static bool try_stoull(unsigned long long& val, const std::string& str)
{
try {
val = std::stoull(str, pos, base);
if (sscanf(str.c_str(), "%llu", &val) == 1)
return true;
} catch (std::invalid_argument& e) {
#ifndef NDEBUG
std::cerr << __func__ << ": invalid argument: '" << str << "'" << std::endl;
#endif
}
return false;
}
static float parse_float(const std::string& s, std::size_t* float_len = nullptr){
std::stringstream ss(s);
ss.imbue(std::locale::classic());
float ret;
ss >> ret;
if(ss.fail()) throw std::invalid_argument("parse_float: Not a float");
if(float_len != nullptr){
auto pos = ss.tellg();
if(ss.fail()) *float_len = s.size();
else *float_len = pos;
}
return ret;
}
#pragma GCC diagnostic pop
#endif //MANGOHUD_STRING_UTILS_H

@ -0,0 +1,23 @@
#pragma once
#ifndef MANGOHUD_TIMING_HPP
#define MANGOHUD_TIMING_HPP
#include <chrono>
#include "mesa/util/os_time.h"
class MesaClock {
public:
using rep = int64_t;
using period = std::nano;
using duration = std::chrono::duration<rep, period>;
using time_point = std::chrono::time_point<MesaClock>;
const static bool is_steady = true;
static time_point now() noexcept {
return time_point(duration(os_time_get_nano()));
}
};
using Clock = MesaClock;
#endif //MANGOHUD_TIMING_HPP

@ -0,0 +1,108 @@
//-----------------------------------------------------------------------------
// COMPILE-TIME OPTIONS FOR DEAR IMGUI
// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure.
// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions.
//-----------------------------------------------------------------------------
// A) You may edit imconfig.h (and not overwrite it when updating Dear ImGui, or maintain a patch/branch with your modifications to imconfig.h)
// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h"
// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ Dear ImGui is used, which include
// the imgui*.cpp files but also _any_ of your code that uses Dear ImGui. This is because some compile-time options have an affect on data structures.
// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts.
// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using.
//-----------------------------------------------------------------------------
#pragma once
//---- Define assertion handler. Defaults to calling assert().
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
//#define IM_ASSERT(_EXPR) MyAssert(_EXPR)
//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts
//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows
// Using dear imgui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
//#define IMGUI_API __declspec( dllexport )
//#define IMGUI_API __declspec( dllimport )
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names.
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
//---- Disable all of Dear ImGui or don't implement standard windows.
// It is very strongly recommended to NOT disable the demo windows during development. Please read comments in imgui_demo.cpp.
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty. Not recommended.
//#define IMGUI_DISABLE_METRICS_WINDOW // Disable debug/metrics window: ShowMetricsWindow() will be empty.
//---- Don't implement some functions to reduce linkage requirements.
//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc.
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow.
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, ime).
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
//---- Include imgui_user.h at the end of imgui.h as a convenience
//#define IMGUI_INCLUDE_IMGUI_USER_H
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
//#define IMGUI_USE_BGRA_PACKED_COLOR
//---- Use 32-bit for ImWchar (default is 16-bit) to support full unicode code points.
//#define IMGUI_USE_WCHAR32
//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version
// By default the embedded implementations are declared static and not available outside of imgui cpp files.
//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h"
//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h"
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
//---- Unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined, use the much faster STB sprintf library implementation of vsnprintf instead of the one from the default C library.
// Note that stb_sprintf.h is meant to be provided by the user and available in the include path at compile time. Also, the compatibility checks of the arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by STB sprintf.
// #define IMGUI_USE_STB_SPRINTF
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
/*
#define IM_VEC2_CLASS_EXTRA \
ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \
operator MyVec2() const { return MyVec2(x,y); }
#define IM_VEC4_CLASS_EXTRA \
ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \
operator MyVec4() const { return MyVec4(x,y,z,w); }
*/
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
// Your renderer back-end will need to support it (most example renderer back-ends support both 16/32-bit indices).
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
//#define ImDrawIdx unsigned int
//---- Override ImDrawCallback signature (will need to modify renderer back-ends accordingly)
//struct ImDrawList;
//struct ImDrawCmd;
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
//#define ImDrawCallback MyImDrawCallback
//---- Debug Tools: Macro to break in Debugger
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
//#define IM_DEBUG_BREAK IM_ASSERT(0)
//#define IM_DEBUG_BREAK __debugbreak()
//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(),
// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.)
// This adds a small runtime cost which is why it is not enabled by default.
//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX
//---- Debug Tools: Enable slower asserts
//#define IMGUI_DEBUG_PARANOID
//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files.
/*
namespace ImGui
{
void MyFunction(const char* name, const MyMatrix44& v);
}
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,639 @@
// [DEAR IMGUI]
// This is a slightly modified version of stb_rect_pack.h 1.00.
// Those changes would need to be pushed into nothings/stb:
// - Added STBRP__CDECL
// Grep for [DEAR IMGUI] to find the changes.
// stb_rect_pack.h - v1.00 - public domain - rectangle packing
// Sean Barrett 2014
//
// Useful for e.g. packing rectangular textures into an atlas.
// Does not do rotation.
//
// Not necessarily the awesomest packing method, but better than
// the totally naive one in stb_truetype (which is primarily what
// this is meant to replace).
//
// Has only had a few tests run, may have issues.
//
// More docs to come.
//
// No memory allocations; uses qsort() and assert() from stdlib.
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
//
// This library currently uses the Skyline Bottom-Left algorithm.
//
// Please note: better rectangle packers are welcome! Please
// implement them to the same API, but with a different init
// function.
//
// Credits
//
// Library
// Sean Barrett
// Minor features
// Martins Mozeiko
// github:IntellectualKitty
//
// Bugfixes / warning fixes
// Jeremy Jaussaud
// Fabian Giesen
//
// Version history:
//
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
// 0.99 (2019-02-07) warning fixes
// 0.11 (2017-03-03) return packing success/fail result
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
// 0.09 (2016-08-27) fix compiler warnings
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
// 0.05: added STBRP_ASSERT to allow replacing assert
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
// 0.01: initial release
//
// LICENSE
//
// See end of file for license information.
//////////////////////////////////////////////////////////////////////////////
//
// INCLUDE SECTION
//
#ifndef STB_INCLUDE_STB_RECT_PACK_H
#define STB_INCLUDE_STB_RECT_PACK_H
#define STB_RECT_PACK_VERSION 1
#ifdef STBRP_STATIC
#define STBRP_DEF static
#else
#define STBRP_DEF extern
#endif
#ifdef __cplusplus
extern "C" {
#endif
typedef struct stbrp_context stbrp_context;
typedef struct stbrp_node stbrp_node;
typedef struct stbrp_rect stbrp_rect;
#ifdef STBRP_LARGE_RECTS
typedef int stbrp_coord;
#else
typedef unsigned short stbrp_coord;
#endif
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
// Assign packed locations to rectangles. The rectangles are of type
// 'stbrp_rect' defined below, stored in the array 'rects', and there
// are 'num_rects' many of them.
//
// Rectangles which are successfully packed have the 'was_packed' flag
// set to a non-zero value and 'x' and 'y' store the minimum location
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
// if you imagine y increasing downwards). Rectangles which do not fit
// have the 'was_packed' flag set to 0.
//
// You should not try to access the 'rects' array from another thread
// while this function is running, as the function temporarily reorders
// the array while it executes.
//
// To pack into another rectangle, you need to call stbrp_init_target
// again. To continue packing into the same rectangle, you can call
// this function again. Calling this multiple times with multiple rect
// arrays will probably produce worse packing results than calling it
// a single time with the full rectangle array, but the option is
// available.
//
// The function returns 1 if all of the rectangles were successfully
// packed and 0 otherwise.
struct stbrp_rect
{
// reserved for your use:
int id;
// input:
stbrp_coord w, h;
// output:
stbrp_coord x, y;
int was_packed; // non-zero if valid packing
}; // 16 bytes, nominally
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
// Initialize a rectangle packer to:
// pack a rectangle that is 'width' by 'height' in dimensions
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
//
// You must call this function every time you start packing into a new target.
//
// There is no "shutdown" function. The 'nodes' memory must stay valid for
// the following stbrp_pack_rects() call (or calls), but can be freed after
// the call (or calls) finish.
//
// Note: to guarantee best results, either:
// 1. make sure 'num_nodes' >= 'width'
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
//
// If you don't do either of the above things, widths will be quantized to multiples
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
//
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
// may run out of temporary storage and be unable to pack some rectangles.
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
// Optionally call this function after init but before doing any packing to
// change the handling of the out-of-temp-memory scenario, described above.
// If you call init again, this will be reset to the default (false).
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
// Optionally select which packing heuristic the library should use. Different
// heuristics will produce better/worse results for different data sets.
// If you call init again, this will be reset to the default.
enum
{
STBRP_HEURISTIC_Skyline_default=0,
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
STBRP_HEURISTIC_Skyline_BF_sortHeight
};
//////////////////////////////////////////////////////////////////////////////
//
// the details of the following structures don't matter to you, but they must
// be visible so you can handle the memory allocations for them
struct stbrp_node
{
stbrp_coord x,y;
stbrp_node *next;
};
struct stbrp_context
{
int width;
int height;
int align;
int init_mode;
int heuristic;
int num_nodes;
stbrp_node *active_head;
stbrp_node *free_head;
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
};
#ifdef __cplusplus
}
#endif
#endif
//////////////////////////////////////////////////////////////////////////////
//
// IMPLEMENTATION SECTION
//
#ifdef STB_RECT_PACK_IMPLEMENTATION
#ifndef STBRP_SORT
#include <stdlib.h>
#define STBRP_SORT qsort
#endif
#ifndef STBRP_ASSERT
#include <assert.h>
#define STBRP_ASSERT assert
#endif
// [DEAR IMGUI] Added STBRP__CDECL
#ifdef _MSC_VER
#define STBRP__NOTUSED(v) (void)(v)
#define STBRP__CDECL __cdecl
#else
#define STBRP__NOTUSED(v) (void)sizeof(v)
#define STBRP__CDECL
#endif
enum
{
STBRP__INIT_skyline = 1
};
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
{
switch (context->init_mode) {
case STBRP__INIT_skyline:
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
context->heuristic = heuristic;
break;
default:
STBRP_ASSERT(0);
}
}
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
{
if (allow_out_of_mem)
// if it's ok to run out of memory, then don't bother aligning them;
// this gives better packing, but may fail due to OOM (even though
// the rectangles easily fit). @TODO a smarter approach would be to only
// quantize once we've hit OOM, then we could get rid of this parameter.
context->align = 1;
else {
// if it's not ok to run out of memory, then quantize the widths
// so that num_nodes is always enough nodes.
//
// I.e. num_nodes * align >= width
// align >= width / num_nodes
// align = ceil(width/num_nodes)
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
}
}
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
{
int i;
#ifndef STBRP_LARGE_RECTS
STBRP_ASSERT(width <= 0xffff && height <= 0xffff);
#endif
for (i=0; i < num_nodes-1; ++i)
nodes[i].next = &nodes[i+1];
nodes[i].next = NULL;
context->init_mode = STBRP__INIT_skyline;
context->heuristic = STBRP_HEURISTIC_Skyline_default;
context->free_head = &nodes[0];
context->active_head = &context->extra[0];
context->width = width;
context->height = height;
context->num_nodes = num_nodes;
stbrp_setup_allow_out_of_mem(context, 0);
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
context->extra[0].x = 0;
context->extra[0].y = 0;
context->extra[0].next = &context->extra[1];
context->extra[1].x = (stbrp_coord) width;
#ifdef STBRP_LARGE_RECTS
context->extra[1].y = (1<<30);
#else
context->extra[1].y = 65535;
#endif
context->extra[1].next = NULL;
}
// find minimum y position if it starts at x1
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
{
stbrp_node *node = first;
int x1 = x0 + width;
int min_y, visited_width, waste_area;
STBRP__NOTUSED(c);
STBRP_ASSERT(first->x <= x0);
#if 0
// skip in case we're past the node
while (node->next->x <= x0)
++node;
#else
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
#endif
STBRP_ASSERT(node->x <= x0);
min_y = 0;
waste_area = 0;
visited_width = 0;
while (node->x < x1) {
if (node->y > min_y) {
// raise min_y higher.
// we've accounted for all waste up to min_y,
// but we'll now add more waste for everything we've visted
waste_area += visited_width * (node->y - min_y);
min_y = node->y;
// the first time through, visited_width might be reduced
if (node->x < x0)
visited_width += node->next->x - x0;
else
visited_width += node->next->x - node->x;
} else {
// add waste area
int under_width = node->next->x - node->x;
if (under_width + visited_width > width)
under_width = width - visited_width;
waste_area += under_width * (min_y - node->y);
visited_width += under_width;
}
node = node->next;
}
*pwaste = waste_area;
return min_y;
}
typedef struct
{
int x,y;
stbrp_node **prev_link;
} stbrp__findresult;
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
{
int best_waste = (1<<30), best_x, best_y = (1 << 30);
stbrp__findresult fr;
stbrp_node **prev, *node, *tail, **best = NULL;
// align to multiple of c->align
width = (width + c->align - 1);
width -= width % c->align;
STBRP_ASSERT(width % c->align == 0);
// if it can't possibly fit, bail immediately
if (width > c->width || height > c->height) {
fr.prev_link = NULL;
fr.x = fr.y = 0;
return fr;
}
node = c->active_head;
prev = &c->active_head;
while (node->x + width <= c->width) {
int y,waste;
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
// bottom left
if (y < best_y) {
best_y = y;
best = prev;
}
} else {
// best-fit
if (y + height <= c->height) {
// can only use it if it first vertically
if (y < best_y || (y == best_y && waste < best_waste)) {
best_y = y;
best_waste = waste;
best = prev;
}
}
}
prev = &node->next;
node = node->next;
}
best_x = (best == NULL) ? 0 : (*best)->x;
// if doing best-fit (BF), we also have to try aligning right edge to each node position
//
// e.g, if fitting
//
// ____________________
// |____________________|
//
// into
//
// | |
// | ____________|
// |____________|
//
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
//
// This makes BF take about 2x the time
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
tail = c->active_head;
node = c->active_head;
prev = &c->active_head;
// find first node that's admissible
while (tail->x < width)
tail = tail->next;
while (tail) {
int xpos = tail->x - width;
int y,waste;
STBRP_ASSERT(xpos >= 0);
// find the left position that matches this
while (node->next->x <= xpos) {
prev = &node->next;
node = node->next;
}
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
if (y + height <= c->height) {
if (y <= best_y) {
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
best_x = xpos;
STBRP_ASSERT(y <= best_y);
best_y = y;
best_waste = waste;
best = prev;
}
}
}
tail = tail->next;
}
}
fr.prev_link = best;
fr.x = best_x;
fr.y = best_y;
return fr;
}
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
{
// find best position according to heuristic
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
stbrp_node *node, *cur;
// bail if:
// 1. it failed
// 2. the best node doesn't fit (we don't always check this)
// 3. we're out of memory
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
res.prev_link = NULL;
return res;
}
// on success, create new node
node = context->free_head;
node->x = (stbrp_coord) res.x;
node->y = (stbrp_coord) (res.y + height);
context->free_head = node->next;
// insert the new node into the right starting point, and
// let 'cur' point to the remaining nodes needing to be
// stiched back in
cur = *res.prev_link;
if (cur->x < res.x) {
// preserve the existing one, so start testing with the next one
stbrp_node *next = cur->next;
cur->next = node;
cur = next;
} else {
*res.prev_link = node;
}
// from here, traverse cur and free the nodes, until we get to one
// that shouldn't be freed
while (cur->next && cur->next->x <= res.x + width) {
stbrp_node *next = cur->next;
// move the current node to the free list
cur->next = context->free_head;
context->free_head = cur;
cur = next;
}
// stitch the list back in
node->next = cur;
if (cur->x < res.x + width)
cur->x = (stbrp_coord) (res.x + width);
#ifdef _DEBUG
cur = context->active_head;
while (cur->x < context->width) {
STBRP_ASSERT(cur->x < cur->next->x);
cur = cur->next;
}
STBRP_ASSERT(cur->next == NULL);
{
int count=0;
cur = context->active_head;
while (cur) {
cur = cur->next;
++count;
}
cur = context->free_head;
while (cur) {
cur = cur->next;
++count;
}
STBRP_ASSERT(count == context->num_nodes+2);
}
#endif
return res;
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
if (p->h > q->h)
return -1;
if (p->h < q->h)
return 1;
return (p->w > q->w) ? -1 : (p->w < q->w);
}
// [DEAR IMGUI] Added STBRP__CDECL
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
{
const stbrp_rect *p = (const stbrp_rect *) a;
const stbrp_rect *q = (const stbrp_rect *) b;
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
}
#ifdef STBRP_LARGE_RECTS
#define STBRP__MAXVAL 0xffffffff
#else
#define STBRP__MAXVAL 0xffff
#endif
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
{
int i, all_rects_packed = 1;
// we use the 'was_packed' field internally to allow sorting/unsorting
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = i;
}
// sort according to heuristic
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
for (i=0; i < num_rects; ++i) {
if (rects[i].w == 0 || rects[i].h == 0) {
rects[i].x = rects[i].y = 0; // empty rect needs no space
} else {
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
if (fr.prev_link) {
rects[i].x = (stbrp_coord) fr.x;
rects[i].y = (stbrp_coord) fr.y;
} else {
rects[i].x = rects[i].y = STBRP__MAXVAL;
}
}
}
// unsort
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
// set was_packed flags and all_rects_packed status
for (i=0; i < num_rects; ++i) {
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
if (!rects[i].was_packed)
all_rects_packed = 0;
}
// return the all_rects_packed status
return all_rects_packed;
}
#endif
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -0,0 +1,24 @@
project(
'Dear ImGui core library',
'cpp',
license : 'MIT',
version : '1.78-WIP-tables-fe3637fa607257ff83bf61092d2173f83b311527',
)
dearimgui_inc = include_directories('.')
dearimgui_src = files(
'imgui.cpp',
'imgui_draw.cpp',
'imgui_widgets.cpp',
)
dearimgui_lib = static_library(
'dearimgui',
dearimgui_src,
include_directories : dearimgui_inc,
)
dearimgui_dep = declare_dependency(
link_with : dearimgui_lib,
include_directories : dearimgui_inc,
)
Loading…
Cancel
Save