Merge branch 'develop'

pull/148/head
FlightlessMango 4 years ago
commit 2177aa35dc

4
.gitignore vendored

@ -37,3 +37,7 @@ v*.tar.gz
*.out
*.app
# subprojects
subprojects/packagecache/
subprojects/imgui-*/
subprojects/Vulkan-Headers-*/

@ -98,10 +98,12 @@ You can find an example config in /usr/share/doc/mangohud
---
### `MANGOHUD_CONFIG` environment variable
### `MANGOHUD_CONFIG` and `MANGOHUD_CONFIGFILE` environment variables
You can also customize the hud by using the `MANGOHUD_CONFIG` environment variable while separating different options with a comma. This takes priority over any config file.
You can also specify configuration file with `MANGOHUD_CONFIGFILE=/path/to/config` for applications whose names are hard to guess (java, python etc).
A partial list of parameters are below. See the config file for a complete list.
| Variable | Description |
@ -114,6 +116,7 @@ A partial list of parameters are below. See the config file for a complete list.
| `font_size=` | Customizeable font size (default=24) |
| `width=`<br>`height=` | Customizeable hud dimensions (in pixels) |
| `position=` | Location of the hud: `top-left` (default), `top-right`, `bottom-left`, `bottom-right` |
| `offset_x` `offset_y` | Hud position offsets |
| `no_display` | Hide the hud by default |
| `toggle_hud=`<br>`toggle_logging=` | Modifiable toggle hotkeys. Default are F12 and F2, respectively. |
| `reload_cfg=` | Change keybind for reloading the config |
@ -126,6 +129,10 @@ A partial list of parameters are below. See the config file for a complete list.
| `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 Spotify metadata |
| `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 |
Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32`

@ -26,6 +26,9 @@ gpu_stats
# gpu_core_clock
# gpu_mem_clock
### Display loaded MangoHud architecture
# arch
### Display the frametime line graph
frame_timing
@ -88,6 +91,13 @@ background_alpha=0.5
# crosshair_size=
# crosshair_color=RRGGBB
### Show Spotify metadata
# media_player
### Specify gpu with pci bus id for amdgpu and NVML stats.
### Set to 'domain:bus:slot.function'
# pci_dev = 0:0a:0.0
################## INTERACTION #################
### Change toggle keybinds for the hud & logging

@ -1,8 +1,8 @@
#!/bin/sh
MANGOHUD_LIB_NAME="libMangoHud.so"
if [ "$MANGOHUD_NODLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="libMangoHud_nodlsym.so"
if [ "$MANGOHUD_DLSYM" = "1" ]; then
MANGOHUD_LIB_NAME="libMangoHud_dlsym.so:${MANGOHUD_LIB_NAME}"
fi
if [ "$#" -eq 0 ]; then

@ -1,4 +1,6 @@
#!/bin/bash
#!/usr/bin/env bash
set -e
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}"
@ -33,6 +35,7 @@ dependencies() {
esac
}
install() {
set +e
for i in $(eval echo $DEPS); do
$MANAGER_QUERY "$i" &> /dev/null
if [[ $? == 1 ]]; then
@ -45,20 +48,21 @@ dependencies() {
$SU_CMD $MANAGER_INSTALL $INSTALL
fi
fi
set -e
}
echo "# Checking Dependencies"
case $DISTRO in
"Arch Linux"|"Manjaro")
"Arch Linux"|"Manjaro Linux")
MANAGER_QUERY="pacman -Q"
MANAGER_INSTALL="pacman -S"
DEPS="{gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd}"
DEPS="{gcc,meson,pkgconf,python-mako,glslang,libglvnd,lib32-libglvnd,libxnvctrl}"
install
;;
"Fedora")
MANAGER_QUERY="dnf list installed"
MANAGER_INSTALL="dnf install"
DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel}"
DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel}"
install
unset INSTALL
@ -68,11 +72,11 @@ dependencies() {
*"buntu"|"Linux Mint"|"Debian GNU/Linux"|"Zorin OS"|"Pop!_OS"|"elementary OS")
MANAGER_QUERY="dpkg-query -s"
MANAGER_INSTALL="apt install"
DEPS="{gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev:i386}"
DEPS="{gcc,g++,gcc-multilib,g++-multilib,ninja-build,python3-pip,python3-setuptools,python3-wheel,pkg-config,mesa-common-dev,libx11-dev,libxnvctrl-dev,libdbus-1-dev}"
install
if [[ $($SU_CMD pip3 show meson; echo $?) == 1 || $($SU_CMD pip3 show mako; echo $?) == 1 ]]; then
$SU_CMD pip3 install meson mako
$SU_CMD pip3 install 'meson>=0.54' mako
fi
if [[ ! -f /usr/local/bin/glslangValidator ]]; then
wget https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip
@ -82,10 +86,27 @@ dependencies() {
fi
;;
"openSUSE Leap"|"openSUSE Tumbleweed")
PACKMAN_PKGS="libXNVCtrl-devel"
case $DISTRO in
"openSUSE Leap")
echo "You may have to enable packman repository for some extra packages: ${PACKMAN_PKGS}"
echo "zypper ar -cfp 90 https://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Leap_15.1/ packman"
;;
"openSUSE Tumbleweed")
echo "You may have to enable packman repository for some extra packages: ${PACKMAN_PKGS}"
echo "zypper ar -cfp 90 http://ftp.gwdg.de/pub/linux/misc/packman/suse/openSUSE_Tumbleweed/ packman"
;;
esac
MANAGER_QUERY="rpm -q"
MANAGER_INSTALL="zypper install"
DEPS="{gcc-c++,gcc-c++-32bit,meson,libpkgconf-devel,python3-Mako,libX11-devel,libX11-devel-32bit,glslang-devel,libglvnd-devel,libglvnd-devel-32bit,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel}"
DEPS="{gcc-c++,gcc-c++-32bit,libpkgconf-devel,ninja,python3-pip,python3-Mako,libX11-devel,glslang-devel,glibc-devel,glibc-devel-32bit,libstdc++-devel,libstdc++-devel-32bit,Mesa-libGL-devel,dbus-1-devel,${PACKMAN_PKGS}}"
install
if [[ $(sudo pip3 show meson; echo $?) == 1 ]]; then
sudo pip3 install 'meson>=0.54'
fi
;;
"Solus")
unset MANAGER_QUERY
@ -121,14 +142,14 @@ configure() {
dependencies
git submodule update --init --depth 50
if [[ ! -f "build/meson64/build.ninja" ]]; then
meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false
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 -m32"
export CXX="g++ -m32"
export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
export LLVM_CONFIG="/usr/bin/llvm-config32"
meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false
meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
fi
}
@ -169,6 +190,8 @@ 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

@ -0,0 +1,290 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are 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 Materials.
**
** THE MATERIALS ARE 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
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef _WIN64
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

@ -28,20 +28,21 @@
Pyry Haulos <pyry.haulos@gmail.com>
*/
#pragma once
#include <elf.h>
#include <link.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
#define __PUBLIC __attribute__ ((visibility ("default")))
//#define __PUBLIC __attribute__ ((visibility ("default")))
#define __PUBLIC
#ifdef __x86_64__
#if UINTPTR_MAX == 0xffffffffffffffff
# define __elf64
#endif
#ifdef __i386__
#else
# define __elf32
#endif

File diff suppressed because it is too large Load Diff

@ -1,26 +1,6 @@
# Copyright © 2019 Intel Corporation
# 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.
project('MangoHud',
['c', 'cpp'],
version : 'v0.3.1',
version : 'v0.3.5',
license : 'MIT',
default_options : ['buildtype=release', 'c_std=c99', 'cpp_std=c++14']
)
@ -30,6 +10,11 @@ cpp = meson.get_compiler('cpp')
prog_python = import('python').find_installation('python3')
null_dep = dependency('', required : false)
mangohud_version = vcs_tag(
command: ['git', 'describe', '--tags', '--dirty=+'],
input: 'version.h.in',
output: 'version.h')
pre_args = [
'-D__STDC_CONSTANT_MACROS',
'-D__STDC_FORMAT_MACROS',
@ -92,25 +77,30 @@ endforeach
vulkan_wsi_args = []
vulkan_wsi_deps = []
with_platform_x11 = true
with_platform_wayland = false
dep_x11 = dependency('x11', required: get_option('with_x11'))
dep_wayland_client = dependency('wayland-client',
required: get_option('with_wayland'), version : '>=1.11')
if with_platform_x11
dep_x11 = dependency('x11')
if dep_x11.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_XLIB_KHR']
vulkan_wsi_deps += dep_x11
vulkan_wsi_deps += dep_x11.partial_dependency(compile_args : true, includes : true)
endif
if with_platform_wayland
dep_wayland_client = dependency('wayland-client', version : '>=1.11')
if dep_wayland_client.found()
vulkan_wsi_args += ['-DVK_USE_PLATFORM_WAYLAND_KHR']
vulkan_wsi_deps += dep_wayland_client
endif
if not dep_x11.found() and not dep_wayland_client.found()
error('At least one of "with_x11" and "with_wayland" should be enabled')
endif
inc_common = [
include_directories('include'),
]
dep_vulkan = dependency('vulkan', required: get_option('use_system_vulkan'))
dep_pthread = dependency('threads')
dbus_dep = dependency('dbus-1', required: get_option('with_dbus')).partial_dependency(compile_args : true, includes : true)
# Check for generic C arguments
c_args = []
@ -183,18 +173,16 @@ else
dep_dl = cc.find_library('dl')
endif
dep_pthread = cc.find_library('pthread')
if dep_vulkan.found()
vulkan_headers = []
datadir = get_option('datadir')
if not datadir.startswith('/')
datadir = get_option('prefix') / datadir
endif
vk_api_xml = files(datadir / 'vulkan/registry/vk.xml')
else
subdir('modules/Vulkan-Headers')
vulkan_headers = vk_api_xml
vkh_sp = subproject('vulkanheaders')
vk_api_xml = vkh_sp.get_variable('vulkan_api_xml')
dep_vulkan = vkh_sp.get_variable('vulkanheaders_dep')
endif
vk_enum_to_str = custom_target(
@ -212,6 +200,13 @@ util_files = files(
'src/mesa/util/os_time.c',
)
sizeof_ptr = cc.sizeof('void*')
if sizeof_ptr == 8
pre_args += '-DMANGOHUD_ARCH="64bit"'
elif sizeof_ptr == 4
pre_args += '-DMANGOHUD_ARCH="32bit"'
endif
if get_option('use_system_nvml')
cpp_nvml_args = '-DUSE_SYSTEM_NVML'
else

@ -4,3 +4,8 @@ option('use_system_nvml', type : 'boolean', value : false, description : 'Use sy
option('mangohud_prefix', type : 'string', value : '', description: 'Add prefix to cross-compiled library, like "lib32-".')
option('append_libdir_mangohud', type : 'boolean', value : true, description: 'Append "mangohud" to libdir path or not.')
option('include_doc', type : 'boolean', value : true, description: 'Include the example config')
option('with_xnvctrl', type : 'feature', value : 'enabled', description: 'Enable XNVCtrl support')
option('with_x11', type : 'feature', value : 'enabled')
option('with_wayland', type : 'feature', value : 'disabled')
option('with_dbus', type : 'feature', value : 'enabled')
option('with_dlsym', type : 'feature', value : 'disabled')

@ -1 +1 @@
Subproject commit 96a2c4619b0c8009f684556683b2e1b6408bb0dc
Subproject commit 1f02d240b38f445abb0381ade0867752d5d2bc7b

@ -1,11 +0,0 @@
#!/bin/sh
GIT="$1"
BUILD_DIR="$2"
if [ ! -f "$BUILD_DIR/registry/vk.xml" ]; then
"$GIT" clone --depth 1 https://github.com/KhronosGroup/Vulkan-Headers.git "$BUILD_DIR"
fi
ln -sf "registry/vk.xml" modules/Vulkan-Headers/
ln -sf "include/vulkan" modules/Vulkan-Headers/

@ -1,12 +0,0 @@
prog_git = find_program('git')
prog_sh = find_program('sh')
script_clone_hdrs = files('clone_headers.sh')
vk_api_xml = custom_target(
'vk_api_xml',
input : [],
output : ['vk.xml'], # because output can't have segments, link vk.xml to this subdir dir in build prefix
command : [
prog_sh, script_clone_hdrs, prog_git, meson.current_build_dir()
],
)

@ -0,0 +1,54 @@
#include <vector>
#include <string>
#include <algorithm>
#include "blacklist.h"
#include "string_utils.h"
#include "file_utils.h"
static std::string get_proc_name() {
#ifdef _GNU_SOURCE_OFF
std::string p(program_invocation_name);
std::string proc_name = p.substr(p.find_last_of("/\\") + 1);
#else
std::string p = get_exe_path();
std::string proc_name;
if (ends_with(p, "wine-preloader") || ends_with(p, "wine64-preloader")) {
get_wine_exe_name(proc_name, true);
} else {
proc_name = p.substr(p.find_last_of("/\\") + 1);
}
#endif
return proc_name;
}
static bool check_blacklisted() {
std::vector<std::string> blacklist {
"Battle.net.exe",
"BethesdaNetLauncher.exe",
"EpicGamesLauncher.exe",
"IGOProxy.exe",
"IGOProxy64.exe",
"Origin.exe",
"OriginThinSetupInternal.exe",
"steam",
"steamwebhelper",
"gldriverquery",
"vulkandriverquery",
"Steam.exe",
"ffxivlauncher.exe",
"ffxivlauncher64.exe",
};
std::string proc_name = get_proc_name();
bool blacklisted = std::find(blacklist.begin(), blacklist.end(), proc_name) != blacklist.end();
#ifndef NDEBUG
fprintf(stderr, "MANGOHUD: process %s is blacklisted: %d\n", proc_name.c_str(), blacklisted);
#endif
return blacklisted;
}
bool& is_blacklisted() {
static bool blacklisted = check_blacklisted();
return blacklisted;
}

@ -0,0 +1,3 @@
#pragma once
bool& is_blacklisted();

@ -26,9 +26,8 @@ void parseConfigLine(std::string line, std::unordered_map<std::string,std::strin
options[param] = value;
}
void parseConfigFile(overlay_params& params) {
params.options.clear();
std::vector<std::string> paths;
void enumerate_config_files(std::vector<std::string>& paths)
{
static const char *mangohud_dir = "/MangoHud/";
std::string env_data = get_data_dir();
@ -50,29 +49,23 @@ void parseConfigFile(overlay_params& params) {
// find executable's path when run in Wine
if (!env_config.empty() && (basename == "wine-preloader" || basename == "wine64-preloader")) {
std::string line;
std::ifstream stream("/proc/self/cmdline");
while (std::getline(stream, line, '\0'))
{
if (!line.empty()
&& ((n = line.find_last_of("/\\")) != std::string::npos)
&& n < line.size() - 1) // have at least one character
{
auto dot = line.find_last_of('.');
if (dot < n)
dot = line.size();
paths.push_back(env_config + mangohud_dir + "wine-" + line.substr(n + 1, dot - n - 1) + ".conf");
break;
}
else if (ends_with(line, ".exe", true))
{
auto dot = line.find_last_of('.');
paths.push_back(env_config + mangohud_dir + "wine-" + line.substr(0, dot) + ".conf");
break;
}
std::string name;
if (get_wine_exe_name(name)) {
paths.push_back(env_config + mangohud_dir + "wine-" + name + ".conf");
}
}
}
}
void parseConfigFile(overlay_params& params) {
params.options.clear();
std::vector<std::string> paths;
const char *cfg_file = getenv("MANGOHUD_CONFIGFILE");
if (cfg_file)
paths.push_back(cfg_file);
else
enumerate_config_files(paths);
std::string line;
for (auto p = paths.rbegin(); p != paths.rend(); p++) {

@ -26,7 +26,6 @@
#endif
#include "file_utils.h"
pthread_t cpuTempThread;
void calculateCPUData(CPUData& cpuData,
unsigned long long int usertime,

@ -0,0 +1,755 @@
#include <cstdio>
#include <iostream>
#include <sstream>
#include <array>
#include "dbus_info.h"
#include "string_utils.h"
using ms = std::chrono::milliseconds;
struct metadata spotify;
struct metadata generic_mpris;
typedef std::vector<std::pair<std::string, std::string>> string_pair_vec;
typedef std::unordered_map<std::string, string_pair_vec> string_pair_vec_map;
typedef std::unordered_map<std::string, std::string> string_map;
std::string format_signal(const DBusSignal& s)
{
std::stringstream ss;
ss << "type='signal',interface='" << s.intf << "'";
ss << ",member='" << s.signal << "'";
return ss.str();
}
static bool check_msg_arg(libdbus_loader& dbus, DBusMessageIter *iter, int type)
{
int curr_type = DBUS_TYPE_INVALID;
if ((curr_type = dbus.message_iter_get_arg_type (iter)) != type) {
#ifndef NDEBUG
std::cerr << "Argument is not of type '" << (char)type << "' != '" << (char) curr_type << "'" << std::endl;
#endif
return false;
}
return true;
}
bool get_string_array(libdbus_loader& dbus, DBusMessageIter *iter_, std::vector<std::string>& entries)
{
DBusMessageIter iter = *iter_;
DBusMessageIter subiter;
int current_type = DBUS_TYPE_INVALID;
current_type = dbus.message_iter_get_arg_type (&iter);
if (current_type == DBUS_TYPE_VARIANT) {
dbus.message_iter_recurse (&iter, &iter);
current_type = dbus.message_iter_get_arg_type (&iter);
}
if (current_type != DBUS_TYPE_ARRAY) {
#ifndef NDEBUG
std::cerr << "Not an array: '" << (char)current_type << "'" << std::endl;
#endif
return false;
}
char *val = nullptr;
dbus.message_iter_recurse (&iter, &subiter);
entries.clear();
while ((current_type = dbus.message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID) {
if (current_type == DBUS_TYPE_STRING)
{
dbus.message_iter_get_basic (&subiter, &val);
entries.push_back(val);
}
dbus.message_iter_next (&subiter);
}
return true;
}
static bool get_variant_string(libdbus_loader& dbus, DBusMessageIter *iter_, std::string &val, bool key_or_value = false)
{
DBusMessageIter iter = *iter_;
char *str = nullptr;
int type = dbus.message_iter_get_arg_type (&iter);
if (type != DBUS_TYPE_VARIANT && type != DBUS_TYPE_DICT_ENTRY)
return false;
dbus.message_iter_recurse (&iter, &iter);
if (key_or_value) {
dbus.message_iter_next (&iter);
if (!check_msg_arg (dbus, &iter, DBUS_TYPE_VARIANT))
return false;
dbus.message_iter_recurse (&iter, &iter);
}
if (!check_msg_arg (dbus, &iter, DBUS_TYPE_STRING))
return false;
dbus.message_iter_get_basic(&iter, &str);
val = str;
return true;
}
static bool get_variant_string(libdbus_loader& dbus, DBusMessage *msg, std::string &val, bool key_or_value = false)
{
DBusMessageIter iter;
dbus.message_iter_init (msg, &iter);
return get_variant_string(dbus, &iter, val, key_or_value);
}
static void parse_mpris_metadata(libdbus_loader& dbus, DBusMessageIter *iter_, string_pair_vec& entries)
{
DBusMessageIter subiter, iter = *iter_;
std::string key, val;
std::vector<std::string> list;
while (dbus.message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
{
//std::cerr << "\ttype: " << (char)dbus.message_iter_get_arg_type(&iter) << std::endl;
if (!get_variant_string(dbus, &iter, key))
return;
dbus.message_iter_recurse (&iter, &subiter);
dbus.message_iter_next (&subiter);
//std::cerr << "\tkey: " << key << std::endl;
if (get_variant_string(dbus, &subiter, val)) {
//std::cerr << "\t\t" << val << std::endl;
entries.push_back({key, val});
}
else if (get_string_array(dbus, &subiter, list)) {
for (auto& s : list) {
//std::cerr << "\t\t" << s << std::endl;
entries.push_back({key, s});
}
}
dbus.message_iter_next (&iter);
}
}
static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage *msg, std::string& source, string_pair_vec_map& entries_map)
{
const char *val_char = nullptr;
DBusMessageIter iter;
std::string key, val;
std::vector<DBusMessageIter> stack;
stack.push_back({});
dbus.message_iter_init (msg, &stack.back());
// Should be 'org.mpris.MediaPlayer2.Player'
if (!check_msg_arg(dbus, &stack.back(), DBUS_TYPE_STRING))
return;
dbus.message_iter_get_basic(&stack.back(), &val_char);
source = val_char;
if (source != "org.mpris.MediaPlayer2.Player")
return;
dbus.message_iter_next (&stack.back());
//std::cerr << "type: " << (char)dbus.message_iter_get_arg_type(&stack.back()) << std::endl;
if (!check_msg_arg(dbus, &stack.back(), DBUS_TYPE_ARRAY))
return;
dbus.message_iter_recurse (&stack.back(), &iter);
stack.push_back(iter);
while (dbus.message_iter_get_arg_type(&stack.back()) != DBUS_TYPE_INVALID)
{
if (!get_variant_string(dbus, &stack.back(), key)) {
dbus.message_iter_next (&stack.back());
continue;
}
if (key == "Metadata") {
#ifndef NDEBUG
std::cerr << __func__ << ": Found Metadata!" << std::endl;
#endif
// dive into Metadata
dbus.message_iter_recurse (&stack.back(), &iter);
// get the array of entries
dbus.message_iter_next (&iter);
if (!check_msg_arg(dbus, &iter, DBUS_TYPE_VARIANT))
continue;
dbus.message_iter_recurse (&iter, &iter);
if (!check_msg_arg(dbus, &iter, DBUS_TYPE_ARRAY))
continue;
dbus.message_iter_recurse (&iter, &iter);
parse_mpris_metadata(dbus, &iter, entries_map["Metadata"]);
}
else if (key == "PlaybackStatus") {
dbus.message_iter_recurse (&stack.back(), &iter);
dbus.message_iter_next (&iter);
if (get_variant_string(dbus, &iter, val))
entries_map["PlaybackStatus"].push_back({key, val});
}
dbus.message_iter_next (&stack.back());
}
}
static void parse_property_changed(libdbus_loader& dbus, DBusMessage *msg, std::string& source, string_pair_vec& entries)
{
char *name = nullptr;
int i;
uint64_t u64;
double d;
std::vector<DBusMessageIter> stack;
stack.push_back({});
#ifndef NDEBUG
std::vector<char> padding;
padding.push_back('\0');
#endif
dbus.message_iter_init (msg, &stack.back());
int type, prev_type = 0;
type = dbus.message_iter_get_arg_type (&stack.back());
if (type != DBUS_TYPE_STRING) {
#ifndef NDEBUG
std::cerr << __func__ << "First element is not a string" << std::endl;
#endif
return;
}
dbus.message_iter_get_basic(&stack.back(), &name);
source = name;
#ifndef NDEBUG
std::cout << name << std::endl;
#endif
std::pair<std::string, std::string> kv;
dbus.message_iter_next (&stack.back());
// the loop should be able parse the whole message if used for generic use-cases
while ((type = dbus.message_iter_get_arg_type (&stack.back())) != DBUS_TYPE_INVALID) {
#ifndef NDEBUG
padding.back() = ' ';
padding.resize(stack.size() + 1, ' ');
padding.back() = '\0';
std::cout << padding.data() << "Type: " << (char)type;
#endif
if (type == DBUS_TYPE_STRING) {
dbus.message_iter_get_basic(&stack.back(), &name);
#ifndef NDEBUG
std::cout << "=" << name << std::endl;
#endif
if (prev_type == DBUS_TYPE_DICT_ENTRY) // is key ?
kv.first = name;
if (prev_type == DBUS_TYPE_VARIANT || prev_type == DBUS_TYPE_ARRAY) { // is value ?
kv.second = name;
entries.push_back(kv);
}
}
else if (type == DBUS_TYPE_INT32) {
dbus.message_iter_get_basic(&stack.back(), &i);
#ifndef NDEBUG
std::cout << "=" << i << std::endl;
#endif
}
else if (type == DBUS_TYPE_UINT64) {
dbus.message_iter_get_basic(&stack.back(), &u64);
#ifndef NDEBUG
std::cout << "=" << u64 << std::endl;
#endif
}
else if (type == DBUS_TYPE_DOUBLE) {
dbus.message_iter_get_basic(&stack.back(), &d);
#ifndef NDEBUG
std::cout << "=" << d << std::endl;
#endif
}
else if (type == DBUS_TYPE_ARRAY || type == DBUS_TYPE_DICT_ENTRY || type == DBUS_TYPE_VARIANT) {
#ifndef NDEBUG
std::cout << std::endl;
#endif
prev_type = type;
DBusMessageIter iter;
dbus.message_iter_recurse (&stack.back(), &iter);
if (dbus.message_iter_get_arg_type (&stack.back()) != DBUS_TYPE_INVALID)
stack.push_back(iter);
continue;
} else {
#ifndef NDEBUG
std::cout << std::endl;
#endif
}
while(FALSE == dbus.message_iter_next (&stack.back()) && stack.size() > 1) {
stack.pop_back();
prev_type = 0;
}
}
}
bool get_dict_string_array(libdbus_loader& dbus, DBusMessage *msg, string_pair_vec& entries)
{
DBusMessageIter iter, outer_iter;
dbus.message_iter_init (msg, &outer_iter);
int current_type = DBUS_TYPE_INVALID;
current_type = dbus.message_iter_get_arg_type (&outer_iter);
if (current_type == DBUS_TYPE_VARIANT) {
dbus.message_iter_recurse (&outer_iter, &outer_iter);
current_type = dbus.message_iter_get_arg_type (&outer_iter);
}
if (current_type != DBUS_TYPE_ARRAY) {
#ifndef NDEBUG
std::cerr << "Not an array " << (char)current_type << std::endl;
#endif
return false;
}
char *val_key = nullptr, *val_value = nullptr;
dbus.message_iter_recurse (&outer_iter, &outer_iter);
while ((current_type = dbus.message_iter_get_arg_type (&outer_iter)) != DBUS_TYPE_INVALID) {
// printf("type: %d\n", current_type);
if (current_type == DBUS_TYPE_DICT_ENTRY)
{
dbus.message_iter_recurse (&outer_iter, &iter);
// dict entry key
//printf("\tentry: {%c, ", dbus.message_iter_get_arg_type (&iter));
dbus.message_iter_get_basic (&iter, &val_key);
std::string key = val_key;
// dict entry value
dbus.message_iter_next (&iter);
if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_VARIANT)
dbus.message_iter_recurse (&iter, &iter);
if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_ARRAY) {
dbus.message_iter_recurse (&iter, &iter);
if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) {
//printf("%c}\n", dbus.message_iter_get_arg_type (&iter));
dbus.message_iter_get_basic (&iter, &val_value);
entries.push_back({val_key, val_value});
}
}
else if (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) {
//printf("%c}\n", dbus.message_iter_get_arg_type (&iter));
dbus.message_iter_get_basic (&iter, &val_value);
entries.push_back({val_key, val_value});
}
}
dbus.message_iter_next (&outer_iter);
}
return true;
}
static void assign_metadata(metadata& meta, string_pair_vec_map& entries_map)
{
string_pair_vec_map::const_iterator it;
it = entries_map.find("Metadata");
if (it != entries_map.end()) {
meta.title.clear();
meta.artists.clear();
meta.album.clear();
std::lock_guard<std::mutex> lk(meta.mutex);
std::vector<std::string> artists;
meta.valid = false;
for (auto& kv : it->second) {
#ifndef NDEBUG
std::cerr << kv.first << " = " << kv.second << std::endl;
#endif
if (kv.first == "xesam:artist")
artists.push_back(kv.second);
else if (kv.first == "xesam:title")
meta.title = kv.second;
else if (kv.first == "xesam:album")
meta.album = kv.second;
else if (kv.first == "mpris:artUrl")
meta.artUrl = kv.second;
else if (kv.first == "PlaybackStatus")
meta.playing = (kv.second == "Playing");
}
// XXX Spotify only sends one artist anyway
for (auto p = artists.begin(); p != artists.end(); p++) {
meta.artists += *p;
if (p != artists.end() - 1)
meta.artists += ", ";
}
}
it = entries_map.find("PlaybackStatus");
if (it != entries_map.end()) {
for (auto& kv : it->second) {
if (kv.first == "PlaybackStatus")
meta.playing = (kv.second == "Playing");
}
}
if (meta.artists.size() || !meta.title.empty())
meta.valid = meta.playing;
meta.ticker.needs_recalc = true;
meta.ticker.pos = 0;
meta.ticker.longest = 0;
meta.ticker.dir = -1;
}
bool dbus_get_name_owner(dbusmgr::dbus_manager& dbus_mgr, std::string& name_owner, const char *name)
{
auto& dbus = dbus_mgr.dbus();
DBusError error;
dbus.error_init(&error);
DBusMessage * dbus_reply = nullptr;
DBusMessage * dbus_msg = nullptr;
// dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:"org.mpris.MediaPlayer2.spotify"
if (nullptr == (dbus_msg = dbus.message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "GetNameOwner"))) {
throw std::runtime_error("unable to allocate memory for dbus message");
}
if (!dbus.message_append_args (dbus_msg, DBUS_TYPE_STRING, &name, DBUS_TYPE_INVALID)) {
dbus.message_unref(dbus_msg);
std::cerr << "MANGOHUD: " << __func__ << ": dbus_message_append_args failed\n";
dbus.error_free(&error);
return false;
}
if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT_USE_DEFAULT, &error))) {
dbus.message_unref(dbus_msg);
std::cerr << "MANGOHUD: " << __func__ << ": "<< error.message << "\n";
dbus.error_free(&error);
return false;
}
const char* val = nullptr;
DBusMessageIter iter;
dbus.message_iter_init (dbus_reply, &iter);
if (dbus.message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return false;
dbus.message_iter_get_basic(&iter, &val);
if (val)
name_owner = val;
dbus.message_unref(dbus_msg);
dbus.message_unref(dbus_reply);
dbus.error_free(&error);
return true;
}
bool dbus_list_name_to_owner(dbusmgr::dbus_manager& dbus_mgr, string_map& name_owners)
{
auto& dbus = dbus_mgr.dbus();
DBusError error;
dbus.error_init(&error);
std::vector<std::string> names;
std::string owner;
DBusMessageIter iter;
DBusMessage * dbus_reply = nullptr;
DBusMessage * dbus_msg = nullptr;
// dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:"org.mpris.MediaPlayer2.spotify"
if (nullptr == (dbus_msg = dbus.message_new_method_call("org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", "ListNames"))) {
throw std::runtime_error("unable to allocate memory for dbus message");
}
if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT_USE_DEFAULT, &error))) {
dbus.message_unref(dbus_msg);
std::cerr << "MANGOHUD: " << __func__ << ": "<< error.message << "\n";
dbus.error_free(&error);
return false;
}
dbus.message_iter_init (dbus_reply, &iter);
if (!get_string_array(dbus, &iter, names))
return false;
for (auto& name : names) {
if (!starts_with(name, "org.mpris.MediaPlayer2."))
continue;
if (dbus_get_name_owner(dbus_mgr, owner, name.c_str())) {
name_owners[name] = owner;
}
}
dbus.message_unref(dbus_msg);
dbus.message_unref(dbus_reply);
dbus.error_free(&error);
return true;
}
void dbus_get_player_property(dbusmgr::dbus_manager& dbus_mgr, string_pair_vec& entries, const char * dest, const char * prop)
{
auto& dbus = dbus_mgr.dbus();
DBusError error;
dbus.error_init(&error);
DBusMessage * dbus_reply = nullptr;
DBusMessage * dbus_msg = nullptr;
// dbus-send --print-reply --session --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 org.freedesktop.DBus.Properties.Get string:'org.mpris.MediaPlayer2.Player' string:'Metadata'
if (nullptr == (dbus_msg = dbus.message_new_method_call(dest, "/org/mpris/MediaPlayer2", "org.freedesktop.DBus.Properties", "Get"))) {
throw std::runtime_error("unable to allocate memory for dbus message");
}
const char *v_STRINGS[] = {
"org.mpris.MediaPlayer2.Player",
};
if (!dbus.message_append_args (dbus_msg, DBUS_TYPE_STRING, &v_STRINGS[0], DBUS_TYPE_STRING, &prop, DBUS_TYPE_INVALID)) {
dbus.message_unref(dbus_msg);
throw std::runtime_error("dbus_message_append_args failed");
}
if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT_USE_DEFAULT, &error))) {
dbus.message_unref(dbus_msg);
throw dbusmgr::dbus_error(dbus, &error);
}
std::string entry;
if (get_dict_string_array(dbus, dbus_reply, entries)) {
// nothing
} else if (get_variant_string(dbus, dbus_reply, entry)) {
entries.push_back({prop, entry});
}
dbus.message_unref(dbus_msg);
dbus.message_unref(dbus_reply);
dbus.error_free(&error);
}
void get_spotify_metadata(dbusmgr::dbus_manager& dbus, metadata& meta)
{
meta.artists.clear();
string_pair_vec_map entries;
dbus_get_player_property(dbus, entries["Metadata"], "org.mpris.MediaPlayer2.spotify", "Metadata");
dbus_get_player_property(dbus, entries["PlaybackStatus"], "org.mpris.MediaPlayer2.spotify", "PlaybackStatus");
assign_metadata(meta, entries);
}
namespace dbusmgr {
void dbus_manager::init()
{
if (m_inited)
return;
if (!m_dbus_ldr.IsLoaded() && !m_dbus_ldr.Load("libdbus-1.so.3"))
throw std::runtime_error("Could not load libdbus-1.so.3");
m_dbus_ldr.error_init(&m_error);
m_dbus_ldr.threads_init_default();
if ( nullptr == (m_dbus_conn = m_dbus_ldr.bus_get(DBUS_BUS_SESSION, &m_error)) ) {
throw dbus_error(m_dbus_ldr, &m_error);
}
std::cout << "Connected to D-Bus as \"" << m_dbus_ldr.bus_get_unique_name(m_dbus_conn) << "\"." << std::endl;
dbus_list_name_to_owner(*this, m_name_owners);
connect_to_signals();
m_inited = true;
}
void dbus_manager::deinit()
{
if (!m_inited)
return;
// unreference system bus connection instead of closing it
if (m_dbus_conn) {
disconnect_from_signals();
m_dbus_ldr.connection_unref(m_dbus_conn);
m_dbus_conn = nullptr;
}
m_dbus_ldr.error_free(&m_error);
m_inited = false;
}
dbus_manager::~dbus_manager()
{
deinit();
}
void dbus_manager::connect_to_signals()
{
for (auto kv : m_signals) {
auto signal = format_signal(kv);
m_dbus_ldr.bus_add_match(m_dbus_conn, signal.c_str(), &m_error);
if (m_dbus_ldr.error_is_set(&m_error)) {
::perror(m_error.name);
::perror(m_error.message);
m_dbus_ldr.error_free(&m_error);
//return;
}
}
start_thread();
}
void dbus_manager::disconnect_from_signals()
{
for (auto kv : m_signals) {
auto signal = format_signal(kv);
m_dbus_ldr.bus_remove_match(m_dbus_conn, signal.c_str(), &m_error);
if (m_dbus_ldr.error_is_set(&m_error)) {
::perror(m_error.name);
::perror(m_error.message);
m_dbus_ldr.error_free(&m_error);
}
}
stop_thread();
}
void dbus_manager::add_callback(CBENUM type, callback_func func)
{
m_callbacks[type] = func;
}
void dbus_manager::stop_thread()
{
m_quit = true;
if (m_thread.joinable())
m_thread.join();
}
void dbus_manager::start_thread()
{
stop_thread();
m_quit = false;
m_thread = std::thread(dbus_thread, this);
}
void dbus_manager::dbus_thread(dbus_manager *pmgr)
{
(void)parse_property_changed;
DBusMessage *msg = nullptr;
auto& dbus = pmgr->dbus();
// loop listening for signals being emmitted
while (!pmgr->m_quit) {
// non blocking read of the next available message
if (!dbus.connection_read_write(pmgr->m_dbus_conn, 0))
return; // connection closed
msg = dbus.connection_pop_message(pmgr->m_dbus_conn);
// loop again if we haven't read a message
if (nullptr == msg) {
std::this_thread::sleep_for(ms(10));
continue;
}
for (auto& sig : pmgr->m_signals) {
if (dbus.message_is_signal(msg, sig.intf, sig.signal))
{
const char *sender = dbus.message_get_sender(msg);
#ifndef NDEBUG
std::cerr << __func__ << ": " << sig.intf << "::" << sig.signal << "\n";
std::cerr << "Sender: " << sender << "\n";
#endif
switch (sig.type) {
case ST_PROPERTIESCHANGED:
{
std::string source;
string_pair_vec_map entries_map;
//parse_property_changed(msg, source, entries);
parse_mpris_properties(dbus, msg, source, entries_map);
#ifndef NDEBUG
std::cerr << "Source: " << source << "\n";
#endif
if (source != "org.mpris.MediaPlayer2.Player")
break;
if (pmgr->m_name_owners["org.mpris.MediaPlayer2.spotify"] == sender) {
assign_metadata(spotify, entries_map);
} else {
assign_metadata(generic_mpris, entries_map);
if (generic_mpris.playing && !generic_mpris.valid) {
dbus_get_player_property(*pmgr, entries_map["Metadata"], sender, "Metadata");
assign_metadata(generic_mpris, entries_map);
}
}
}
break;
case ST_NAMEOWNERCHANGED:
{
DBusMessageIter iter;
dbus.message_iter_init (msg, &iter);
std::vector<std::string> str;
const char *value = nullptr;
while (dbus.message_iter_get_arg_type (&iter) == DBUS_TYPE_STRING) {
dbus.message_iter_get_basic (&iter, &value);
str.push_back(value);
dbus.message_iter_next (&iter);
}
// register new name
if (str.size() == 3
&& starts_with(str[0], "org.mpris.MediaPlayer2.")
&& !str[2].empty()
)
{
pmgr->m_name_owners[str[0]] = str[2];
}
// did a player quit?
if (str[2].empty()) {
if (str.size() == 3
&& str[0] == "org.mpris.MediaPlayer2.spotify"
) {
spotify.valid = false;
} else {
auto it = pmgr->m_name_owners.find(str[0]);
if (it != pmgr->m_name_owners.end()
&& it->second == str[1]) {
generic_mpris.artists.clear();
generic_mpris.title.clear();
generic_mpris.album.clear();
generic_mpris.valid = false;
}
}
}
}
break;
default:
break;
}
}
}
// free the message
dbus.message_unref(msg);
}
}
dbus_manager dbus_mgr;
}

@ -0,0 +1,125 @@
#pragma once
#include <array>
#include <stdexcept>
#include <thread>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <unordered_map>
#include <mutex>
#include "loaders/loader_dbus.h"
struct metadata {
//std::vector<std::string> artists;
std::string artists; // pre-concatenate
std::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;
};
enum SignalType
{
ST_NAMEOWNERCHANGED,
ST_PROPERTIESCHANGED,
};
struct DBusSignal
{
const char * intf;
const char * signal;
SignalType type;
};
extern struct metadata spotify;
extern struct metadata generic_mpris;
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();
void init();
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;
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;
}
void get_spotify_metadata(dbusmgr::dbus_manager& dbus, metadata& meta);

@ -19,6 +19,11 @@
#include <fnmatch.h>
#include "elfhacks.h"
#ifndef __ELF_NATIVE_CLASS
#include "sys/reg.h"
#define __ELF_NATIVE_CLASS __WORDSIZE
#endif
/**
* \addtogroup elfhacks
* \{

@ -27,6 +27,7 @@ bool find_folder(const char* root, const char* prefix, std::string& dest)
return false;
}
// XXX xfs/jfs need stat() for inode type
while ((dp = readdir(dirp))) {
if ((dp->d_type == DT_LNK || dp->d_type == DT_DIR) && starts_with(dp->d_name, prefix)) {
dest = dp->d_name;
@ -99,13 +100,45 @@ bool dir_exists(const std::string& path)
return !stat(path.c_str(), &s) && S_ISDIR(s.st_mode);
}
std::string get_exe_path()
std::string readlink(const char * link)
{
char result[PATH_MAX] {};
ssize_t count = readlink("/proc/self/exe", result, PATH_MAX);
ssize_t count = readlink(link, result, PATH_MAX);
return std::string(result, (count > 0) ? count : 0);
}
std::string get_exe_path()
{
return readlink("/proc/self/exe");
}
bool get_wine_exe_name(std::string& name, bool keep_ext)
{
std::string line;
std::ifstream cmdline("/proc/self/cmdline");
auto n = std::string::npos;
while (std::getline(cmdline, line, '\0'))
{
if (!line.empty()
&& ((n = line.find_last_of("/\\")) != std::string::npos)
&& n < line.size() - 1) // have at least one character
{
auto dot = keep_ext ? std::string::npos : line.find_last_of('.');
if (dot < n)
dot = line.size();
name = line.substr(n + 1, dot - n - 1);
return true;
}
else if (ends_with(line, ".exe", true))
{
auto dot = keep_ext ? std::string::npos : line.find_last_of('.');
name = line.substr(0, dot);
return true;
}
}
return false;
}
std::string get_home_dir()
{
std::string path;

@ -15,7 +15,9 @@ 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 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();

@ -11,11 +11,12 @@ void glXSwapIntervalEXT(void*, void*, int);
int glXSwapIntervalSGI(int);
int glXSwapIntervalMESA(unsigned int);
int glXGetSwapIntervalMESA(void);
bool glXMakeCurrent(void*, void*, void*);
int glXMakeCurrent(void*, void*, void*);
void* glXGetCurrentContext();
void* glXGetProcAddress(const unsigned char*);
void* glXGetProcAddressARB(const unsigned char*);
int glXQueryDrawable(void *dpy, void* glxdraw, int attr, unsigned int * value);
unsigned int eglSwapBuffers( void*, void* );

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,207 @@
#include <cstdlib>
#include <functional>
#include <thread>
#include <string>
#include <iostream>
#include <memory>
#include <imgui.h>
#include "font_default.h"
#include "cpu.h"
#include "file_utils.h"
#include "imgui_hud.h"
#include "notify.h"
#ifdef HAVE_DBUS
#include "dbus_info.h"
#endif
#include <glad/glad.h>
namespace MangoHud { namespace GL {
struct GLVec
{
GLint v[4];
GLint operator[] (size_t i)
{
return v[i];
}
bool operator== (const GLVec& r)
{
return v[0] == r.v[0]
&& v[1] == r.v[1]
&& v[2] == r.v[2]
&& v[3] == r.v[3];
}
bool operator!= (const GLVec& r)
{
return !(*this == r);
}
};
struct state {
ImGuiContext *imgui_ctx = nullptr;
ImFont* font = nullptr;
ImFont* font1 = nullptr;
};
static GLVec last_vp {}, last_sb {};
static swapchain_stats sw_stats {};
static state state;
static uint32_t vendorID;
static std::string deviceName;
static notify_thread notifier;
static bool cfg_inited = false;
static ImVec2 window_size;
static bool inited = false;
overlay_params params {};
// seems to quit by itself though
static std::unique_ptr<notify_thread, std::function<void(notify_thread *)>>
stop_it(&notifier, [](notify_thread *n){ stop_notifier(*n); });
void imgui_init()
{
if (cfg_inited)
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
notifier.params = &params;
start_notifier(notifier);
window_size = ImVec2(params.width, params.height);
init_system_info();
cfg_inited = true;
init_cpu_stats(params);
}
//static
void imgui_create(void *ctx)
{
if (inited)
return;
inited = true;
if (!ctx)
return;
imgui_init();
gladLoadGL();
GetOpenGLVersion(sw_stats.version_gl.major,
sw_stats.version_gl.minor,
sw_stats.version_gl.is_gles);
deviceName = (char*)glGetString(GL_RENDERER);
sw_stats.deviceName = deviceName;
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
vendorID = 0x1002;
} else {
vendorID = 0x10de;
}
init_gpu_stats(vendorID, params);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
state.imgui_ctx = ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
imgui_custom_style(params);
glGetIntegerv (GL_VIEWPORT, last_vp.v);
glGetIntegerv (GL_SCISSOR_BOX, last_sb.v);
ImGui::GetIO().IniFilename = NULL;
ImGui::GetIO().DisplaySize = ImVec2(last_vp[2], last_vp[3]);
ImGui_ImplOpenGL3_Init();
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
int font_size = params.font_size;
if (!font_size)
font_size = 24;
if (!params.font_file.empty() && file_exists(params.font_file)) {
state.font = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size);
state.font1 = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f);
} else {
ImFontConfig font_cfg = ImFontConfig();
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const ImWchar* glyph_ranges = io.Fonts->GetGlyphRangesDefault();
state.font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, &font_cfg, glyph_ranges);
state.font1 = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55, &font_cfg, glyph_ranges);
}
sw_stats.font1 = state.font1;
// Restore global context or ours might clash with apps that use Dear ImGui
ImGui::SetCurrentContext(saved_ctx);
}
void imgui_shutdown()
{
#ifndef NDEBUG
std::cerr << __func__ << std::endl;
#endif
if (state.imgui_ctx) {
ImGui::SetCurrentContext(state.imgui_ctx);
ImGui_ImplOpenGL3_Shutdown();
ImGui::DestroyContext(state.imgui_ctx);
state.imgui_ctx = nullptr;
}
inited = false;
}
void imgui_set_context(void *ctx)
{
if (!ctx) {
imgui_shutdown();
return;
}
#ifndef NDEBUG
std::cerr << __func__ << ": " << ctx << std::endl;
#endif
imgui_create(ctx);
}
void imgui_render(unsigned int width, unsigned int height)
{
if (!state.imgui_ctx)
return;
check_keybinds(params);
update_hud_info(sw_stats, params, vendorID);
ImGuiContext *saved_ctx = ImGui::GetCurrentContext();
ImGui::SetCurrentContext(state.imgui_ctx);
ImGui::GetIO().DisplaySize = ImVec2(width, height);
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
{
std::lock_guard<std::mutex> lk(notifier.mutex);
position_layer(params, window_size);
render_imgui(sw_stats, params, window_size, false);
}
ImGui::PopStyleVar(3);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
ImGui::SetCurrentContext(saved_ctx);
}
}} // namespaces

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

@ -13,6 +13,7 @@
// CHANGELOG
// (minor and older changes stripped away, please see git history for details)
// 2020-04-12: OpenGL: Fixed context version check mistakenly testing for 4.0+ instead of 3.2+ to enable ImGuiBackendFlags_RendererHasVtxOffset.
// 2020-01-07: OpenGL: Added support for glbindings OpenGL loader.
// 2019-10-25: OpenGL: Using a combination of GL define and runtime GL version to decide whether to use glDrawElementsBaseVertex(). Fix building with pre-3.2 GL loaders.
// 2019-09-22: OpenGL: Detect default GL loader using __has_include compiler facility.
@ -63,19 +64,14 @@
// ES 3.0 300 "#version 300 es" = WebGL 2.0
//----------------------------------------
#include "imgui.h"
#include <imgui.h>
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <stdint.h> // intptr_t
#include <GL/gl3w.h>
#define GL_CLIP_ORIGIN 0x935C
#define GL_NEGATIVE_ONE_TO_ONE 0x935E
#define GL_ZERO_TO_ONE 0x935F
#define GL_CLIP_DEPTH_MODE 0x935D
#include <glad/glad.h>
void* get_glx_proc_address(const char* name);
void (*glClipControl)(int origin, int depth);
namespace MangoHud {
// Desktop GL 3.2+ has glDrawElementsBaseVertex() which GL ES and WebGL don't have.
#if defined(IMGUI_IMPL_OPENGL_ES2) || defined(IMGUI_IMPL_OPENGL_ES3) || !defined(GL_VERSION_3_2)
@ -92,271 +88,16 @@ static GLuint g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0;
static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; // Uniforms location
static int g_AttribLocationVtxPos = 0, g_AttribLocationVtxUV = 0, g_AttribLocationVtxColor = 0; // Vertex attributes location
static unsigned int g_VboHandle = 0, g_ElementsHandle = 0;
static bool g_IsGLES = false;
// Functions
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
glClipControl = reinterpret_cast<decltype(glClipControl)> (get_glx_proc_address("glClipControl"));
// Query for GL version
#if !defined(IMGUI_IMPL_OPENGL_ES2)
glsl_version = "#version 130";
GLint major = -1, minor = -1;
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
g_GlVersion = major * 1000 + minor;
printf("Version: %d.%d\n", major, minor);
if (major >= 4 && minor >= 1)
glsl_version = "#version 410";
else if (major > 3 || (major == 3 && minor >= 2))
glsl_version = "#version 150";
#else
g_GlVersion = 2000; // GLES 2
#endif
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 3200)
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
#endif
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (glsl_version == NULL)
glsl_version = "#version 130";
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
#endif
(void)vertex_array_object;
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(vertex_array_object);
#endif
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
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)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
#ifdef GL_SAMPLER_BINDING
GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
#endif
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array_object; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
#endif
#ifdef GL_POLYGON_MODE
GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
#endif
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
bool clip_origin_lower_left = true;
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
GLenum last_clip_origin = 0; glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
GLenum last_clip_depth_mode = 0; glGetIntegerv(GL_CLIP_DEPTH_MODE, (GLint*)&last_clip_depth_mode);
if (last_clip_origin == GL_UPPER_LEFT) {
clip_origin_lower_left = false;
if (glClipControl)
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
}
#endif
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
#ifndef IMGUI_IMPL_OPENGL_ES2
glGenVertexArrays(1, &vertex_array_object);
#endif
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
//if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
//else
// glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if (g_GlVersion >= 3200)
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
#endif
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
#ifndef IMGUI_IMPL_OPENGL_ES2
glDeleteVertexArrays(1, &vertex_array_object);
#endif
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
#ifdef GL_SAMPLER_BINDING
glBindSampler(0, last_sampler);
#endif
glActiveTexture(last_active_texture);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array_object);
#endif
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
#ifdef GL_POLYGON_MODE
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
#endif
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
#if defined(GL_CLIP_ORIGIN) && !defined(__APPLE__)
if (!clip_origin_lower_left && glClipControl)
glClipControl(last_clip_origin, last_clip_depth_mode);
#endif
}
bool ImGui_ImplOpenGL3_CreateFontsTexture()
static bool ImGui_ImplOpenGL3_CreateFontsTexture()
{
// Build texture atlas
ImGuiIO& io = ImGui::GetIO();
unsigned char* pixels;
int width, height;
io.Fonts->GetTexDataAsAlpha8(&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->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
// Upload texture to graphics system
GLint last_texture;
@ -365,10 +106,11 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
glBindTexture(GL_TEXTURE_2D, g_FontTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#ifdef GL_UNPACK_ROW_LENGTH
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
#endif
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, pixels);
//#ifdef GL_UNPACK_ROW_LENGTH
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);
// Store our identifier
io.Fonts->TexID = (ImTextureID)(intptr_t)g_FontTexture;
@ -379,7 +121,7 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture()
return true;
}
void ImGui_ImplOpenGL3_DestroyFontsTexture()
static void ImGui_ImplOpenGL3_DestroyFontsTexture()
{
if (g_FontTexture)
{
@ -426,16 +168,16 @@ static bool CheckProgram(GLuint handle, const char* desc)
return (GLboolean)status == GL_TRUE;
}
bool ImGui_ImplOpenGL3_CreateDeviceObjects()
static bool ImGui_ImplOpenGL3_CreateDeviceObjects()
{
// Backup GL state
GLint last_texture, last_array_buffer;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
//#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array;
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
#endif
if (g_GlVersion >= 300)
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array);
// Parse GLSL version string
int glsl_version = 130;
@ -508,7 +250,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"varying vec4 Frag_Color;\n"
"void main()\n"
"{\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st).r;\n"
" gl_FragColor = Frag_Color * texture2D(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_130 =
@ -518,7 +260,7 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
"out vec4 Out_Color;\n"
"void main()\n"
"{\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st).r;\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_300_es =
@ -529,7 +271,7 @@ 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).r;\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
const GLchar* fragment_shader_glsl_410_core =
@ -539,7 +281,7 @@ 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).r;\n"
" Out_Color = Frag_Color * texture(Texture, Frag_UV.st);\n"
"}\n";
#ifndef NDEBUG
@ -603,14 +345,14 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects()
// Restore modified GL state
glBindTexture(GL_TEXTURE_2D, last_texture);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
#ifndef IMGUI_IMPL_OPENGL_ES2
glBindVertexArray(last_vertex_array);
#endif
//#ifndef IMGUI_IMPL_OPENGL_ES2
if (g_GlVersion >= 300)
glBindVertexArray(last_vertex_array);
return true;
}
void ImGui_ImplOpenGL3_DestroyDeviceObjects()
static void ImGui_ImplOpenGL3_DestroyDeviceObjects()
{
#ifndef NDEBUG
printf("%s\n", __func__);
@ -625,3 +367,312 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects()
ImGui_ImplOpenGL3_DestroyFontsTexture();
}
void GetOpenGLVersion(int& major, int& minor, bool& isGLES)
{
//glGetIntegerv(GL_MAJOR_VERSION, &major);
//glGetIntegerv(GL_MINOR_VERSION, &minor);
const char* version;
const char* prefixes[] = {
"OpenGL ES-CM ",
"OpenGL ES-CL ",
"OpenGL ES ",
nullptr
};
version = (const char*) glGetString(GL_VERSION);
if (!version)
return;
//if (glGetError() == 0x500) {
for (int i = 0; prefixes[i]; i++) {
const size_t length = strlen(prefixes[i]);
if (strncmp(version, prefixes[i], length) == 0) {
version += length;
isGLES = true;
break;
}
}
sscanf(version, "%d.%d", &major, &minor);
//}
}
bool ImGui_ImplOpenGL3_Init(const char* glsl_version)
{
GLint major = 0, minor = 0;
GetOpenGLVersion(major, minor, g_IsGLES);
printf("Version: %d.%d %s\n", major, minor, g_IsGLES ? "ES" : "");
if (!g_IsGLES) {
// Not GL ES
glsl_version = "#version 130";
g_GlVersion = major * 100 + minor * 10;
if (major >= 4 && minor >= 1)
glsl_version = "#version 410";
else if (major > 3 || (major == 3 && minor >= 2))
glsl_version = "#version 150";
else if (major < 2)
glsl_version = "#version 100";
} else {
if (major >= 3)
g_GlVersion = major * 100 + minor * 10; // GLES >= 3
else
g_GlVersion = 200; // GLES 2
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (g_GlVersion == 200)
glsl_version = "#version 100";
else if (g_GlVersion >= 300)
glsl_version = "#version 300 es";
else
glsl_version = "#version 130";
}
// Setup back-end capabilities flags
ImGuiIO& io = ImGui::GetIO();
io.BackendRendererName = "imgui_impl_opengl3";
//#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if ((!g_IsGLES && g_GlVersion >= 320) || (g_IsGLES && g_GlVersion >= 320))
io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
// Store GLSL version string so we can refer to it later in case we recreate shaders.
// Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure.
if (glsl_version == NULL)
glsl_version = "#version 130";
IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersionString));
strcpy(g_GlslVersionString, glsl_version);
strcat(g_GlslVersionString, "\n");
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
return true;
}
void ImGui_ImplOpenGL3_Shutdown()
{
ImGui_ImplOpenGL3_DestroyDeviceObjects();
}
void ImGui_ImplOpenGL3_NewFrame()
{
if (!g_ShaderHandle)
ImGui_ImplOpenGL3_CreateDeviceObjects();
}
static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_width, int fb_height, GLuint vertex_array_object)
{
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill
glEnable(GL_BLEND);
glBlendEquation(GL_FUNC_ADD);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glEnable(GL_SCISSOR_TEST);
//#ifdef GL_POLYGON_MODE
if (!g_IsGLES && g_GlVersion >= 200)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
// Setup viewport, orthographic projection matrix
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps.
glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height);
float L = draw_data->DisplayPos.x;
float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x;
float T = draw_data->DisplayPos.y;
float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y;
const float ortho_projection[4][4] =
{
{ 2.0f/(R-L), 0.0f, 0.0f, 0.0f },
{ 0.0f, 2.0f/(T-B), 0.0f, 0.0f },
{ 0.0f, 0.0f, -1.0f, 0.0f },
{ (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f },
};
glUseProgram(g_ShaderHandle);
glUniform1i(g_AttribLocationTex, 0);
glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]);
if (g_GlVersion >= 330)
glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise.
(void)vertex_array_object;
//#ifndef IMGUI_IMPL_OPENGL_ES2
if (g_GlVersion >= 300)
glBindVertexArray(vertex_array_object);
// Bind vertex/index buffers and setup attributes for ImDrawVert
glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle);
glEnableVertexAttribArray(g_AttribLocationVtxPos);
glEnableVertexAttribArray(g_AttribLocationVtxUV);
glEnableVertexAttribArray(g_AttribLocationVtxColor);
glVertexAttribPointer(g_AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos));
glVertexAttribPointer(g_AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv));
glVertexAttribPointer(g_AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col));
}
// OpenGL3 Render function.
// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop)
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
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)
return;
// Backup GL state
GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture);
glActiveTexture(GL_TEXTURE0);
GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program);
GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture);
// GL_SAMPLER_BINDING
GLint last_sampler;
if (!g_IsGLES && g_GlVersion >= 330)
glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler);
GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer);
//#ifndef IMGUI_IMPL_OPENGL_ES2
GLint last_vertex_array_object;
if (g_GlVersion >= 300)
glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array_object);
GLint last_polygon_mode[2];
if (!g_IsGLES && g_GlVersion >= 200)
glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode);
GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport);
GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box);
GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb);
GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb);
GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha);
GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha);
GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb);
GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha);
GLboolean last_enable_blend = glIsEnabled(GL_BLEND);
GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE);
GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST);
GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST);
bool clip_origin_lower_left = true;
GLenum last_clip_origin = 0;
GLenum last_clip_depth_mode = 0;
if (!g_IsGLES && /*g_GlVersion >= 450*/ (glad_glClipControl || glad_glClipControlEXT)) {
glGetIntegerv(GL_CLIP_ORIGIN, (GLint*)&last_clip_origin); // Support for GL 4.5's glClipControl(GL_UPPER_LEFT)
glGetIntegerv(GL_CLIP_DEPTH_MODE, (GLint*)&last_clip_depth_mode);
if (last_clip_origin == GL_UPPER_LEFT) {
clip_origin_lower_left = false;
glClipControl(GL_LOWER_LEFT, GL_ZERO_TO_ONE);
}
}
// Setup desired GL state
// Recreate the VAO every time (this is to easily allow multiple GL contexts to be rendered to. VAO are not shared among GL contexts)
// The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound.
GLuint vertex_array_object = 0;
if (g_GlVersion >= 300)
glGenVertexArrays(1, &vertex_array_object);
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports
ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2)
// Render command lists
for (int n = 0; n < draw_data->CmdListsCount; n++)
{
const ImDrawList* cmd_list = draw_data->CmdLists[n];
// Upload vertex/index buffers
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW);
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i];
if (pcmd->UserCallback != NULL)
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
if (pcmd->UserCallback == ImDrawCallback_ResetRenderState)
ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object);
else
pcmd->UserCallback(cmd_list, pcmd);
}
else
{
// Project scissor/clipping rectangles into framebuffer space
ImVec4 clip_rect;
clip_rect.x = (pcmd->ClipRect.x - clip_off.x) * clip_scale.x;
clip_rect.y = (pcmd->ClipRect.y - clip_off.y) * clip_scale.y;
clip_rect.z = (pcmd->ClipRect.z - clip_off.x) * clip_scale.x;
clip_rect.w = (pcmd->ClipRect.w - clip_off.y) * clip_scale.y;
if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f)
{
// Apply scissor/clipping rectangle
//if (clip_origin_lower_left)
glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y));
//else
// glScissor((int)clip_rect.x, (int)clip_rect.y, (int)clip_rect.z, (int)clip_rect.w); // Support for GL 4.5 rarely used glClipControl(GL_UPPER_LEFT)
// Bind texture, Draw
glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId);
//#if IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET
if ((!g_IsGLES && g_GlVersion >= 320) || (g_IsGLES && g_GlVersion >= 320))
glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset);
else
glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)));
}
}
}
}
// Destroy the temporary VAO
if (g_GlVersion >= 300)
glDeleteVertexArrays(1, &vertex_array_object);
// Restore modified GL state
glUseProgram(last_program);
glBindTexture(GL_TEXTURE_2D, last_texture);
if (!g_IsGLES && g_GlVersion >= 330)
glBindSampler(0, last_sampler);
glActiveTexture(last_active_texture);
if (g_GlVersion >= 300)
glBindVertexArray(last_vertex_array_object);
glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer);
glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha);
glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha);
if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND);
if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE);
if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST);
if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST);
if (!g_IsGLES && g_GlVersion >= 200)
glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]);
glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]);
glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]);
if (!g_IsGLES && /*g_GlVersion >= 450*/ glad_glClipControl)
if (!clip_origin_lower_left)
glClipControl(last_clip_origin, last_clip_depth_mode);
}
}

@ -23,6 +23,10 @@
#pragma once
namespace MangoHud {
void GetOpenGLVersion(int& major, int& minor, bool& isGLES);
// Backend API
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr);
IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown();
@ -30,7 +34,9 @@ IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data);
// (Optional) Called by Init/NewFrame/Shutdown
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
//IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture();
//IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture();
//IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects();
//IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects();
}

@ -1,417 +0,0 @@
#include <iostream>
#include <array>
#include <unordered_map>
#include <memory>
#include <functional>
#include <cstring>
#include <cstdio>
#include <dlfcn.h>
#include <string>
#include "real_dlsym.h"
#include "loaders/loader_gl.h"
#include "GL/gl3w.h"
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include "font_default.h"
#include "overlay.h"
#include "cpu.h"
#include "mesa/util/macros.h"
#include "mesa/util/os_time.h"
#include "file_utils.h"
#include "notify.h"
#include <chrono>
#include <iomanip>
#define EXPORT_C_(type) extern "C" __attribute__((__visibility__("default"))) type
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName);
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName);
static gl_loader gl;
struct state {
ImGuiContext *imgui_ctx = nullptr;
ImFont* font = nullptr;
ImFont* font1 = nullptr;
};
struct GLVec
{
GLint v[4];
GLint operator[] (size_t i)
{
return v[i];
}
bool operator== (const GLVec& r)
{
return v[0] == r.v[0]
&& v[1] == r.v[1]
&& v[2] == r.v[2]
&& v[3] == r.v[3];
}
bool operator!= (const GLVec& r)
{
return !(*this == r);
}
};
static GLVec last_vp {}, last_sb {};
static ImVec2 window_size;
static overlay_params params {};
static swapchain_stats sw_stats {};
static state state;
static notify_thread notifier;
static bool cfg_inited = false;
static bool inited = false;
static uint32_t vendorID;
static std::string deviceName;
// seems to quit by itself though
static std::unique_ptr<notify_thread, std::function<void(notify_thread *)>>
stop_notifier(&notifier, [](notify_thread *n){ n->quit = true; });
void imgui_init()
{
if (cfg_inited)
return;
parse_overlay_config(&params, getenv("MANGOHUD_CONFIG"));
notifier.params = &params;
pthread_create(&fileChange, NULL, &fileChanged, &notifier);
window_size = ImVec2(params.width, params.height);
init_system_info();
cfg_inited = true;
init_cpu_stats(params);
}
void imgui_create(void *ctx)
{
if (inited)
return;
inited = true;
if (!ctx)
return;
imgui_init();
gl3wInit();
std::cerr << "GL version: " << glGetString(GL_VERSION) << std::endl;
glGetIntegerv(GL_MAJOR_VERSION, &sw_stats.version_gl.major);
glGetIntegerv(GL_MINOR_VERSION, &sw_stats.version_gl.minor);
deviceName = (char*)glGetString(GL_RENDERER);
if (deviceName.find("Radeon") != std::string::npos
|| deviceName.find("AMD") != std::string::npos){
vendorID = 0x1002;
} else {
vendorID = 0x10de;
}
init_gpu_stats(vendorID, params);
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
state.imgui_ctx = ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO(); (void)io;
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
//io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
// Setup Dear ImGui style
ImGui::StyleColorsDark();
//ImGui::StyleColorsClassic();
imgui_custom_style(params);
glGetIntegerv (GL_VIEWPORT, last_vp.v);
glGetIntegerv (GL_SCISSOR_BOX, last_sb.v);
ImGui::GetIO().IniFilename = NULL;
ImGui::GetIO().DisplaySize = ImVec2(last_vp[2], last_vp[3]);
ImGui_ImplOpenGL3_Init();
// Make a dummy GL call (we don't actually need the result)
// IF YOU GET A CRASH HERE: it probably means that you haven't initialized the OpenGL function loader used by this code.
// Desktop OpenGL 3/4 need a function loader. See the IMGUI_IMPL_OPENGL_LOADER_xxx explanation above.
GLint current_texture;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &current_texture);
int font_size = params.font_size;
if (!font_size)
font_size = 24;
if (!params.font_file.empty() && file_exists(params.font_file)) {
state.font = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size);
state.font1 = io.Fonts->AddFontFromFileTTF(params.font_file.c_str(), font_size * 0.55f);
} else {
ImFontConfig font_cfg = ImFontConfig();
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
const ImWchar* glyph_ranges = io.Fonts->GetGlyphRangesDefault();
state.font = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size, &font_cfg, glyph_ranges);
state.font1 = io.Fonts->AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_size * 0.55, &font_cfg, glyph_ranges);
}
sw_stats.font1 = state.font1;
}
void imgui_shutdown()
{
#ifndef NDEBUG
std::cerr << __func__ << std::endl;
#endif
if (state.imgui_ctx) {
ImGui_ImplOpenGL3_Shutdown();
ImGui::DestroyContext(state.imgui_ctx);
state.imgui_ctx = nullptr;
}
inited = false;
}
void imgui_set_context(void *ctx)
{
if (!ctx) {
imgui_shutdown();
return;
}
#ifndef NDEBUG
std::cerr << __func__ << ": " << ctx << std::endl;
#endif
imgui_create(ctx);
}
void imgui_render()
{
if (!ImGui::GetCurrentContext())
return;
// check which one is affected by window resize and use that
GLVec vp; glGetIntegerv (GL_VIEWPORT, vp.v);
GLVec sb; glGetIntegerv (GL_SCISSOR_BOX, sb.v);
if (vp != last_vp) {
#ifndef NDEBUG
printf("viewport: %d %d %d %d\n", vp[0], vp[1], vp[2], vp[3]);
#endif
ImGui::GetIO().DisplaySize = ImVec2(vp[2], vp[3]);
}
if (sb != last_sb
|| last_vp == sb // openmw initial viewport size is the same (correct)
// at start as scissor box, so apply it instead
) {
#ifndef NDEBUG
printf("scissor box: %d %d %d %d\n", sb[0], sb[1], sb[2], sb[3]);
#endif
ImGui::GetIO().DisplaySize = ImVec2(sb[2], sb[3]);
}
last_vp = vp;
last_sb = sb;
ImGui_ImplOpenGL3_NewFrame();
ImGui::NewFrame();
{
std::lock_guard<std::mutex> lk(notifier.mutex);
position_layer(params, window_size);
render_imgui(sw_stats, params, window_size, false);
}
ImGui::PopStyleVar(3);
ImGui::Render();
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
}
void* get_proc_address(const char* name) {
void (*func)() = (void (*)())real_dlsym( RTLD_NEXT, name );
if (!func) {
std::cerr << "MANGOHUD: Failed to get function '" << name << "'" << std::endl;
exit( 1 );
}
return (void*)func;
}
void* get_glx_proc_address(const char* name) {
if (!gl.Load()) {
// Force load libGL then. If it still doesn't find it, get_proc_address should quit the program
void *handle = real_dlopen("libGL.so.1", RTLD_LAZY);
if (!handle)
std::cerr << "MANGOHUD: couldn't find libGL.so.1" << std::endl;
gl.Load(handle);
}
void *func = nullptr;
if (gl.glXGetProcAddress)
func = gl.glXGetProcAddress( (const unsigned char*) name );
if (!func && gl.glXGetProcAddressARB)
func = gl.glXGetProcAddressARB( (const unsigned char*) name );
if (!func)
func = get_proc_address( name );
return func;
}
EXPORT_C_(void *) glXCreateContext(void *dpy, void *vis, void *shareList, int direct)
{
gl.Load();
void *ctx = gl.glXCreateContext(dpy, vis, shareList, direct);
#ifndef NDEBUG
std::cerr << __func__ << ":" << ctx << std::endl;
#endif
return ctx;
}
EXPORT_C_(bool) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
gl.Load();
#ifndef NDEBUG
std::cerr << __func__ << ": " << drawable << ", " << ctx << std::endl;
#endif
bool ret = gl.glXMakeCurrent(dpy, drawable, ctx);
if (ret)
imgui_set_context(ctx);
if (params.gl_vsync >= -1) {
if (gl.glXSwapIntervalEXT)
gl.glXSwapIntervalEXT(dpy, drawable, params.gl_vsync);
if (gl.glXSwapIntervalSGI)
gl.glXSwapIntervalSGI(params.gl_vsync);
if (gl.glXSwapIntervalMESA)
gl.glXSwapIntervalMESA(params.gl_vsync);
}
return ret;
}
EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
gl.Load();
imgui_create(gl.glXGetCurrentContext());
check_keybinds(params);
update_hud_info(sw_stats, params, vendorID);
imgui_render();
gl.glXSwapBuffers(dpy, drawable);
if (fps_limit_stats.targetFrameTime > 0){
fps_limit_stats.frameStart = os_time_get_nano();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = os_time_get_nano();
}
}
EXPORT_C_(void) glXSwapIntervalEXT(void *dpy, void *draw, int interval) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
gl.Load();
if (params.gl_vsync >= 0)
interval = params.gl_vsync;
gl.glXSwapIntervalEXT(dpy, draw, interval);
}
EXPORT_C_(int) glXSwapIntervalSGI(int interval) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
gl.Load();
if (params.gl_vsync >= 0)
interval = params.gl_vsync;
return gl.glXSwapIntervalSGI(interval);
}
EXPORT_C_(int) glXSwapIntervalMESA(unsigned int interval) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
gl.Load();
if (params.gl_vsync >= 0)
interval = (unsigned int)params.gl_vsync;
return gl.glXSwapIntervalMESA(interval);
}
EXPORT_C_(int) glXGetSwapIntervalMESA() {
gl.Load();
static bool first_call = true;
int interval = gl.glXGetSwapIntervalMESA();
if (first_call) {
first_call = false;
if (params.gl_vsync >= 0) {
interval = params.gl_vsync;
gl.glXSwapIntervalMESA(interval);
}
}
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
return interval;
}
struct func_ptr {
const char *name;
void *ptr;
};
static std::array<const func_ptr, 9> name_to_funcptr_map = {{
#define ADD_HOOK(fn) { #fn, (void *) fn }
ADD_HOOK(glXGetProcAddress),
ADD_HOOK(glXGetProcAddressARB),
ADD_HOOK(glXCreateContext),
ADD_HOOK(glXMakeCurrent),
ADD_HOOK(glXSwapBuffers),
ADD_HOOK(glXSwapIntervalEXT),
ADD_HOOK(glXSwapIntervalSGI),
ADD_HOOK(glXSwapIntervalMESA),
ADD_HOOK(glXGetSwapIntervalMESA),
#undef ADD_HOOK
}};
static void *find_ptr(const char *name)
{
for (auto& func : name_to_funcptr_map) {
if (strcmp(name, func.name) == 0)
return func.ptr;
}
return nullptr;
}
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = find_ptr( (const char*)procName );
if (func)
return func;
return get_glx_proc_address((const char*)procName);
}
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName) {
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = find_ptr( (const char*)procName );
if (func)
return func;
return get_glx_proc_address((const char*)procName);
}
#ifdef HOOK_DLSYM
EXPORT_C_(void*) dlsym(void * handle, const char * name)
{
void* func = find_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
return func;
}
//fprintf(stderr,"%s: foreign: %s\n", __func__ , name);
return real_dlsym(handle, name);
}
#endif

@ -0,0 +1,108 @@
#include <iostream>
#include <array>
#include <cstring>
#include "real_dlsym.h"
#include "mesa/util/macros.h"
#include "mesa/util/os_time.h"
#include "blacklist.h"
#include <chrono>
#include <iomanip>
#include "imgui_hud.h"
using namespace MangoHud::GL;
#define EXPORT_C_(type) extern "C" __attribute__((__visibility__("default"))) type
EXPORT_C_(void *) eglGetProcAddress(const char* procName);
void* get_egl_proc_address(const char* name) {
void *func = nullptr;
static void *(*pfn_eglGetProcAddress)(const char*) = nullptr;
if (!pfn_eglGetProcAddress)
pfn_eglGetProcAddress = reinterpret_cast<decltype(pfn_eglGetProcAddress)>(get_proc_address("eglGetProcAddress"));
if (pfn_eglGetProcAddress)
func = pfn_eglGetProcAddress(name);
if (!func)
func = get_proc_address( name );
if (!func) {
std::cerr << "MANGOHUD: Failed to get function '" << name << "'" << std::endl;
}
return func;
}
//EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
EXPORT_C_(int) eglMakeCurrent_OFF(void *dpy, void *draw, void *read,void *ctx) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << draw << ", " << ctx << std::endl;
#endif
int ret = 0;
return ret;
}
EXPORT_C_(unsigned int) eglSwapBuffers( void* dpy, void* surf)
{
static int (*pfn_eglSwapBuffers)(void*, void*) = nullptr;
if (!pfn_eglSwapBuffers)
pfn_eglSwapBuffers = reinterpret_cast<decltype(pfn_eglSwapBuffers)>(get_proc_address("eglSwapBuffers"));
if (!is_blacklisted()) {
static int (*pfn_eglQuerySurface)(void* dpy, void* surface, int attribute, int *value) = nullptr;
if (!pfn_eglQuerySurface)
pfn_eglQuerySurface = reinterpret_cast<decltype(pfn_eglQuerySurface)>(get_proc_address("eglQuerySurface"));
//std::cerr << __func__ << "\n";
imgui_create(surf);
int width=0, height=0;
if (pfn_eglQuerySurface(dpy, surf, 0x3056, &height) &&
pfn_eglQuerySurface(dpy, surf, 0x3057, &width))
imgui_render(width, height);
//std::cerr << "\t" << width << " x " << height << "\n";
}
return pfn_eglSwapBuffers(dpy, surf);
}
struct func_ptr {
const char *name;
void *ptr;
};
static std::array<const func_ptr, 1> name_to_funcptr_map = {{
#define ADD_HOOK(fn) { #fn, (void *) fn }
ADD_HOOK(eglGetProcAddress),
#undef ADD_HOOK
}};
EXPORT_C_(void *) mangohud_find_egl_ptr(const char *name)
{
if (is_blacklisted())
return nullptr;
for (auto& func : name_to_funcptr_map) {
if (strcmp(name, func.name) == 0)
return func.ptr;
}
return nullptr;
}
EXPORT_C_(void *) eglGetProcAddress(const char* procName) {
//std::cerr << __func__ << ": " << procName << std::endl;
void* func = mangohud_find_egl_ptr(procName);
if (func)
return func;
return get_egl_proc_address(procName);
}

@ -0,0 +1,254 @@
#include <X11/Xlib.h>
#include <iostream>
#include <array>
#include <thread>
#include <vector>
#include <algorithm>
#include <cstring>
#include "real_dlsym.h"
#include "loaders/loader_glx.h"
#include "loaders/loader_x11.h"
#include "mesa/util/macros.h"
#include "mesa/util/os_time.h"
#include "blacklist.h"
#include <chrono>
#include <iomanip>
#include "imgui_hud.h"
using namespace MangoHud::GL;
#define EXPORT_C_(type) extern "C" __attribute__((__visibility__("default"))) type
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName);
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName);
#ifndef GLX_WIDTH
#define GLX_WIDTH 0x801D
#define GLX_HEIGTH 0x801E
#endif
static glx_loader glx;
static std::vector<std::thread::id> gl_threads;
void* get_glx_proc_address(const char* name) {
glx.Load();
void *func = nullptr;
if (glx.GetProcAddress)
func = glx.GetProcAddress( (const unsigned char*) name );
if (!func && glx.GetProcAddressARB)
func = glx.GetProcAddressARB( (const unsigned char*) name );
if (!func)
func = get_proc_address( name );
if (!func) {
std::cerr << "MANGOHUD: Failed to get function '" << name << "'" << std::endl;
}
return func;
}
EXPORT_C_(void *) glXCreateContext(void *dpy, void *vis, void *shareList, int direct)
{
glx.Load();
void *ctx = glx.CreateContext(dpy, vis, shareList, direct);
#ifndef NDEBUG
std::cerr << __func__ << ":" << ctx << std::endl;
#endif
return ctx;
}
EXPORT_C_(int) glXMakeCurrent(void* dpy, void* drawable, void* ctx) {
glx.Load();
#ifndef NDEBUG
std::cerr << __func__ << ": " << drawable << ", " << ctx << std::endl;
#endif
int ret = glx.MakeCurrent(dpy, drawable, ctx);
if (!is_blacklisted()) {
if (ret) {
//TODO might as well just ignore everything here as long as VBOs get recreated anyway
auto it = std::find(gl_threads.begin(), gl_threads.end(), std::this_thread::get_id());
if (!ctx) {
if (it != gl_threads.end())
gl_threads.erase(it);
if (!gl_threads.size())
imgui_set_context(nullptr);
} else {
if (it == gl_threads.end())
gl_threads.push_back(std::this_thread::get_id());
imgui_set_context(ctx);
#ifndef NDEBUG
std::cerr << "MANGOHUD: GL thread count: " << gl_threads.size() << "\n";
#endif
}
}
if (params.gl_vsync >= -1) {
if (glx.SwapIntervalEXT)
glx.SwapIntervalEXT(dpy, drawable, params.gl_vsync);
if (glx.SwapIntervalSGI)
glx.SwapIntervalSGI(params.gl_vsync);
if (glx.SwapIntervalMESA)
glx.SwapIntervalMESA(params.gl_vsync);
}
}
return ret;
}
EXPORT_C_(void) glXSwapBuffers(void* dpy, void* drawable) {
glx.Load();
if (!is_blacklisted()) {
imgui_create(glx.GetCurrentContext());
unsigned int width = -1, height = -1;
// glXQueryDrawable is buggy, use XGetGeometry instead
Window unused_window;
int unused;
static bool xgetgeom_failed = false;
if (xgetgeom_failed || !g_x11->XGetGeometry((Display*)dpy,
(Window)drawable, &unused_window,
&unused, &unused,
&width, &height,
(unsigned int*) &unused, (unsigned int*) &unused)) {
xgetgeom_failed = true;
glx.QueryDrawable(dpy, drawable, GLX_WIDTH, &width);
glx.QueryDrawable(dpy, drawable, GLX_HEIGTH, &height);
}
/*GLint vp[4]; glGetIntegerv (GL_VIEWPORT, vp);
width = vp[2];
height = vp[3];*/
imgui_render(width, height);
}
glx.SwapBuffers(dpy, drawable);
if (!is_blacklisted() && fps_limit_stats.targetFrameTime > 0){
fps_limit_stats.frameStart = os_time_get_nano();
FpsLimiter(fps_limit_stats);
fps_limit_stats.frameEnd = os_time_get_nano();
}
}
EXPORT_C_(void) glXSwapIntervalEXT(void *dpy, void *draw, int interval) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
glx.Load();
if (!is_blacklisted() && params.gl_vsync >= 0)
interval = params.gl_vsync;
glx.SwapIntervalEXT(dpy, draw, interval);
}
EXPORT_C_(int) glXSwapIntervalSGI(int interval) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
glx.Load();
if (!is_blacklisted() && params.gl_vsync >= 0)
interval = params.gl_vsync;
return glx.SwapIntervalSGI(interval);
}
EXPORT_C_(int) glXSwapIntervalMESA(unsigned int interval) {
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
glx.Load();
if (!is_blacklisted() && params.gl_vsync >= 0)
interval = (unsigned int)params.gl_vsync;
return glx.SwapIntervalMESA(interval);
}
EXPORT_C_(int) glXGetSwapIntervalMESA() {
glx.Load();
int interval = glx.GetSwapIntervalMESA();
if (!is_blacklisted()) {
static bool first_call = true;
if (first_call) {
first_call = false;
if (params.gl_vsync >= 0) {
interval = params.gl_vsync;
glx.SwapIntervalMESA(interval);
}
}
}
#ifndef NDEBUG
std::cerr << __func__ << ": " << interval << std::endl;
#endif
return interval;
}
struct func_ptr {
const char *name;
void *ptr;
};
static std::array<const func_ptr, 9> name_to_funcptr_map = {{
#define ADD_HOOK(fn) { #fn, (void *) fn }
ADD_HOOK(glXGetProcAddress),
ADD_HOOK(glXGetProcAddressARB),
ADD_HOOK(glXCreateContext),
ADD_HOOK(glXMakeCurrent),
ADD_HOOK(glXSwapBuffers),
ADD_HOOK(glXSwapIntervalEXT),
ADD_HOOK(glXSwapIntervalSGI),
ADD_HOOK(glXSwapIntervalMESA),
ADD_HOOK(glXGetSwapIntervalMESA),
#undef ADD_HOOK
}};
EXPORT_C_(void *) mangohud_find_glx_ptr(const char *name)
{
if (is_blacklisted())
return nullptr;
for (auto& func : name_to_funcptr_map) {
if (strcmp(name, func.name) == 0)
return func.ptr;
}
return nullptr;
}
EXPORT_C_(void *) glXGetProcAddress(const unsigned char* procName) {
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = mangohud_find_glx_ptr( (const char*)procName );
if (func)
return func;
return get_glx_proc_address((const char*)procName);
}
EXPORT_C_(void *) glXGetProcAddressARB(const unsigned char* procName) {
//std::cerr << __func__ << ":" << procName << std::endl;
void* func = mangohud_find_glx_ptr( (const char*)procName );
if (func)
return func;
return get_glx_proc_address((const char*)procName);
}

@ -1,4 +0,0 @@
#pragma once
void *real_dlopen(const char *filename, int flag);
void* real_dlsym( void*, const char* );

@ -4,26 +4,29 @@
struct gpuInfo gpu_info;
FILE *amdGpuFile = nullptr, *amdTempFile = nullptr, *amdGpuVramTotalFile = nullptr, *amdGpuVramUsedFile = nullptr, *amdGpuCoreClockFile = nullptr, *amdGpuMemoryClockFile = nullptr;
pthread_t cpuThread, gpuThread, cpuInfoThread;
void *getNvidiaGpuInfo(void *){
if (!nvmlSuccess)
checkNvidia();
void getNvidiaGpuInfo(){
if (nvmlSuccess){
getNvidiaInfo();
getNVMLInfo();
gpu_info.load = nvidiaUtilization.gpu;
gpu_info.temp = nvidiaTemp;
gpu_info.memoryUsed = nvidiaMemory.used / (1024.f * 1024.f * 1024.f);
gpu_info.CoreClock = nvidiaCoreClock;
gpu_info.MemClock = nvidiaMemClock * 2;
gpu_info.MemClock = nvidiaMemClock;
}
pthread_detach(gpuThread);
return NULL;
#ifdef HAVE_XNVCTRL
else if (nvctrlSuccess) {
getNvctrlInfo();
gpu_info.load = nvctrl_info.load;
gpu_info.temp = nvctrl_info.temp;
gpu_info.memoryUsed = nvctrl_info.memoryUsed / (1024.f);
gpu_info.CoreClock = nvctrl_info.CoreClock;
gpu_info.MemClock = nvctrl_info.MemClock;
}
#endif
}
void *getAmdGpuUsage(void *){
void getAmdGpuUsage(){
int64_t value = 0;
if (amdGpuFile) {
@ -80,7 +83,4 @@ void *getAmdGpuUsage(void *){
amdgpu.MemClock = value / 1000000;
gpu_info.MemClock = amdgpu.MemClock;
}
pthread_detach(gpuThread);
return NULL;
}

@ -3,10 +3,10 @@
#include <stdlib.h>
#include <stdio.h>
#include "nvidia_info.h"
#include "nvctrl.h"
using namespace std;
extern FILE *amdGpuFile, *amdTempFile, *amdGpuVramTotalFile, *amdGpuVramUsedFile, *amdGpuCoreClockFile, *amdGpuMemoryClockFile;
extern pthread_t cpuThread, gpuThread, cpuInfoThread;
struct amdGpu {
int load;
@ -29,6 +29,5 @@ struct gpuInfo{
extern struct amdGpu amdgpu;
extern struct gpuInfo gpu_info;
void *getNvidiaInfo(void*);
void *getNvidiaGpuInfo(void*);
void *getAmdGpuUsage(void*);
void getNvidiaGpuInfo(void);
void getAmdGpuUsage(void);

@ -0,0 +1,37 @@
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include "real_dlsym.h"
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;
if (!find_glx_ptr)
find_glx_ptr = reinterpret_cast<decltype(find_glx_ptr)> (real_dlsym(RTLD_NEXT, "mangohud_find_glx_ptr"));
if (!find_egl_ptr)
find_egl_ptr = reinterpret_cast<decltype(find_egl_ptr)> (real_dlsym(RTLD_NEXT, "mangohud_find_egl_ptr"));
void* func = nullptr;
if (find_glx_ptr) {
func = find_glx_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
return func;
}
}
if (find_egl_ptr) {
func = find_egl_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
return func;
}
}
//fprintf(stderr,"%s: foreign: %s\n", __func__ , name);
return real_dlsym(handle, name);
}

@ -2,8 +2,7 @@
#include "string_utils.h"
#include <fstream>
pthread_t ioThread;
void *getIoStats(void *args) {
void getIoStats(void *args) {
iostats *io = reinterpret_cast<iostats *>(args);
if (io) {
io->prev.read_bytes = io->curr.read_bytes;
@ -22,6 +21,4 @@ void *getIoStats(void *args) {
io->diff.read = (io->curr.read_bytes - io->prev.read_bytes) / (1024.f * 1024.f);
io->diff.write = (io->curr.write_bytes - io->prev.write_bytes) / (1024.f * 1024.f);
}
pthread_detach(ioThread);
return NULL;
}

@ -2,8 +2,6 @@
#include <pthread.h>
#include <inttypes.h>
extern pthread_t ioThread;
struct iostats {
struct {
unsigned long long read_bytes;
@ -19,4 +17,4 @@ struct iostats {
} diff;
};
void *getIoStats(void *args);
void getIoStats(void *args);

@ -1,18 +1,24 @@
#include <X11/Xlib.h>
#include <iostream>
#include "X11/keysym.h"
#include <functional>
#pragma once
#include "shared_x11.h"
#include "loaders/loader_x11.h"
#ifndef KeySym
typedef unsigned long KeySym;
#endif
double elapsedF2, elapsedF12, elapsedReloadCfg;
uint64_t last_f2_press, last_f12_press, reload_cfg_press;
pthread_t f2;
char *displayid = getenv("DISPLAY");
std::unique_ptr<Display, std::function<void(Display*)>> dpy(XOpenDisplay(displayid), [](Display* d) { XCloseDisplay(d); });
#ifdef HAVE_X11
bool key_is_pressed(KeySym ks) {
if (!init_x11())
return false;
char keys_return[32];
XQueryKeymap(dpy.get(), keys_return);
KeyCode kc2 = XKeysymToKeycode(dpy.get(), ks);
g_x11->XQueryKeymap(get_xdisplay(), keys_return);
KeyCode kc2 = g_x11->XKeysymToKeycode(get_xdisplay(), ks);
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
return isPressed;
}
#endif

@ -0,0 +1,281 @@
#include "loaders/loader_dbus.h"
#include <iostream>
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined(LIBRARY_LOADER_DBUS_H_DLOPEN) && !defined(LIBRARY_LOADER_DBUS_H_DT_NEEDED)
#error neither LIBRARY_LOADER_DBUS_H_DLOPEN nor LIBRARY_LOADER_DBUS_H_DT_NEEDED defined
#endif
#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN) && defined(LIBRARY_LOADER_DBUS_H_DT_NEEDED)
#error both LIBRARY_LOADER_DBUS_H_DLOPEN and LIBRARY_LOADER_DBUS_H_DT_NEEDED defined
#endif
libdbus_loader::libdbus_loader() : loaded_(false) {
}
libdbus_loader::~libdbus_loader() {
CleanUp(loaded_);
}
bool libdbus_loader::Load(const std::string& library_name) {
if (loaded_) {
return false;
}
#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_) {
std::cerr << "MANGOHUD: " << library_name << " dlopen failed: " << dlerror() << std::endl;
return false;
}
bus_add_match =
reinterpret_cast<decltype(this->bus_add_match)>(
dlsym(library_, "dbus_bus_add_match"));
if (!bus_add_match) {
CleanUp(true);
return false;
}
bus_get =
reinterpret_cast<decltype(this->bus_get)>(
dlsym(library_, "dbus_bus_get"));
if (!bus_get) {
CleanUp(true);
return false;
}
bus_get_unique_name =
reinterpret_cast<decltype(this->bus_get_unique_name)>(
dlsym(library_, "dbus_bus_get_unique_name"));
if (!bus_get_unique_name) {
CleanUp(true);
return false;
}
bus_remove_match =
reinterpret_cast<decltype(this->bus_remove_match)>(
dlsym(library_, "dbus_bus_remove_match"));
if (!bus_remove_match) {
CleanUp(true);
return false;
}
connection_pop_message =
reinterpret_cast<decltype(this->connection_pop_message)>(
dlsym(library_, "dbus_connection_pop_message"));
if (!connection_pop_message) {
CleanUp(true);
return false;
}
connection_read_write =
reinterpret_cast<decltype(this->connection_read_write)>(
dlsym(library_, "dbus_connection_read_write"));
if (!connection_read_write) {
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"));
if (!connection_send_with_reply_and_block) {
CleanUp(true);
return false;
}
connection_unref =
reinterpret_cast<decltype(this->connection_unref)>(
dlsym(library_, "dbus_connection_unref"));
if (!connection_unref) {
CleanUp(true);
return false;
}
error_free =
reinterpret_cast<decltype(this->error_free)>(
dlsym(library_, "dbus_error_free"));
if (!error_free) {
CleanUp(true);
return false;
}
error_init =
reinterpret_cast<decltype(this->error_init)>(
dlsym(library_, "dbus_error_init"));
if (!error_init) {
CleanUp(true);
return false;
}
error_is_set =
reinterpret_cast<decltype(this->error_is_set)>(
dlsym(library_, "dbus_error_is_set"));
if (!error_is_set) {
CleanUp(true);
return false;
}
message_append_args =
reinterpret_cast<decltype(this->message_append_args)>(
dlsym(library_, "dbus_message_append_args"));
if (!message_append_args) {
CleanUp(true);
return false;
}
message_is_signal =
reinterpret_cast<decltype(this->message_is_signal)>(
dlsym(library_, "dbus_message_is_signal"));
if (!message_is_signal) {
CleanUp(true);
return false;
}
message_iter_get_arg_type =
reinterpret_cast<decltype(this->message_iter_get_arg_type)>(
dlsym(library_, "dbus_message_iter_get_arg_type"));
if (!message_iter_get_arg_type) {
CleanUp(true);
return false;
}
message_iter_get_basic =
reinterpret_cast<decltype(this->message_iter_get_basic)>(
dlsym(library_, "dbus_message_iter_get_basic"));
if (!message_iter_get_basic) {
CleanUp(true);
return false;
}
message_iter_init =
reinterpret_cast<decltype(this->message_iter_init)>(
dlsym(library_, "dbus_message_iter_init"));
if (!message_iter_init) {
CleanUp(true);
return false;
}
message_iter_next =
reinterpret_cast<decltype(this->message_iter_next)>(
dlsym(library_, "dbus_message_iter_next"));
if (!message_iter_next) {
CleanUp(true);
return false;
}
message_iter_recurse =
reinterpret_cast<decltype(this->message_iter_recurse)>(
dlsym(library_, "dbus_message_iter_recurse"));
if (!message_iter_recurse) {
CleanUp(true);
return false;
}
message_new_method_call =
reinterpret_cast<decltype(this->message_new_method_call)>(
dlsym(library_, "dbus_message_new_method_call"));
if (!message_new_method_call) {
CleanUp(true);
return false;
}
message_unref =
reinterpret_cast<decltype(this->message_unref)>(
dlsym(library_, "dbus_message_unref"));
if (!message_unref) {
CleanUp(true);
return false;
}
move_error =
reinterpret_cast<decltype(this->move_error)>(
dlsym(library_, "dbus_move_error"));
if (!move_error) {
CleanUp(true);
return false;
}
threads_init_default =
reinterpret_cast<decltype(this->threads_init_default)>(
dlsym(library_, "dbus_threads_init_default"));
if (!threads_init_default) {
CleanUp(true);
return false;
}
message_get_sender =
reinterpret_cast<decltype(this->message_get_sender)>(
dlsym(library_, "dbus_message_get_sender"));
if (!message_get_sender) {
CleanUp(true);
return false;
}
#endif
#if defined(LIBRARY_LOADER_DBUS_H_DT_NEEDED)
bus_add_match = &::dbus_bus_add_match;
bus_get = &::dbus_bus_get;
bus_get_unique_name = &::dbus_bus_get_unique_name;
bus_remove_match = &::dbus_bus_remove_match;
connection_pop_message = &::dbus_connection_pop_message;
connection_read_write = &::dbus_connection_read_write;
connection_send_with_reply_and_block = &::dbus_connection_send_with_reply_and_block;
connection_unref = &::dbus_connection_unref;
error_free = &::dbus_error_free;
error_init = &::dbus_error_init;
error_is_set = &::dbus_error_is_set;
message_append_args = &::dbus_message_append_args;
message_is_signal = &::dbus_message_is_signal;
message_iter_get_arg_type = &::dbus_message_iter_get_arg_type;
message_iter_get_basic = &::dbus_message_iter_get_basic;
message_iter_init = &::dbus_message_iter_init;
message_iter_next = &::dbus_message_iter_next;
message_iter_recurse = &::dbus_message_iter_recurse;
message_new_method_call = &::dbus_message_new_method_call;
message_unref = &::dbus_message_unref;
move_error = &::dbus_move_error;
threads_init_default = &::dbus_threads_init_default;
#endif
loaded_ = true;
return true;
}
void libdbus_loader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
bus_add_match = NULL;
bus_get = NULL;
bus_get_unique_name = NULL;
bus_remove_match = NULL;
connection_pop_message = NULL;
connection_read_write = NULL;
connection_send_with_reply_and_block = NULL;
connection_unref = NULL;
error_free = NULL;
error_init = NULL;
error_is_set = NULL;
message_append_args = NULL;
message_is_signal = NULL;
message_iter_get_arg_type = NULL;
message_iter_get_basic = NULL;
message_iter_init = NULL;
message_iter_next = NULL;
message_iter_recurse = NULL;
message_new_method_call = NULL;
message_unref = NULL;
move_error = NULL;
threads_init_default = NULL;
}

@ -0,0 +1,62 @@
#ifndef LIBRARY_LOADER_DBUS_H
#define LIBRARY_LOADER_DBUS_H
#include <dbus/dbus.h>
#define LIBRARY_LOADER_DBUS_H_DLOPEN
#include <string>
#include <dlfcn.h>
class libdbus_loader {
public:
libdbus_loader();
libdbus_loader(const std::string& library_name) : libdbus_loader() {
Load(library_name);
}
~libdbus_loader();
bool Load(const std::string& library_name);
bool IsLoaded() { return loaded_; }
decltype(&::dbus_bus_add_match) bus_add_match;
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_pop_message) connection_pop_message;
decltype(&::dbus_connection_read_write) connection_read_write;
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_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;
decltype(&::dbus_message_iter_init) message_iter_init;
decltype(&::dbus_message_iter_next) message_iter_next;
decltype(&::dbus_message_iter_recurse) message_iter_recurse;
decltype(&::dbus_message_new_method_call) message_new_method_call;
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:
void CleanUp(bool unload);
#if defined(LIBRARY_LOADER_DBUS_H_DLOPEN)
void* library_;
#endif
bool loaded_;
// Disallow copy constructor and assignment operator.
libdbus_loader(const libdbus_loader&);
void operator=(const libdbus_loader&);
};
#endif // LIBRARY_LOADER_DBUS_H

@ -1,113 +0,0 @@
#include "gl/real_dlsym.h"
#include "loaders/loader_gl.h"
gl_loader::gl_loader() : loaded_(false) {
}
gl_loader::~gl_loader() {
CleanUp(loaded_);
}
bool gl_loader::Load(void *handle, bool egl_only) {
if (loaded_) {
return true;
}
if (!handle)
handle = RTLD_NEXT;
eglSwapBuffers =
reinterpret_cast<decltype(this->eglSwapBuffers)>(
real_dlsym(handle, "eglSwapBuffers"));
if (egl_only) {
loaded_ = true;
return true;
}
glXGetProcAddress =
reinterpret_cast<decltype(this->glXGetProcAddress)>(
real_dlsym(handle, "glXGetProcAddress"));
glXGetProcAddressARB =
reinterpret_cast<decltype(this->glXGetProcAddressARB)>(
real_dlsym(handle, "glXGetProcAddressARB"));
if (!glXGetProcAddress) {
CleanUp(true);
return false;
}
glXCreateContext =
reinterpret_cast<decltype(this->glXCreateContext)>(
glXGetProcAddress((const unsigned char *)"glXCreateContext"));
if (!glXCreateContext) {
CleanUp(true);
return false;
}
glXDestroyContext =
reinterpret_cast<decltype(this->glXDestroyContext)>(
glXGetProcAddress((const unsigned char *)"glXDestroyContext"));
if (!glXDestroyContext) {
CleanUp(true);
return false;
}
glXGetCurrentContext =
reinterpret_cast<decltype(this->glXGetCurrentContext)>(
glXGetProcAddress((const unsigned char *)"glXGetCurrentContext"));
if (!glXGetCurrentContext) {
CleanUp(true);
return false;
}
glXSwapBuffers =
reinterpret_cast<decltype(this->glXSwapBuffers)>(
glXGetProcAddress((const unsigned char *)"glXSwapBuffers"));
if (!glXSwapBuffers) {
CleanUp(true);
return false;
}
glXSwapIntervalEXT =
reinterpret_cast<decltype(this->glXSwapIntervalEXT)>(
glXGetProcAddress((const unsigned char *)"glXSwapIntervalEXT"));
glXSwapIntervalSGI =
reinterpret_cast<decltype(this->glXSwapIntervalSGI)>(
glXGetProcAddress((const unsigned char *)"glXSwapIntervalSGI"));
glXSwapIntervalMESA =
reinterpret_cast<decltype(this->glXSwapIntervalMESA)>(
glXGetProcAddress((const unsigned char *)"glXSwapIntervalMESA"));
glXGetSwapIntervalMESA =
reinterpret_cast<decltype(this->glXGetSwapIntervalMESA)>(
glXGetProcAddress((const unsigned char *)"glXGetSwapIntervalMESA"));
glXMakeCurrent =
reinterpret_cast<decltype(this->glXMakeCurrent)>(
glXGetProcAddress((const unsigned char *)"glXMakeCurrent"));
if (!glXMakeCurrent) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
void gl_loader::CleanUp(bool unload) {
loaded_ = false;
glXGetProcAddress = nullptr;
glXGetProcAddressARB = nullptr;
glXCreateContext = nullptr;
glXDestroyContext = nullptr;
glXSwapBuffers = nullptr;
glXSwapIntervalEXT = nullptr;
glXSwapIntervalSGI = nullptr;
glXSwapIntervalMESA = nullptr;
glXMakeCurrent = nullptr;
}

@ -1,39 +0,0 @@
#ifndef LIBRARY_LOADER_GL_H
#define LIBRARY_LOADER_GL_H
#include "gl/gl.h"
#include <dlfcn.h>
class gl_loader {
public:
gl_loader();
~gl_loader();
bool Load(void *handle = nullptr, bool egl_only = false);
bool IsLoaded() { return loaded_; }
decltype(&::glXGetProcAddress) glXGetProcAddress;
decltype(&::glXGetProcAddressARB) glXGetProcAddressARB;
decltype(&::glXCreateContext) glXCreateContext;
decltype(&::glXDestroyContext) glXDestroyContext;
decltype(&::glXSwapBuffers) glXSwapBuffers;
decltype(&::glXSwapIntervalEXT) glXSwapIntervalEXT;
decltype(&::glXSwapIntervalSGI) glXSwapIntervalSGI;
decltype(&::glXSwapIntervalMESA) glXSwapIntervalMESA;
decltype(&::glXGetSwapIntervalMESA) glXGetSwapIntervalMESA;
decltype(&::glXMakeCurrent) glXMakeCurrent;
decltype(&::glXGetCurrentContext) glXGetCurrentContext;
decltype(&::eglSwapBuffers) eglSwapBuffers;
private:
void CleanUp(bool unload);
bool loaded_;
// Disallow copy constructor and assignment operator.
gl_loader(const gl_loader&);
void operator=(const gl_loader&);
};
#endif // LIBRARY_LOADER_GL_H

@ -0,0 +1,116 @@
#include <iostream>
#include "real_dlsym.h"
#include "loaders/loader_glx.h"
glx_loader::glx_loader() : loaded_(false) {
}
glx_loader::~glx_loader() {
CleanUp(loaded_);
}
bool glx_loader::Load() {
if (loaded_) {
return true;
}
// Force load libGL
void *handle = real_dlopen("libGL.so.1", RTLD_LAZY);
if (!handle) {
std::cerr << "MANGOHUD: couldn't find libGL.so.1: " << dlerror() << std::endl;
return false;
}
GetProcAddress =
reinterpret_cast<decltype(this->GetProcAddress)>(
real_dlsym(handle, "glXGetProcAddress"));
GetProcAddressARB =
reinterpret_cast<decltype(this->GetProcAddressARB)>(
real_dlsym(handle, "glXGetProcAddressARB"));
if (!GetProcAddress) {
CleanUp(true);
return false;
}
CreateContext =
reinterpret_cast<decltype(this->CreateContext)>(
GetProcAddress((const unsigned char *)"glXCreateContext"));
if (!CreateContext) {
CleanUp(true);
return false;
}
DestroyContext =
reinterpret_cast<decltype(this->DestroyContext)>(
GetProcAddress((const unsigned char *)"glXDestroyContext"));
if (!DestroyContext) {
CleanUp(true);
return false;
}
GetCurrentContext =
reinterpret_cast<decltype(this->GetCurrentContext)>(
GetProcAddress((const unsigned char *)"glXGetCurrentContext"));
if (!GetCurrentContext) {
CleanUp(true);
return false;
}
SwapBuffers =
reinterpret_cast<decltype(this->SwapBuffers)>(
GetProcAddress((const unsigned char *)"glXSwapBuffers"));
if (!SwapBuffers) {
CleanUp(true);
return false;
}
SwapIntervalEXT =
reinterpret_cast<decltype(this->SwapIntervalEXT)>(
GetProcAddress((const unsigned char *)"glXSwapIntervalEXT"));
SwapIntervalSGI =
reinterpret_cast<decltype(this->SwapIntervalSGI)>(
GetProcAddress((const unsigned char *)"glXSwapIntervalSGI"));
SwapIntervalMESA =
reinterpret_cast<decltype(this->SwapIntervalMESA)>(
GetProcAddress((const unsigned char *)"glXSwapIntervalMESA"));
GetSwapIntervalMESA =
reinterpret_cast<decltype(this->GetSwapIntervalMESA)>(
GetProcAddress((const unsigned char *)"glXGetSwapIntervalMESA"));
QueryDrawable =
reinterpret_cast<decltype(this->QueryDrawable)>(
GetProcAddress((const unsigned char *)"glXQueryDrawable"));
MakeCurrent =
reinterpret_cast<decltype(this->MakeCurrent)>(
GetProcAddress((const unsigned char *)"glXMakeCurrent"));
if (!MakeCurrent) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
void glx_loader::CleanUp(bool unload) {
loaded_ = false;
GetProcAddress = nullptr;
GetProcAddressARB = nullptr;
CreateContext = nullptr;
DestroyContext = nullptr;
SwapBuffers = nullptr;
SwapIntervalEXT = nullptr;
SwapIntervalSGI = nullptr;
SwapIntervalMESA = nullptr;
QueryDrawable = nullptr;
MakeCurrent = nullptr;
}
glx_loader glx;

@ -0,0 +1,34 @@
#pragma once
#include "gl/gl.h"
#include <dlfcn.h>
class glx_loader {
public:
glx_loader();
~glx_loader();
bool Load();
bool IsLoaded() { return loaded_; }
decltype(&::glXGetProcAddress) GetProcAddress;
decltype(&::glXGetProcAddressARB) GetProcAddressARB;
decltype(&::glXCreateContext) CreateContext;
decltype(&::glXDestroyContext) DestroyContext;
decltype(&::glXSwapBuffers) SwapBuffers;
decltype(&::glXSwapIntervalEXT) SwapIntervalEXT;
decltype(&::glXSwapIntervalSGI) SwapIntervalSGI;
decltype(&::glXSwapIntervalMESA) SwapIntervalMESA;
decltype(&::glXGetSwapIntervalMESA) GetSwapIntervalMESA;
decltype(&::glXMakeCurrent) MakeCurrent;
decltype(&::glXGetCurrentContext) GetCurrentContext;
decltype(&::glXQueryDrawable) QueryDrawable;
private:
void CleanUp(bool unload);
bool loaded_;
// Disallow copy constructor and assignment operator.
glx_loader(const glx_loader&);
void operator=(const glx_loader&);
};

@ -0,0 +1,95 @@
#include "loader_nvctrl.h"
#include <iostream>
// Put these sanity checks here so that they fire at most once
// (to avoid cluttering the build output).
#if !defined(LIBRARY_LOADER_NVCTRL_H_DLOPEN) && !defined(LIBRARY_LOADER_NVCTRL_H_DT_NEEDED)
#error neither LIBRARY_LOADER_NVCTRL_H_DLOPEN nor LIBRARY_LOADER_NVCTRL_H_DT_NEEDED defined
#endif
#if defined(LIBRARY_LOADER_NVCTRL_H_DLOPEN) && defined(LIBRARY_LOADER_NVCTRL_H_DT_NEEDED)
#error both LIBRARY_LOADER_NVCTRL_H_DLOPEN and LIBRARY_LOADER_NVCTRL_H_DT_NEEDED defined
#endif
libnvctrl_loader::libnvctrl_loader() : loaded_(false) {
}
libnvctrl_loader::~libnvctrl_loader() {
CleanUp(loaded_);
}
bool libnvctrl_loader::Load(const std::string& library_name) {
if (loaded_) {
return false;
}
#if defined(LIBRARY_LOADER_NVCTRL_H_DLOPEN)
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_) {
std::cerr << "MANGOHUD: " << library_name << " dlopen failed: " << dlerror() << std::endl;
return false;
}
XNVCTRLIsNvScreen =
reinterpret_cast<decltype(this->XNVCTRLIsNvScreen)>(
dlsym(library_, "XNVCTRLIsNvScreen"));
if (!XNVCTRLIsNvScreen) {
CleanUp(true);
return false;
}
XNVCTRLQueryVersion =
reinterpret_cast<decltype(this->XNVCTRLQueryVersion)>(
dlsym(library_, "XNVCTRLQueryVersion"));
if (!XNVCTRLQueryVersion) {
CleanUp(true);
return false;
}
XNVCTRLQueryAttribute =
reinterpret_cast<decltype(this->XNVCTRLQueryAttribute)>(
dlsym(library_, "XNVCTRLQueryAttribute"));
if (!XNVCTRLQueryAttribute) {
CleanUp(true);
return false;
}
XNVCTRLQueryTargetStringAttribute =
reinterpret_cast<decltype(this->XNVCTRLQueryTargetStringAttribute)>(
dlsym(library_, "XNVCTRLQueryTargetStringAttribute"));
if (!XNVCTRLQueryTargetStringAttribute) {
CleanUp(true);
return false;
}
XNVCTRLQueryTargetAttribute64 =
reinterpret_cast<decltype(this->XNVCTRLQueryTargetAttribute64)>(
dlsym(library_, "XNVCTRLQueryTargetAttribute64"));
if (!XNVCTRLQueryTargetAttribute64) {
CleanUp(true);
return false;
}
#endif
#if defined(LIBRARY_LOADER_NVCTRL_H_DT_NEEDED)
XNVCTRLQueryVersion = &::XNVCTRLQueryVersion;
XNVCTRLQueryAttribute = &::XNVCTRLQueryAttribute;
#endif
loaded_ = true;
return true;
}
void libnvctrl_loader::CleanUp(bool unload) {
#if defined(LIBRARY_LOADER_NVCTRL_H_DLOPEN)
if (unload) {
dlclose(library_);
library_ = NULL;
}
#endif
loaded_ = false;
XNVCTRLQueryVersion = NULL;
XNVCTRLQueryAttribute = NULL;
}

@ -0,0 +1,46 @@
// This is generated file. Do not modify directly.
// Path to the code generator: /home/crz/git/MangoHud/generate_library_loader.py .
#ifndef LIBRARY_LOADER_NVCTRL_H
#define LIBRARY_LOADER_NVCTRL_H
#define Bool bool
#include <X11/Xlib.h>
#include "NVCtrl/NVCtrlLib.h"
#define LIBRARY_LOADER_NVCTRL_H_DLOPEN
#include <string>
#include <dlfcn.h>
class libnvctrl_loader {
public:
libnvctrl_loader();
libnvctrl_loader(const std::string& library_name) : libnvctrl_loader() {
Load(library_name);
}
~libnvctrl_loader();
bool Load(const std::string& library_name);
bool IsLoaded() { return loaded_; }
decltype(&::XNVCTRLIsNvScreen) XNVCTRLIsNvScreen;
decltype(&::XNVCTRLQueryVersion) XNVCTRLQueryVersion;
decltype(&::XNVCTRLQueryAttribute) XNVCTRLQueryAttribute;
decltype(&::XNVCTRLQueryTargetStringAttribute) XNVCTRLQueryTargetStringAttribute;
decltype(&::XNVCTRLQueryTargetAttribute64) XNVCTRLQueryTargetAttribute64;
private:
void CleanUp(bool unload);
#if defined(LIBRARY_LOADER_NVCTRL_H_DLOPEN)
void* library_;
#endif
bool loaded_;
// Disallow copy constructor and assignment operator.
libnvctrl_loader(const libnvctrl_loader&);
void operator=(const libnvctrl_loader&);
};
#endif // LIBRARY_LOADER_NVCTRL_H

@ -161,6 +161,19 @@ bool libnvml_loader::Load(const std::string& library_name) {
return false;
}
#if defined(LIBRARY_LOADER_NVML_H_DLOPEN)
nvmlErrorString =
reinterpret_cast<decltype(this->nvmlErrorString)>(
dlsym(library_, "nvmlErrorString"));
#endif
#if defined(LIBRARY_LOADER_NVML_H_DT_NEEDED)
nvmlErrorString = &::nvmlErrorString;
#endif
if (!nvmlErrorString) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}

@ -35,6 +35,7 @@ class libnvml_loader {
decltype(&::nvmlDeviceGetHandleByPciBusId_v2) nvmlDeviceGetHandleByPciBusId_v2;
decltype(&::nvmlDeviceGetMemoryInfo) nvmlDeviceGetMemoryInfo;
decltype(&::nvmlDeviceGetClockInfo) nvmlDeviceGetClockInfo;
decltype(&::nvmlErrorString) nvmlErrorString;
private:
void CleanUp(bool unload);

@ -0,0 +1,91 @@
#include "loader_x11.h"
#include <iostream>
libx11_loader::libx11_loader() : loaded_(false) {
}
libx11_loader::~libx11_loader() {
CleanUp(loaded_);
}
bool libx11_loader::Load(const std::string& library_name) {
if (loaded_) {
return false;
}
library_ = dlopen(library_name.c_str(), RTLD_LAZY);
if (!library_) {
std::cerr << "MANGOHUD: " << library_name << " dlopen failed: " << dlerror() << std::endl;
return false;
}
XOpenDisplay =
reinterpret_cast<decltype(this->XOpenDisplay)>(
dlsym(library_, "XOpenDisplay"));
if (!XOpenDisplay) {
CleanUp(true);
return false;
}
XCloseDisplay =
reinterpret_cast<decltype(this->XCloseDisplay)>(
dlsym(library_, "XCloseDisplay"));
if (!XCloseDisplay) {
CleanUp(true);
return false;
}
XQueryKeymap =
reinterpret_cast<decltype(this->XQueryKeymap)>(
dlsym(library_, "XQueryKeymap"));
if (!XQueryKeymap) {
CleanUp(true);
return false;
}
XKeysymToKeycode =
reinterpret_cast<decltype(this->XKeysymToKeycode)>(
dlsym(library_, "XKeysymToKeycode"));
if (!XKeysymToKeycode) {
CleanUp(true);
return false;
}
XStringToKeysym =
reinterpret_cast<decltype(this->XStringToKeysym)>(
dlsym(library_, "XStringToKeysym"));
if (!XStringToKeysym) {
CleanUp(true);
return false;
}
XGetGeometry =
reinterpret_cast<decltype(this->XGetGeometry)>(
dlsym(library_, "XGetGeometry"));
if (!XGetGeometry) {
CleanUp(true);
return false;
}
loaded_ = true;
return true;
}
void libx11_loader::CleanUp(bool unload) {
if (unload) {
dlclose(library_);
library_ = NULL;
}
loaded_ = false;
XOpenDisplay = NULL;
XCloseDisplay = NULL;
XQueryKeymap = NULL;
XKeysymToKeycode = NULL;
XStringToKeysym = NULL;
XGetGeometry = NULL;
}
std::shared_ptr<libx11_loader> g_x11(new libx11_loader("libX11.so.6"));

@ -0,0 +1,36 @@
#pragma once
#include <X11/Xlib.h>
#include <memory>
#include <string>
#include <dlfcn.h>
class libx11_loader {
public:
libx11_loader();
libx11_loader(const std::string& library_name) { Load(library_name); }
~libx11_loader();
bool Load(const std::string& library_name);
bool IsLoaded() { return loaded_; }
decltype(&::XOpenDisplay) XOpenDisplay;
decltype(&::XCloseDisplay) XCloseDisplay;
decltype(&::XQueryKeymap) XQueryKeymap;
decltype(&::XKeysymToKeycode) XKeysymToKeycode;
decltype(&::XStringToKeysym) XStringToKeysym;
decltype(&::XGetGeometry) XGetGeometry;
private:
void CleanUp(bool unload);
void* library_ = nullptr;
bool loaded_ = false;
// Disallow copy constructor and assignment operator.
libx11_loader(const libx11_loader&);
void operator=(const libx11_loader&);
};
extern std::shared_ptr<libx11_loader> g_x11;

@ -37,7 +37,7 @@ void writeFile(string filename){
logArray.clear();
}
void *logging(void *params_void){
void logging(void *params_void){
overlay_params *params = reinterpret_cast<overlay_params *>(params_void);
time_t now_log = time(0);
tm *log_time = localtime(&now_log);
@ -65,5 +65,4 @@ void *logging(void *params_void){
}
writeFile(params->output_file + date);
return NULL;
}

@ -3,7 +3,7 @@
"layer" : {
"name": "@PROJECT_NAME@@LAYER_SUFFIX@",
"type": "GLOBAL",
"api_version": "1.1.125",
"api_version": "1.2.135",
"library_path": "@libdir_mangohud@libMangoHud.so",
"implementation_version": "1",
"description": "Vulkan Hud Overlay",

@ -6,7 +6,6 @@
#include <thread>
struct memory_information mem_info;
pthread_t memoryThread;
float memused, memmax;
FILE *open_file(const char *file, int *reported) {
@ -25,7 +24,7 @@ FILE *open_file(const char *file, int *reported) {
return fp;
}
void *update_meminfo(void *) {
void update_meminfo(void) {
FILE *meminfo_fp;
static int reported = 0;
@ -97,6 +96,4 @@ void *update_meminfo(void *) {
memmax = float(mem_info.memmax) / (1024 * 1024);
fclose(meminfo_fp);
pthread_detach(memoryThread);
return NULL;
}

@ -1,7 +1,6 @@
#include <stdio.h>
#include <thread>
extern pthread_t memoryThread;
extern float memused, memmax;
struct memory_information {
@ -12,5 +11,5 @@ struct memory_information {
unsigned long long bufmem, buffers, cached;
};
void *update_meminfo(void*);
void update_meminfo(void);
FILE *open_file(const char *file, int *reported);

@ -1,23 +1,3 @@
# Copyright © 2019 Intel Corporation
# 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.
glslang = find_program('glslangValidator')
# Needs prefix for configure_file()
@ -42,6 +22,7 @@ vklayer_files = files(
'overlay.cpp',
'overlay_params.cpp',
'font_unispace.c',
'blacklist.cpp',
'cpu.cpp',
'loaders/loader_nvml.cpp',
'nvml.cpp',
@ -52,19 +33,60 @@ vklayer_files = files(
'gpu.cpp',
'notify.cpp',
'elfhacks.cpp',
'real_dlsym.cpp',
)
opengl_files = files(
'gl/inject.cpp',
'gl/real_dlsym.cpp',
'gl/glad.c',
'gl/imgui_impl_opengl3.cpp',
'loaders/loader_gl.cpp',
'gl/gl3w/GL/gl3w.c',
'gl/imgui_hud.cpp',
'gl/inject_egl.cpp',
)
pre_args += '-DHOOK_DLSYM'
inc_opengl = include_directories('gl/gl3w')
# lib_xnvctrl = cc.find_library('XNVCtrl')
if get_option('with_dlsym').enabled()
pre_args += '-DHOOK_DLSYM'
endif
if get_option('with_xnvctrl').enabled()
if not get_option('with_x11').enabled()
error('XNVCtrl also needs \'with_x11\'')
endif
xnvctrl_h_found = cc.has_header('NVCtrl/NVCtrl.h')
if not xnvctrl_h_found
error('NVCtrl.h was not found. Disable with \'with_xnvctrl\' if this feature is not needed.')
endif
pre_args += '-DHAVE_XNVCTRL'
vklayer_files += files(
'loaders/loader_nvctrl.cpp',
'nvctrl.cpp',
)
endif
if get_option('with_x11').enabled()
pre_args += '-DHAVE_X11'
vklayer_files += files(
'loaders/loader_x11.cpp',
'shared_x11.cpp',
)
opengl_files += files(
'loaders/loader_glx.cpp',
'gl/inject_glx.cpp',
)
endif
if dbus_dep.found() and get_option('with_dbus').enabled()
pre_args += '-DHAVE_DBUS'
vklayer_files += files(
'dbus.cpp',
'loaders/loader_dbus.cpp',
)
endif
vklayer_mesa_overlay = shared_library(
'MangoHud',
util_files,
@ -72,7 +94,7 @@ vklayer_mesa_overlay = shared_library(
vklayer_files,
opengl_files,
overlay_spv,
vulkan_headers,
vk_enum_to_str,
c_args : [
pre_args,
c_vis_args,
@ -87,12 +109,36 @@ vklayer_mesa_overlay = shared_library(
],
dependencies : [
vulkan_wsi_deps,
libimgui_core_dep,
libimgui_core_dep,
dbus_dep,
dep_dl,
dep_pthread,
dep_vulkan],
include_directories : [inc_common, inc_opengl],
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro']),
include_directories : [inc_common],
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL']),
install_dir : libdir_mangohud,
install : true
)
mangohud_dlsym = shared_library(
'MangoHud_dlsym',
files(
'elfhacks.cpp',
'real_dlsym.cpp',
'hook_dlsym.cpp',
),
c_args : [
pre_args,
c_vis_args,
no_override_init_args,
],
cpp_args : [
pre_args,
cpp_vis_args,
],
dependencies : [dep_dl],
include_directories : [inc_common],
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro', '-Wl,--exclude-libs,ALL']),
install_dir : libdir_mangohud,
install : true
)

@ -1,35 +1,63 @@
#include <chrono>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include "config.h"
#include "notify.h"
pthread_t fileChange;
#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
void *fileChanged(void *params_void){
static void fileChanged(void *params_void) {
notify_thread *nt = reinterpret_cast<notify_thread *>(params_void);
int length, i = 0;
int fd;
int wd;
char buffer[EVENT_BUF_LEN];
fd = inotify_init();
wd = inotify_add_watch( fd, nt->params->config_file_path.c_str(), IN_MODIFY);
overlay_params local_params = *nt->params;
while (!nt->quit) {
length = read( fd, buffer, EVENT_BUF_LEN );
length = read( nt->fd, buffer, EVENT_BUF_LEN );
while (i < length) {
struct inotify_event *event =
(struct inotify_event *) &buffer[i];
i += EVENT_SIZE + event->len;
if (event->mask & IN_MODIFY) {
parse_overlay_config(&local_params, getenv("MANGOHUD_CONFIG"));
std::lock_guard<std::mutex> lk(nt->mutex);
parse_overlay_config(nt->params, getenv("MANGOHUD_CONFIG"));
*nt->params = local_params;
}
}
i = 0;
printf("File Changed\n");
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
return NULL;
}
}
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");
if (nt.wd < 0) {
close(nt.fd);
nt.fd = -1;
return false;
}
std::thread(fileChanged, &nt).detach();
return true;
}
void stop_notifier(notify_thread& nt)
{
if (nt.fd < 0)
return;
nt.quit = true;
inotify_rm_watch(nt.fd, nt.wd);
close(nt.fd);
nt.fd = -1;
}

@ -4,10 +4,11 @@
struct notify_thread
{
int fd = -1, wd = -1;
overlay_params *params = nullptr;
bool quit = false;
std::mutex mutex;
};
extern pthread_t fileChange;
extern void *fileChanged(void *params_void);
bool start_notifier(notify_thread& nt);
void stop_notifier(notify_thread& nt);

@ -0,0 +1,151 @@
#include <iostream>
#include <cstring>
#include <sstream>
#include <unordered_map>
#include <memory>
#include <functional>
#include "nvctrl.h"
#include "loaders/loader_nvctrl.h"
#include "loaders/loader_x11.h"
#include "string_utils.h"
typedef std::unordered_map<std::string, std::string> string_map;
static std::unique_ptr<Display, std::function<void(Display*)>> display;
libnvctrl_loader nvctrl("libXNVCtrl.so.0");
struct nvctrlInfo nvctrl_info;
bool nvctrlSuccess = false;
static bool find_nv_x11(Display*& dpy)
{
char buf[8] {};
for (int i = 0; i < 16; i++) {
snprintf(buf, sizeof(buf), ":%d", i);
Display *d = g_x11->XOpenDisplay(buf);
if (d) {
if (nvctrl.XNVCTRLIsNvScreen(d, 0)) {
dpy = d;
std::cerr << "MANGOHUD: XNVCtrl is using display " << buf << std::endl;
return true;
}
g_x11->XCloseDisplay(d);
}
}
return false;
}
bool checkXNVCtrl()
{
if (!g_x11->IsLoaded()) {
std::cerr << "MANGOHUD: XNVCtrl: X11 loader failed to load\n";
return false;
}
if (!nvctrl.IsLoaded()) {
std::cerr << "MANGOHUD: XNVCtrl loader failed to load\n";
return false;
}
Display *dpy;
nvctrlSuccess = find_nv_x11(dpy);
if (!nvctrlSuccess) {
std::cerr << "MANGOHUD: XNVCtrl didn't find the correct display" << std::endl;
return false;
}
auto local_x11 = g_x11;
display = { dpy,
[local_x11](Display *dpy) {
local_x11->XCloseDisplay(dpy);
}
};
return true;
}
static void parse_token(std::string token, string_map& options) {
std::string param, value;
size_t equal = token.find("=");
if (equal == std::string::npos)
return;
value = token.substr(equal+1);
param = token.substr(0, equal);
trim(param);
trim(value);
//std::cerr << __func__ << ": " << param << "=" << value << std::endl;
if (!param.empty())
options[param] = value;
}
char* get_attr_target_string(int attr, int target_type, int target_id) {
char* c = nullptr;
if (!nvctrl.XNVCTRLQueryTargetStringAttribute(display.get(), target_type, target_id, 0, attr, &c)) {
std::cerr << "Failed to query attribute '" << attr << "'.\n";
}
return c;
}
void getNvctrlInfo(){
string_map params;
std::string token;
if (!display)
return;
int enums[] = {
NV_CTRL_STRING_GPU_UTILIZATION,
NV_CTRL_STRING_GPU_CURRENT_CLOCK_FREQS,
0 // keep null
};
for (size_t i=0; enums[i]; i++) {
char* str = get_attr_target_string(enums[i], NV_CTRL_TARGET_TYPE_GPU, 0);
if (!str)
continue;
std::stringstream ss (str);
while (std::getline(ss, token, ',')) {
parse_token(token, params);
}
free(str);
}
if (!try_stoi(nvctrl_info.load, params["graphics"]))
nvctrl_info.load = 0;
if (!try_stoi(nvctrl_info.CoreClock, params["nvclock"]))
nvctrl_info.CoreClock = 0;
if (!try_stoi(nvctrl_info.MemClock, params["memclock"]))
nvctrl_info.MemClock = 0;
int64_t temp = 0;
nvctrl.XNVCTRLQueryTargetAttribute64(display.get(),
NV_CTRL_TARGET_TYPE_GPU,
0,
0,
NV_CTRL_GPU_CORE_TEMPERATURE,
&temp);
nvctrl_info.temp = temp;
int64_t memtotal = 0;
nvctrl.XNVCTRLQueryTargetAttribute64(display.get(),
NV_CTRL_TARGET_TYPE_GPU,
0,
0,
NV_CTRL_TOTAL_DEDICATED_GPU_MEMORY,
&memtotal);
nvctrl_info.memoryTotal = memtotal;
int64_t memused = 0;
nvctrl.XNVCTRLQueryTargetAttribute64(display.get(),
NV_CTRL_TARGET_TYPE_GPU,
0,
0,
NV_CTRL_USED_DEDICATED_GPU_MEMORY,
&memused);
nvctrl_info.memoryUsed = memused;
}

@ -0,0 +1,14 @@
struct nvctrlInfo{
int load;
int temp;
float memoryUsed;
float memoryTotal;
int MemClock;
int CoreClock;
};
extern struct nvctrlInfo nvctrl_info;
extern bool nvctrlSuccess;
bool checkXNVCtrl(void);
void getNvctrlInfo(void);
char *get_attr_target_string(int attr, int target_type, int target_id);

@ -1,5 +1,3 @@
#include <stdbool.h>
#include <stdio.h>
#include <nvml.h>
extern nvmlReturn_t result;
@ -9,5 +7,5 @@ extern struct nvmlUtilization_st nvidiaUtilization;
extern struct nvmlMemory_st nvidiaMemory;
extern bool nvmlSuccess;
bool checkNvidia(void);
void getNvidiaInfo(void);
bool checkNVML(const char* pciBusId);
bool getNVMLInfo(void);

@ -1,5 +1,6 @@
#include "loaders/loader_nvml.h"
#include "nvidia_info.h"
#include <iostream>
libnvml_loader nvml("libnvidia-ml.so.1");
@ -10,26 +11,41 @@ unsigned int nvidiaTemp, nvidiaCoreClock, nvidiaMemClock;
struct nvmlUtilization_st nvidiaUtilization;
struct nvmlMemory_st nvidiaMemory {};
bool checkNvidia(){
bool checkNVML(const char* pciBusId){
if (nvml.IsLoaded()){
result = nvml.nvmlInit();
if (NVML_SUCCESS != result) {
printf("MANGOHUD: Nvidia module not loaded\n");
std::cerr << "MANGOHUD: Nvidia module not loaded\n";
} else {
nvmlSuccess = true;
return true;
nvmlReturn_t ret = NVML_ERROR_UNKNOWN;
if (pciBusId && ((ret = nvml.nvmlDeviceGetHandleByPciBusId(pciBusId, &nvidiaDevice)) != NVML_SUCCESS)) {
std::cerr << "MANGOHUD: Getting device handle by PCI bus ID failed: " << nvml.nvmlErrorString(ret) << "\n";
std::cerr << " Using index 0.\n";
}
if (ret != NVML_SUCCESS)
ret = nvml.nvmlDeviceGetHandleByIndex(0, &nvidiaDevice);
if (ret != NVML_SUCCESS)
std::cerr << "MANGOHUD: Getting device handle failed: " << nvml.nvmlErrorString(ret) << "\n";
nvmlSuccess = (ret == NVML_SUCCESS);
return nvmlSuccess;
}
} else {
printf("Failed to load NVML!\n");
std::cerr << "MANGOHUD: Failed to load NVML\n";
}
return false;
}
void getNvidiaInfo(){
nvml.nvmlDeviceGetHandleByIndex(0, &nvidiaDevice);
nvml.nvmlDeviceGetUtilizationRates(nvidiaDevice, &nvidiaUtilization);
bool getNVMLInfo(){
nvmlReturn_t response;
response = nvml.nvmlDeviceGetUtilizationRates(nvidiaDevice, &nvidiaUtilization);
nvml.nvmlDeviceGetTemperature(nvidiaDevice, NVML_TEMPERATURE_GPU, &nvidiaTemp);
nvml.nvmlDeviceGetMemoryInfo(nvidiaDevice, &nvidiaMemory);
nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_GRAPHICS, &nvidiaCoreClock);
nvml.nvmlDeviceGetClockInfo(nvidiaDevice, NVML_CLOCK_MEM, &nvidiaMemClock);
if (response == NVML_ERROR_NOT_SUPPORTED)
nvmlSuccess = false;
return nvmlSuccess;
}

File diff suppressed because it is too large Load Diff

@ -4,14 +4,13 @@
#include "overlay_params.h"
#include "iostats.h"
extern std::string engineName;
struct frame_stat {
uint64_t stats[OVERLAY_PARAM_ENABLED_MAX];
uint64_t stats[OVERLAY_PLOTS_MAX];
};
struct swapchain_stats {
uint64_t n_frames;
enum overlay_param_enabled stat_selector;
enum overlay_plots stat_selector;
double time_dividor;
struct frame_stat stats_min, stats_max;
struct frame_stat frames_stats[200];
@ -27,12 +26,16 @@ struct swapchain_stats {
struct {
int32_t major;
int32_t minor;
bool is_gles;
} version_gl;
struct {
int32_t major;
int32_t minor;
int32_t patch;
} version_vk;
std::string engineName;
std::string engineVersion;
std::string deviceName;
};
struct fps_limit {
@ -53,4 +56,4 @@ void init_cpu_stats(overlay_params& params);
void check_keybinds(struct overlay_params& params);
void init_system_info(void);
void FpsLimiter(struct fps_limit& stats);
void imgui_custom_style(struct overlay_params& params);
void imgui_custom_style(struct overlay_params& params);

@ -1,33 +1,8 @@
/*
* Copyright © 2019 Intel Corporation
*
* 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 (including the next
* paragraph) 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.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/sysinfo.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#include <wordexp.h>
#include "imgui.h"
#include <iostream>
@ -38,6 +13,15 @@
#include "mesa/util/os_socket.h"
#ifdef HAVE_X11
#include <X11/keysym.h>
#include "loaders/loader_x11.h"
#endif
#ifdef HAVE_DBUS
#include "dbus_info.h"
#endif
static enum overlay_param_position
parse_position(const char *str)
@ -86,23 +70,35 @@ parse_alpha(const char *str)
return strtof(str, NULL);
}
#ifdef HAVE_X11
static KeySym
parse_toggle_hud(const char *str)
{
return XStringToKeysym(str);
if (g_x11->IsLoaded())
return g_x11->XStringToKeysym(str);
return 0;
}
static KeySym
parse_toggle_logging(const char *str)
{
return XStringToKeysym(str);
if (g_x11->IsLoaded())
return g_x11->XStringToKeysym(str);
return 0;
}
static KeySym
parse_reload_cfg(const char *str)
{
return XStringToKeysym(str);
if (g_x11->IsLoaded())
return g_x11->XStringToKeysym(str);
return 0;
}
#else
#define parse_toggle_hud(x) 0
#define parse_toggle_logging(x) 0
#define parse_reload_cfg(x) 0
#endif
static uint32_t
parse_fps_sampling_period(const char *str)
@ -185,6 +181,7 @@ parse_path(const char *str)
#define parse_font_file(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_crosshair_color(s) parse_color(s)
#define parse_cpu_color(s) parse_color(s)
@ -331,9 +328,6 @@ parse_overlay_config(struct overlay_params *params,
params->width = 280;
params->height = 140;
params->control = -1;
params->toggle_hud = XK_F12;
params->toggle_logging = XK_F2;
params->reload_cfg = XK_F4;
params->fps_limit = 0;
params->vsync = -1;
params->gl_vsync = -2;
@ -353,6 +347,12 @@ parse_overlay_config(struct overlay_params *params,
params->background_color = strtol("020202", NULL, 16);
params->text_color = strtol("ffffff", NULL, 16);
#ifdef HAVE_X11
params->toggle_hud = XK_F12;
params->toggle_logging = XK_F2;
params->reload_cfg = XK_F4;
#endif
// first pass with env var
if (env)
parse_overlay_env(params, env);
@ -397,8 +397,6 @@ parse_overlay_config(struct overlay_params *params,
if (env && read_cfg)
parse_overlay_env(params, env);
// Command buffer gets reused and timestamps cause hangs for some reason, force off for now
params->enabled[OVERLAY_PARAM_ENABLED_gpu_timing] = false;
// Convert from 0xRRGGBB to ImGui's format
std::array<unsigned *, 10> colors = {
&params->crosshair_color,
@ -439,6 +437,21 @@ parse_overlay_config(struct overlay_params *params,
}
// set frametime limit
if (params->fps_limit > 0)
if (params->fps_limit >= 0)
fps_limit_stats.targetFrameTime = int64_t(1000000000.0 / params->fps_limit);
}
#ifdef HAVE_DBUS
if (params->enabled[OVERLAY_PARAM_ENABLED_media_player]) {
try {
dbusmgr::dbus_mgr.init();
get_spotify_metadata(dbusmgr::dbus_mgr, spotify);
} catch (std::runtime_error& e) {
std::cerr << "Failed to get initial Spotify metadata: " << e.what() << std::endl;
}
} else {
dbusmgr::dbus_mgr.deinit();
spotify.valid = false;
}
#endif
}

@ -1,26 +1,3 @@
/*
* Copyright © 2019 Intel Corporation
*
* 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 (including the next
* paragraph) 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.
*/
#ifndef OVERLAY_PARAMS_H
#define OVERLAY_PARAMS_H
@ -34,7 +11,10 @@ extern "C" {
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <X11/Xlib.h>
#ifndef KeySym
typedef unsigned long KeySym;
#endif
#define RGBGetBValue(rgb) (rgb & 0x000000FF)
#define RGBGetGValue(rgb) ((rgb >> 8) & 0x000000FF)
@ -44,10 +24,7 @@ extern "C" {
#define OVERLAY_PARAMS \
OVERLAY_PARAM_BOOL(fps) \
OVERLAY_PARAM_BOOL(frame) \
OVERLAY_PARAM_BOOL(frame_timing) \
OVERLAY_PARAM_BOOL(present_timing) \
OVERLAY_PARAM_BOOL(gpu_timing) \
OVERLAY_PARAM_BOOL(core_load) \
OVERLAY_PARAM_BOOL(cpu_temp) \
OVERLAY_PARAM_BOOL(gpu_temp) \
@ -63,6 +40,9 @@ extern "C" {
OVERLAY_PARAM_BOOL(io_write) \
OVERLAY_PARAM_BOOL(gpu_mem_clock) \
OVERLAY_PARAM_BOOL(gpu_core_clock) \
OVERLAY_PARAM_BOOL(arch) \
OVERLAY_PARAM_BOOL(media_player) \
OVERLAY_PARAM_BOOL(version) \
OVERLAY_PARAM_CUSTOM(fps_sampling_period) \
OVERLAY_PARAM_CUSTOM(output_file) \
OVERLAY_PARAM_CUSTOM(font_file) \
@ -97,6 +77,7 @@ extern "C" {
OVERLAY_PARAM_CUSTOM(text_color) \
OVERLAY_PARAM_CUSTOM(alpha) \
OVERLAY_PARAM_CUSTOM(log_duration) \
OVERLAY_PARAM_CUSTOM(pci_dev) \
OVERLAY_PARAM_CUSTOM(help)
enum overlay_param_position {
@ -106,6 +87,11 @@ enum overlay_param_position {
LAYER_POSITION_BOTTOM_RIGHT,
};
enum overlay_plots {
OVERLAY_PLOTS_frame_timing,
OVERLAY_PLOTS_MAX,
};
enum overlay_param_enabled {
#define OVERLAY_PARAM_BOOL(name) OVERLAY_PARAM_ENABLED_##name,
#define OVERLAY_PARAM_CUSTOM(name)
@ -140,6 +126,7 @@ struct overlay_params {
KeySym toggle_logging;
KeySym reload_cfg;
std::string time_format, output_file, font_file;
std::string pci_dev;
std::string config_file_path;
std::unordered_map<std::string,std::string> options;

@ -4,6 +4,7 @@
* For conditions of distribution and use, see copyright notice in elfhacks.h
*/
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include "real_dlsym.h"
@ -57,7 +58,9 @@ void *real_dlopen(const char *filename, int flag)
FLAG(RTLD_LOCAL)
FLAG(RTLD_NODELETE)
FLAG(RTLD_NOLOAD)
#ifdef RTLD_DEEPBIND
FLAG(RTLD_DEEPBIND)
#endif
#undef FLAG
printf(") = %p\n", result);
}
@ -81,3 +84,34 @@ void *real_dlsym(void *handle, const char *symbol)
return result;
}
void* get_proc_address(const char* name) {
void (*func)() = (void (*)())real_dlsym(RTLD_NEXT, name);
return (void*)func;
}
#ifdef HOOK_DLSYM
EXPORT_C_(void *) mangohud_find_glx_ptr(const char *name);
EXPORT_C_(void *) mangohud_find_egl_ptr(const char *name);
EXPORT_C_(void*) dlsym(void * handle, const char * name)
{
void* func = nullptr;
#ifdef HAVE_X11
func = mangohud_find_glx_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
return func;
}
#endif
func = mangohud_find_egl_ptr(name);
if (func) {
//fprintf(stderr,"%s: local: %s\n", __func__ , name);
return func;
}
//fprintf(stderr,"%s: foreign: %s\n", __func__ , name);
return real_dlsym(handle, name);
}
#endif

@ -0,0 +1,7 @@
#pragma once
#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);

@ -0,0 +1,45 @@
#include "shared_x11.h"
#include "loaders/loader_x11.h"
#include <cstdlib>
#include <iostream>
#include <memory>
#include <functional>
static std::unique_ptr<Display, std::function<void(Display*)>> display;
bool init_x11() {
static bool failed = false;
if (failed)
return false;
if (display)
return true;
if (!g_x11->IsLoaded()) {
std::cerr << "MANGOHUD: X11 loader failed to load\n";
failed = true;
return false;
}
const char *displayid = getenv("DISPLAY");
if (displayid) {
auto local_x11 = g_x11;
display = { g_x11->XOpenDisplay(displayid),
[local_x11](Display* dpy) {
if (dpy)
local_x11->XCloseDisplay(dpy);
}
};
}
failed = !display;
if (failed)
std::cerr << "MANGOHUD: XOpenDisplay failed to open display '" << displayid << "'\n";
return !!display;
}
Display* get_xdisplay()
{
return display.get();
}

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

@ -84,7 +84,9 @@ static bool try_stoi(int& val, const std::string& str, std::size_t* pos = 0, int
val = std::stoi(str, pos, base);
return true;
} catch (std::invalid_argument& e) {
#ifndef NDEBUG
std::cerr << __func__ << ": invalid argument: '" << str << "'" << std::endl;
#endif
}
return false;
}
@ -95,7 +97,9 @@ static bool try_stoull(unsigned long long& val, const std::string& str, std::siz
val = std::stoull(str, pos, base);
return true;
} catch (std::invalid_argument& e) {
#ifndef NDEBUG
std::cerr << __func__ << ": invalid argument: '" << str << "'" << std::endl;
#endif
}
return false;
}

@ -0,0 +1,10 @@
[wrap-file]
directory = Vulkan-Headers-1.2.136
source_url = https://github.com/KhronosGroup/Vulkan-Headers/archive/v1.2.136.tar.gz
source_filename = vulkanheaders-1.2.136.tar.gz
source_hash = d67e61ade037906d76ae4f1a6d5adf38008b30783774a5957a84527f3a5ebdb4
patch_url = https://github.com/stephanlachnit/vulkanheaders-wrap/releases/download/v1.2.136/v1.2.136.zip
patch_filename = vulkanheaders-1.2.136-wrap.zip
patch_hash = 4b0e8ba1f37458b660f4d3ed3f5259ca5b0bd46ef309cc3ccaf22a2a203bda49

@ -0,0 +1,3 @@
#pragma once
#define MANGOHUD_VERSION "@VCS_TAG@"
Loading…
Cancel
Save