Merge branch 'develop'
commit
2177aa35dc
@ -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_ */
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
@ -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);
|
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(¬ifier, [](notify_thread *n){ stop_notifier(*n); });
|
||||||
|
|
||||||
|
void imgui_init()
|
||||||
|
{
|
||||||
|
if (cfg_inited)
|
||||||
|
return;
|
||||||
|
parse_overlay_config(¶ms, getenv("MANGOHUD_CONFIG"));
|
||||||
|
notifier.params = ¶ms;
|
||||||
|
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, ¤t_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
|
@ -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(¬ifier, [](notify_thread *n){ n->quit = true; });
|
|
||||||
|
|
||||||
void imgui_init()
|
|
||||||
{
|
|
||||||
if (cfg_inited)
|
|
||||||
return;
|
|
||||||
parse_overlay_config(¶ms, getenv("MANGOHUD_CONFIG"));
|
|
||||||
notifier.params = ¶ms;
|
|
||||||
pthread_create(&fileChange, NULL, &fileChanged, ¬ifier);
|
|
||||||
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, ¤t_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* );
|
|
@ -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);
|
||||||
|
}
|
@ -1,18 +1,24 @@
|
|||||||
#include <X11/Xlib.h>
|
#pragma once
|
||||||
#include <iostream>
|
#include "shared_x11.h"
|
||||||
#include "X11/keysym.h"
|
#include "loaders/loader_x11.h"
|
||||||
#include <functional>
|
|
||||||
|
#ifndef KeySym
|
||||||
|
typedef unsigned long KeySym;
|
||||||
|
#endif
|
||||||
|
|
||||||
double elapsedF2, elapsedF12, elapsedReloadCfg;
|
double elapsedF2, elapsedF12, elapsedReloadCfg;
|
||||||
uint64_t last_f2_press, last_f12_press, reload_cfg_press;
|
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) {
|
bool key_is_pressed(KeySym ks) {
|
||||||
|
|
||||||
|
if (!init_x11())
|
||||||
|
return false;
|
||||||
|
|
||||||
char keys_return[32];
|
char keys_return[32];
|
||||||
XQueryKeymap(dpy.get(), keys_return);
|
g_x11->XQueryKeymap(get_xdisplay(), keys_return);
|
||||||
KeyCode kc2 = XKeysymToKeycode(dpy.get(), ks);
|
KeyCode kc2 = g_x11->XKeysymToKeycode(get_xdisplay(), ks);
|
||||||
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
|
bool isPressed = !!(keys_return[kc2 >> 3] & (1 << (kc2 & 7)));
|
||||||
return isPressed;
|
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
|
@ -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;
|
@ -1,35 +1,63 @@
|
|||||||
|
#include <chrono>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/inotify.h>
|
#include <sys/inotify.h>
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "notify.h"
|
#include "notify.h"
|
||||||
|
|
||||||
pthread_t fileChange;
|
|
||||||
|
|
||||||
#define EVENT_SIZE ( sizeof (struct inotify_event) )
|
#define EVENT_SIZE ( sizeof (struct inotify_event) )
|
||||||
#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )
|
#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);
|
notify_thread *nt = reinterpret_cast<notify_thread *>(params_void);
|
||||||
int length, i = 0;
|
int length, i = 0;
|
||||||
int fd;
|
|
||||||
int wd;
|
|
||||||
char buffer[EVENT_BUF_LEN];
|
char buffer[EVENT_BUF_LEN];
|
||||||
fd = inotify_init();
|
overlay_params local_params = *nt->params;
|
||||||
wd = inotify_add_watch( fd, nt->params->config_file_path.c_str(), IN_MODIFY);
|
|
||||||
while (!nt->quit) {
|
while (!nt->quit) {
|
||||||
length = read( fd, buffer, EVENT_BUF_LEN );
|
length = read( nt->fd, buffer, EVENT_BUF_LEN );
|
||||||
while (i < length) {
|
while (i < length) {
|
||||||
struct inotify_event *event =
|
struct inotify_event *event =
|
||||||
(struct inotify_event *) &buffer[i];
|
(struct inotify_event *) &buffer[i];
|
||||||
i += EVENT_SIZE + event->len;
|
i += EVENT_SIZE + event->len;
|
||||||
if (event->mask & IN_MODIFY) {
|
if (event->mask & IN_MODIFY) {
|
||||||
|
parse_overlay_config(&local_params, getenv("MANGOHUD_CONFIG"));
|
||||||
std::lock_guard<std::mutex> lk(nt->mutex);
|
std::lock_guard<std::mutex> lk(nt->mutex);
|
||||||
parse_overlay_config(nt->params, getenv("MANGOHUD_CONFIG"));
|
*nt->params = local_params;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i = 0;
|
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;
|
||||||
}
|
}
|
@ -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);
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
@ -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…
Reference in New Issue