diff --git a/.gitignore b/.gitignore
index cae94d71..50e3374e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -38,7 +38,6 @@ lib32-mangohud*.tar.*
# subprojects
subprojects/packagecache/
-subprojects/imgui-*/
subprojects/Vulkan-Headers-*/
#GNU Global Metadata
diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index 95df703c..00000000
--- a/.gitmodules
+++ /dev/null
@@ -1,3 +0,0 @@
-[submodule "modules/ImGui/src"]
- path = modules/ImGui/src
- url = https://github.com/flightlessmango/imgui.git
diff --git a/README.md b/README.md
index 7cec0bbd..38f82a09 100644
--- a/README.md
+++ b/README.md
@@ -136,6 +136,11 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `ram`
`vram` | Displays system RAM/VRAM usage |
| `full` | Enables most of the toggleable parameters (currently excludes `histogram`) |
| `font_size=` | Customizeable font size (default=24) |
+| `font_size_text=` | Customizeable font size for other text like media metadata (default=24) |
+| `font_scale=` | Set global font scale (default=1.0) |
+| `font_file` | Change default font (set location to .TTF/.OTF file ) |
+| `font_file_text` | Change text font. Otherwise `font_file` is used |
+| `font_glyph_ranges` | Specify extra font glyph ranges, comma separated: `korean`, `chinese`, `chinese_simplified`, `japanese`, `cyrillic`, `thai`, `vietnamese`, `latin_ext_a`, `latin_ext_b`. If you experience crashes or text is just squares, reduce font size or glyph ranges. |
| `width=`
`height=` | Customizeable hud dimensions (in pixels) |
| `position=` | Location of the hud: `top-left` (default), `top-right`, `bottom-left`, `bottom-right`, `top-center` |
| `offset_x` `offset_y` | Hud position offsets |
@@ -148,12 +153,12 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `background_alpha` | Set the opacity of the background `0.0-1.0` |
| `read_cfg` | Add to MANGOHUD_CONFIG as first parameter to also load config file. Otherwise only MANGOHUD_CONFIG parameters are used. |
| `output_file` | Define name and location of the output file (Required for logging) |
-| `font_file` | Change default font (set location to .TTF/.OTF file ) |
| `log_duration` | Set amount of time the logging will run for (in seconds) |
| `vsync`
`gl_vsync` | Set vsync for OpenGL or Vulkan |
| `media_player` | Show media player metadata |
-| `media_player_name` | Set main media player DBus service name without the `org.mpris.MediaPlayer2` part, like `spotify`, `vlc`, `audacious` or `cantata`. Defaults to `spotify`. |
-| `font_scale_media_player` | Change size of media player text relative to font_size |
+| `media_player_name` | Force media player DBus service name without the `org.mpris.MediaPlayer2` part, like `spotify`, `vlc`, `audacious` or `cantata`. If none is set, MangoHud tries to switch between currently playing players. |
+| `media_player_order` | Media player metadata field order. Defaults to `title,artist,album`. |
+| `font_scale_media_player` | Change size of media player text relative to font_size |
| `io_read`
`io_write` | Show non-cached IO read/write, in MiB/s |
| `pci_dev` | Select GPU device in multi-gpu setups |
| `version` | Shows current mangohud version |
@@ -166,6 +171,9 @@ Parameters that are enabled by default have to be explicitly disabled. These (cu
| `gpu_name` | Displays GPU name from pci.ids |
| `gpu_power` | Display GPU draw in watts |
| `engine_version` | Display OpenGL or vulkan and vulkan-based render engine's version |
+| `permit_upload` | Allow uploading of logs to Flightlessmango.com |
+| `upload_log` | Change keybind for uploading log |
+| `benchmark_percentiles` | Configure which framerate percentiles are shown in the logging summary. Default is `97+AVG+1+0.1` |
Example: `MANGOHUD_CONFIG=cpu_temp,gpu_temp,position=top-right,height=500,font_size=32`
diff --git a/bin/MangoHud.conf b/bin/MangoHud.conf
index 02e93935..9038a494 100644
--- a/bin/MangoHud.conf
+++ b/bin/MangoHud.conf
@@ -48,8 +48,22 @@ frame_timing
### Change the hud font size (default is 24)
font_size=24
+# font_scale=1.0
+# font_size_text=24
# font_scale_media_player = 0.55
+### Change default font (set location to .TTF/.OTF file )
+## Set font for the whole hud
+# font_file=
+
+## Set font only for text like media player metadata
+# font_file_text=
+
+## Set font glyph ranges. Defaults to latin-only. Don't forget to set font_file/text_font_file to font that supports these.
+## Probably don't enable all at once because of memory usage and hardware limits concerns.
+## If you experience crashes or text is just squares, reduce glyph range or reduce font size.
+# font_glyph_ranges=korean, chinese, chinese_simplified, japanese, cyrillic, thai, vietnamese, latin_ext_a, latin_ext_b
+
### Change the hud position (default is top-left)
position=top-left
@@ -91,12 +105,10 @@ background_alpha=0.5
# background_color=020202
# media_player_color=FFFFFF
-### Change default font (set location to .TTF/.OTF file )
-# font_file
-
### Show media player metadata
# media_player
# media_player_name = spotify
+# media_player_order = title,artist,album
### Specify gpu with pci bus id for amdgpu and NVML stats.
### Set to 'domain:bus:slot.function'
@@ -108,6 +120,7 @@ background_alpha=0.5
#toggle_hud=Shift_R+F12
#toggle_logging=Shift_L+F2
#reload_cfg=Shift_L+F4
+#upload_log=Shift+F3
################## LOG #################
@@ -115,3 +128,8 @@ background_alpha=0.5
# log_duration
### Define name and location of the output file (Required for logging)
# output_file
+### Permit uploading logs directly to Flightlessmango.com
+# permit_upload=1
+### Define a '+'-separated list of percentiles shown in the benchmark results.
+### Use "AVG" to get a mean average. Default percentiles are 97+AVG+1+0.1
+# benchmark_percentiles=
\ No newline at end of file
diff --git a/bin/gen_enum_to_str.py b/bin/gen_enum_to_str.py
index 2f2a7f10..81a84e49 100644
--- a/bin/gen_enum_to_str.py
+++ b/bin/gen_enum_to_str.py
@@ -25,7 +25,7 @@ from __future__ import print_function
import argparse
import os
import textwrap
-import xml.etree.cElementTree as et
+import xml.etree.ElementTree as et
from mako.template import Template
diff --git a/bin/mangohud-setup.sh b/bin/mangohud-setup.sh
index 838d5458..43f60e89 100755
--- a/bin/mangohud-setup.sh
+++ b/bin/mangohud-setup.sh
@@ -1,3 +1,4 @@
+#!/usr/bin/env bash
XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
MANGOHUD_CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
SU_CMD=$(command -v sudo || command -v doas)
diff --git a/build-source.sh b/build-source.sh
index 3ef3af58..7030e1af 100755
--- a/build-source.sh
+++ b/build-source.sh
@@ -3,12 +3,8 @@
VERSION=$(git describe --tags --dirty)
NAME=MangoHud-$VERSION-Source
-# ensure that submodules are present
-git submodule update --init
-# get everything except submodules
+# create archive via git
git archive HEAD --format=tar --prefix=${NAME}/ --output=${NAME}.tar
-# add imgui submodule
-tar -rf ${NAME}.tar --exclude-vcs --transform="s,^modules/ImGui/src,${NAME}/modules/ImGui/src," modules/ImGui/src
# create DFSG compliant version which excludes NVML
cp ${NAME}.tar ${NAME}-DFSG.tar
tar -f ${NAME}-DFSG.tar --delete ${NAME}/include/nvml.h
diff --git a/build-srt.sh b/build-srt.sh
new file mode 100755
index 00000000..b8938e8b
--- /dev/null
+++ b/build-srt.sh
@@ -0,0 +1,185 @@
+#!/usr/bin/env bash
+set -e
+
+# Specialized build script for Steam Runtime SDK docker
+
+OS_RELEASE_FILES=("/etc/os-release" "/usr/lib/os-release")
+XDG_DATA_HOME="${XDG_DATA_HOME:-$HOME/.local/share}"
+XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-$HOME/.config}"
+DATA_DIR="$XDG_DATA_HOME/MangoHud"
+CONFIG_DIR="$XDG_CONFIG_HOME/MangoHud"
+LAYER="build/release/usr/share/vulkan/implicit_layer.d/mangohud.json"
+INSTALL_DIR="build/package/"
+IMPLICIT_LAYER_DIR="$XDG_DATA_HOME/vulkan/implicit_layer.d"
+VERSION=$(git describe --long --tags --always | sed 's/\([^-]*-g\)/r\1/;s/-/./g;s/^v//')
+
+dependencies() {
+
+ if [[ ! -f build/release/usr/lib64/libMangoHud.so ]]; then
+ install() {
+ set +e
+ for i in $(eval echo $DEPS); do
+ dpkg-query -s "$i" &> /dev/null
+ if [[ $? == 1 ]]; then
+ INSTALL="$INSTALL""$i "
+ fi
+ done
+ if [[ ! -z "$INSTALL" ]]; then
+ apt-get update
+ apt-get -y install $INSTALL
+ fi
+ set -e
+ }
+
+ echo "# Checking Dependencies"
+ DEPS="{gcc-5-multilib,g++-5-multilib,unzip}"
+ install
+
+ # py3.2 is weird
+ ln -sf python3.5 /usr/bin/python3
+
+ if [[ ! -f ./bin/get-pip.py ]]; then
+ curl https://bootstrap.pypa.io/get-pip.py -o bin/get-pip.py
+ python3.5 ./bin/get-pip.py
+ fi
+
+ if [[ $(pip3.5 show meson; echo $?) == 1 || $(pip3.5 show mako; echo $?) == 1 ]]; then
+ pip3.5 install meson mako
+ fi
+
+ if [[ ! -f /usr/include/NVCtrl/NVCtrl.h ]]; then
+ curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl0_440.64-0ubuntu1_amd64.deb
+ curl -LO http://mirrors.kernel.org/ubuntu/pool/main/n/nvidia-settings/libxnvctrl-dev_440.64-0ubuntu1_amd64.deb
+ dpkg -i libxnvctrl0_440.64-0ubuntu1_amd64.deb libxnvctrl-dev_440.64-0ubuntu1_amd64.deb
+ fi
+
+ # preinstalled 7.10.xxxx
+ #if [[ ! -f /usr/local/bin/glslangValidator ]]; then
+ # curl -LO https://github.com/KhronosGroup/glslang/releases/download/master-tot/glslang-master-linux-Release.zip
+ # unzip glslang-master-linux-Release.zip bin/glslangValidator
+ # /usr/bin/install -m755 bin/glslangValidator /usr/local/bin/
+ # rm bin/glslangValidator glslang-master-linux-Release.zip
+ #fi
+ fi
+}
+
+configure() {
+ dependencies
+ git submodule update --init
+ if [[ ! -f "build/meson64/build.ninja" ]]; then
+ export CC="gcc-5"
+ export CXX="g++-5"
+ meson build/meson64 --libdir lib/mangohud/lib64 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
+ fi
+ if [[ ! -f "build/meson32/build.ninja" ]]; then
+ export CC="gcc-5 -m32"
+ export CXX="g++-5 -m32"
+ export PKG_CONFIG_PATH="/usr/lib32/pkgconfig:/usr/lib/i386-linux-gnu/pkgconfig:/usr/lib/pkgconfig:${PKG_CONFIG_PATH_32}"
+ export LLVM_CONFIG="/usr/bin/llvm-config32"
+ meson build/meson32 --libdir lib/mangohud/lib32 --prefix /usr -Dappend_libdir_mangohud=false ${CONFIGURE_OPTS}
+ fi
+}
+
+build() {
+ if [[ ! -f "build/meson64/build.ninja" || ! -f "build/meson32/build.ninja" ]]; then
+ configure
+ fi
+ DESTDIR="$PWD/build/release" ninja -C build/meson32 install
+ DESTDIR="$PWD/build/release" ninja -C build/meson64 install
+}
+
+package() {
+ LIB="build/release/usr/lib/mangohud/lib64/libMangoHud.so"
+ LIB32="build/release/usr/lib/mangohud/lib32/libMangoHud.so"
+ if [[ ! -f "$LIB" || "$LIB" -ot "build/meson64/src/libMangoHud.so" ]]; then
+ build
+ fi
+ tar --numeric-owner --owner=0 --group=0 \
+ -C build/release -cvf "build/MangoHud-package.tar" .
+}
+
+release() {
+ rm build/MangoHud-package.tar
+ mkdir -p build/MangoHud
+ package
+ cp --preserve=mode bin/mangohud-setup.sh build/MangoHud/mangohud-setup.sh
+ cp build/MangoHud-package.tar build/MangoHud/MangoHud-package.tar
+ tar --numeric-owner --owner=0 --group=0 \
+ -C build -czvf build/MangoHud-$VERSION.tar.gz MangoHud
+}
+
+install() {
+ rm -rf "$HOME/.local/share/MangoHud/"
+ rm -f "$HOME/.local/share/vulkan/implicit_layer.d/"{mangohud32.json,mangohud64.json}
+
+ [ "$UID" -eq 0 ] || mkdir -pv "${CONFIG_DIR}"
+ [ "$UID" -eq 0 ] || exec sudo bash "$0" install
+
+ /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud.so /usr/lib/mangohud/lib32/libMangoHud.so
+ /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud.so /usr/lib/mangohud/lib64/libMangoHud.so
+ /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib32/libMangoHud_dlsym.so /usr/lib/mangohud/lib32/libMangoHud_dlsym.so
+ /usr/bin/install -vm644 -D ./build/release/usr/lib/mangohud/lib64/libMangoHud_dlsym.so /usr/lib/mangohud/lib64/libMangoHud_dlsym.so
+ /usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86.json
+ /usr/bin/install -vm644 -D ./build/release/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json /usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json
+ /usr/bin/install -vm644 -D ./build/release/usr/share/doc/mangohud/MangoHud.conf.example /usr/share/doc/mangohud/MangoHud.conf.example
+
+ /usr/bin/install -vm755 ./build/release/usr/bin/mangohud.x86 /usr/bin/mangohud.x86
+ /usr/bin/install -vm755 ./build/release/usr/bin/mangohud /usr/bin/mangohud
+
+ echo "MangoHud Installed"
+}
+
+clean() {
+ rm -rf "build"
+}
+
+uninstall() {
+ [ "$UID" -eq 0 ] || exec sudo bash "$0" uninstall
+ rm -rfv "/usr/lib/mangohud"
+ rm -rfv "/usr/share/doc/mangohud"
+ rm -fv "/usr/share/vulkan/implicit_layer.d/mangohud.json"
+ rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.json"
+ rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86.json"
+ rm -fv "/usr/share/vulkan/implicit_layer.d/MangoHud.x86_64.json"
+ rm -fv "/usr/bin/mangohud"
+ rm -fv "/usr/bin/mangohud.x86"
+}
+
+usage() {
+ if test -z $1; then
+ echo "Unrecognized command argument: $a"
+ else
+ echo "$0 requires one argument"
+ fi
+ echo -e "\nUsage: $0 \n"
+ echo "Available commands:"
+ echo -e "\tpull\t\tPull latest commits (code) from Git"
+ echo -e "\tconfigure\tEnsures that dependencies are installed, updates git submodules, and generates files needed for building MangoHud. This is automatically run by the build command"
+ echo -e "\tbuild\t\tIf needed runs configure and then builds (compiles) MangoHud"
+ echo -e "\tpackage\t\tRuns build if needed and then builds a tar package from MangoHud"
+ echo -e "\tinstall\t\tInstall MangoHud onto your system"
+ echo -e "\tclean\t\tRemoves build directory"
+ echo -e "\tuninstall\tRemoves installed MangoHud files from your system"
+ echo -e "\trelease\t\tBuilds a MangoHud release tar package"
+}
+
+for a in $@; do
+ case $a in
+ "") build;;
+ "pull") git pull;;
+ "configure") configure;;
+ "build") build;;
+ "package") package;;
+ "install") install;;
+ "clean") clean;;
+ "uninstall") uninstall;;
+ "release") release;;
+ *)
+ usage
+ esac
+done
+
+if [[ -z $@ ]]; then
+ usage no-args
+fi
+
diff --git a/build.sh b/build.sh
index 3f23f019..bd35c0a0 100755
--- a/build.sh
+++ b/build.sh
@@ -62,7 +62,7 @@ dependencies() {
"Fedora")
MANAGER_QUERY="dnf list installed"
MANAGER_INSTALL="dnf install"
- DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel}"
+ DEPS="{meson,gcc,gcc-c++,libX11-devel,glslang,python3-mako,mesa-libGL-devel,libXNVCtrl-devel,dbus-devel}"
dep_install
unset INSTALL
@@ -237,6 +237,7 @@ usage() {
echo -e "\tbuild\t\tIf needed runs configure and then builds (compiles) MangoHud"
echo -e "\tpackage\t\tRuns build if needed and then builds a tar package from MangoHud"
echo -e "\tinstall\t\tInstall MangoHud onto your system"
+ echo -e "\treinstall\tRuns build, then package, and finally install"
echo -e "\tclean\t\tRemoves build directory"
echo -e "\tuninstall\tRemoves installed MangoHud files from your system"
echo -e "\trelease\t\tBuilds a MangoHud release tar package"
diff --git a/meson.build b/meson.build
index 0f028cde..01f210ac 100644
--- a/meson.build
+++ b/meson.build
@@ -120,11 +120,6 @@ foreach a : ['missing-field-initializers', 'format-truncation']
endif
endforeach
-c_vis_args = []
-if cc.has_argument('-fvisibility=hidden')
- c_vis_args += '-fvisibility=hidden'
-endif
-
# Check for generic C++ arguments
cpp_args = []
foreach a : ['-Werror=return-type',
@@ -151,11 +146,6 @@ foreach a : ['override-init', 'initializer-overrides']
endif
endforeach
-cpp_vis_args = []
-if cpp.has_argument('-fvisibility=hidden')
- cpp_vis_args += '-fvisibility=hidden'
-endif
-
foreach a : pre_args
add_project_arguments(a, language : ['c', 'cpp'])
endforeach
@@ -173,6 +163,13 @@ else
dep_dl = cc.find_library('dl')
endif
+# check for linking with rt by default
+if cc.has_function('clock_gettime')
+ dep_rt = null_dep
+else
+ dep_rt = cc.find_library('rt')
+endif
+
if dep_vulkan.found()
datadir = get_option('datadir')
if not datadir.startswith('/')
@@ -207,5 +204,7 @@ elif sizeof_ptr == 4
pre_args += '-DMANGOHUD_ARCH="32bit"'
endif
-subdir('modules/ImGui')
+dearimgui_sp = subproject('dearimgui')
+dearimgui_dep = dearimgui_sp.get_variable('dearimgui_dep')
+
subdir('src')
diff --git a/modules/.editorconfig b/modules/.editorconfig
deleted file mode 100644
index bfb5adaf..00000000
--- a/modules/.editorconfig
+++ /dev/null
@@ -1,3 +0,0 @@
-# ignore this folder
-root = true
-
diff --git a/modules/ImGui/meson.build b/modules/ImGui/meson.build
deleted file mode 100644
index 7066f0be..00000000
--- a/modules/ImGui/meson.build
+++ /dev/null
@@ -1,11 +0,0 @@
-libimgui_core = static_library(
- 'imgui_core',
- files('src/imgui.cpp', 'src/imgui_draw.cpp', 'src/imgui_widgets.cpp'),
- cpp_args : ['-w'],
- install : false
-)
-
-libimgui_core_dep = declare_dependency(
- link_with : libimgui_core,
- include_directories : include_directories('src')
-)
diff --git a/modules/ImGui/src b/modules/ImGui/src
deleted file mode 160000
index 1f02d240..00000000
--- a/modules/ImGui/src
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1f02d240b38f445abb0381ade0867752d5d2bc7b
diff --git a/src/blacklist.cpp b/src/blacklist.cpp
index 63e374d6..661eb4f8 100644
--- a/src/blacklist.cpp
+++ b/src/blacklist.cpp
@@ -53,7 +53,9 @@ static bool check_blacklisted() {
return blacklisted;
}
-bool is_blacklisted() {
+bool is_blacklisted(bool force_recheck) {
static bool blacklisted = check_blacklisted();
+ if (force_recheck)
+ blacklisted = check_blacklisted();
return blacklisted;
}
diff --git a/src/blacklist.h b/src/blacklist.h
index 308b8308..b4105df3 100644
--- a/src/blacklist.h
+++ b/src/blacklist.h
@@ -1,3 +1,7 @@
#pragma once
+#ifndef MANGOHUD_BLACKLIST_H
+#define MANGOHUD_BLACKLIST_H
-bool is_blacklisted();
+bool is_blacklisted(bool force_recheck = false);
+
+#endif //MANGOHUD_BLACKLIST_H
diff --git a/src/config.cpp b/src/config.cpp
index 1787799b..900da346 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -76,6 +76,7 @@ void parseConfigFile(overlay_params& params) {
continue;
}
+ stream.imbue(std::locale::classic());
std::cerr << "parsing config: " << *p;
while (std::getline(stream, line))
{
diff --git a/src/config.h b/src/config.h
index be03dbac..7e4186ea 100644
--- a/src/config.h
+++ b/src/config.h
@@ -1,2 +1,8 @@
+#pragma once
+#ifndef MANGOHUD_CONFIG_H
+#define MANGOHUD_CONFIG_H
+
#include "overlay_params.h"
void parseConfigFile(overlay_params& p);
+
+#endif //MANGOHUD_CONFIG_H
diff --git a/src/cpu.cpp b/src/cpu.cpp
index 887326dd..f325e6a7 100644
--- a/src/cpu.cpp
+++ b/src/cpu.cpp
@@ -291,12 +291,15 @@ bool CPUStats::GetCpuFile() {
#ifndef NDEBUG
std::cerr << "hwmon: sensor name: " << name << std::endl;
#endif
- if (name == "coretemp" && find_temp_input(path, input, "Package id 0")) {
+ if (name == "coretemp") {
+ find_temp_input(path, input, "Package id 0");
break;
}
- else if ((name == "zenpower" || name == "k10temp") && find_temp_input(path, input, "Tdie")) {
+ else if ((name == "zenpower" || name == "k10temp")) {
+ find_temp_input(path, input, "Tdie");
break;
- } else if (name == "atk0110" && find_temp_input(path, input, "CPU Temperature")){
+ } else if (name == "atk0110") {
+ find_temp_input(path, input, "CPU Temperature");
break;
}
}
diff --git a/src/cpu.h b/src/cpu.h
index 1fdf20c4..1b2c29d8 100644
--- a/src/cpu.h
+++ b/src/cpu.h
@@ -1,3 +1,7 @@
+#pragma once
+#ifndef MANGOHUD_CPU_H
+#define MANGOHUD_CPU_H
+
#include
#include
#include
@@ -68,3 +72,5 @@ private:
};
extern CPUStats cpuStats;
+
+#endif //MANGOHUD_CPU_H
diff --git a/src/dbus.cpp b/src/dbus.cpp
index 2ce5d9d1..a7c23911 100644
--- a/src/dbus.cpp
+++ b/src/dbus.cpp
@@ -1,617 +1,329 @@
+#include
#include
#include
#include
-#include
+
+#include "dbus_helpers.hpp"
#include "dbus_info.h"
#include "string_utils.h"
using ms = std::chrono::milliseconds;
+using namespace DBus_helpers;
+#define DBUS_TIMEOUT 2000 // ms
-struct metadata main_metadata;
-struct metadata generic_mpris;
+struct mutexed_metadata main_metadata;
-typedef std::vector> string_pair_vec;
-typedef std::unordered_map string_pair_vec_map;
-typedef std::unordered_map string_map;
+namespace dbusmgr {
+dbus_manager dbus_mgr;
+}
-#define DBUS_TIMEOUT 2000 // ms
+template
+static void assign_metadata_value(metadata& meta, const std::string& key,
+ const T& value) {
+ if (key == "PlaybackStatus") {
+ meta.playing = (value == "Playing");
+ meta.got_playback_data = true;
+ } else if (key == "xesam:title") {
+ meta.title = value;
+ meta.got_song_data = true;
+ meta.valid = true;
+ } else if (key == "xesam:artist") {
+ meta.artists = value;
+ meta.got_song_data = true;
+ meta.valid = true;
+ } else if (key == "xesam:album") {
+ meta.album = value;
+ meta.got_song_data = true;
+ meta.valid = true;
+ } else if (key == "mpris:artUrl") {
+ meta.artUrl = value;
+ meta.got_song_data = true;
+ } else if (key == "xesam:url") {
+ // HACK if there's no metadata then use this to clear old ones
+ meta.got_song_data = true;
+ }
+}
-std::string format_signal(const DBusSignal& s)
-{
+std::string format_signal(const dbusmgr::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;
+void parse_song_data(DBusMessageIter_wrap iter, metadata& meta){
+ iter.string_map_for_each([&meta](const std::string& key,
+ DBusMessageIter_wrap it) {
+ std::string val;
+ if (it.is_primitive()) {
+ val = it.get_stringified();
+ } else if (it.is_array()) {
+ it.array_for_each_stringify([&](const std::string& str) {
+ if (val.empty()) {
+ val = str;
+ } else {
+ val += ", " + str;
+ }
+ });
+ }
+ assign_metadata_value(meta, key, val);
+ });
}
-bool get_string_array(libdbus_loader& dbus, DBusMessageIter *iter_, std::vector& 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);
+static void parse_mpris_properties(libdbus_loader& dbus, DBusMessage* msg,
+ std::string& source, metadata& meta) {
+ /**
+ * Expected response Format:
+ * string,
+ * map{
+ * "Metadata" -> multimap,
+ * "PlaybackStatus" -> string
+ * }
+ */
+
+ auto iter = DBusMessageIter_wrap(msg, &dbus);
+ source = iter.get_primitive();
+ if (source != "org.mpris.MediaPlayer2.Player") return;
+
+ iter.next();
+ if (not iter.is_array()) return;
+
+ iter.string_map_for_each([&meta](std::string& key, DBusMessageIter_wrap it) {
+ if (key == "Metadata") {
+ parse_song_data(it, meta);
+ } else if (key == "PlaybackStatus") {
+ auto val = it.get_stringified();
+ assign_metadata_value(meta, key, val);
}
- dbus.message_iter_next (&subiter);
- }
+ });
+ meta.valid = (meta.artists.size() || !meta.title.empty());
+}
+
+bool dbus_get_name_owner(dbusmgr::dbus_manager& dbus_mgr,
+ std::string& name_owner, const char* name) {
+ auto reply =
+ DBusMessage_wrap::new_method_call(
+ "org.freedesktop.DBus", "/org/freedesktop/DBus",
+ "org.freedesktop.DBus", "GetNameOwner", &dbus_mgr.dbus())
+ .argument(name)
+ .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT);
+ if (not reply) return false;
+
+ auto iter = reply.iter();
+ if (not iter.is_string()) return false;
+ name_owner = iter.get_primitive();
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)
+bool dbus_get_player_property(dbusmgr::dbus_manager& dbus_mgr, metadata& meta,
+ const char* dest, const char* prop) {
+ auto reply =
+ DBusMessage_wrap::new_method_call(dest, "/org/mpris/MediaPlayer2",
+ "org.freedesktop.DBus.Properties",
+ "Get", &dbus_mgr.dbus())
+ .argument("org.mpris.MediaPlayer2.Player")
+ .argument(prop)
+ .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT);
+
+ if (not reply) return false;
+
+ auto iter = reply.iter();
+
+ if (iter.is_array()) {
+ parse_song_data(iter, meta);
+ } else if (iter.is_primitive()) {
+ assign_metadata_value(meta, prop, iter.get_stringified());
+ } else {
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 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);
- }
+namespace dbusmgr {
+bool dbus_manager::get_media_player_metadata(metadata& meta, std::string name) {
+ if (name == "") name = m_active_player;
+ if (name == "") return false;
+ meta.clear();
+ dbus_get_player_property(*this, meta, name.c_str(), "Metadata");
+ dbus_get_player_property(*this, meta, name.c_str(), "PlaybackStatus");
+ meta.valid = (meta.artists.size() || !meta.title.empty());
+ return true;
}
-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 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});
- }
+bool dbus_manager::init(const std::string& requested_player) {
+ if (m_inited) return true;
- dbus.message_iter_next (&stack.back());
+ if (not requested_player.empty()) {
+ m_requested_player = "org.mpris.MediaPlayer2." + requested_player;
}
-}
-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 stack;
- stack.push_back({});
+ if (!m_dbus_ldr.IsLoaded() && !m_dbus_ldr.Load("libdbus-1.so.3")) {
+ std::cerr << "MANGOHUD: Could not load libdbus-1.so.3\n";
+ return false;
+ }
-#ifndef NDEBUG
- std::vector padding;
- padding.push_back('\0');
-#endif
+ m_dbus_ldr.error_init(&m_error);
- dbus.message_iter_init (msg, &stack.back());
- int type, prev_type = 0;
+ m_dbus_ldr.threads_init_default();
- 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;
+ if (nullptr ==
+ (m_dbus_conn = m_dbus_ldr.bus_get(DBUS_BUS_SESSION, &m_error))) {
+ std::cerr << "MANGOHUD: " << m_error.message << std::endl;
+ m_dbus_ldr.error_free(&m_error);
+ return false;
}
- dbus.message_iter_get_basic(&stack.back(), &name);
- source = name;
-#ifndef NDEBUG
- std::cout << name << std::endl;
-#endif
+ std::cout << "MANGOHUD: Connected to D-Bus as \""
+ << m_dbus_ldr.bus_get_unique_name(m_dbus_conn) << "\"."
+ << std::endl;
- std::pair kv;
+ dbus_list_name_to_owner();
+ connect_to_signals();
+ select_active_player();
- 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
+ m_inited = true;
+ return true;
+}
- if (type == DBUS_TYPE_STRING) {
- dbus.message_iter_get_basic(&stack.back(), &name);
+bool dbus_manager::select_active_player() {
+ auto old_active_player = m_active_player;
+ m_active_player = "";
+ metadata meta {};
+ if (not m_requested_player.empty()) {
+ // If the requested player is available, use it
+ if (m_name_owners.count(m_requested_player) > 0) {
+ m_active_player = m_requested_player;
#ifndef NDEBUG
- std::cout << "=" << name << std::endl;
+ std::cerr << "Selecting requested player: " << m_requested_player
+ << "\n";
#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);
- }
+ get_media_player_metadata(meta, m_active_player);
}
- 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 {
+ } else {
+ // If no player is requested, use any player that is currently playing
+ if (m_active_player.empty()) {
+ auto it = std::find_if(m_name_owners.begin(), m_name_owners.end(), [this, &meta](auto& entry){
+ auto& name = entry.first;
+ get_media_player_metadata(meta, name);
+ if(meta.playing) {
+ return true;
+ }
+ else {
+ meta = {};
+ return false;
+ }
+ });
+
+ if(it != m_name_owners.end()){
+ m_active_player = it->first;
#ifndef NDEBUG
- std::cout << std::endl;
+ std::cerr << "Selecting fallback player: " << m_active_player << "\n";
#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) {
+ if (not m_active_player.empty()) {
+ onNewPlayer(meta);
+ return true;
+ } else {
#ifndef NDEBUG
- std::cerr << "Not an array " << (char)current_type << std::endl;
+ std::cerr << "No active players\n";
#endif
+ if (not old_active_player.empty()) {
+ onNoPlayer();
+ }
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);
+void dbus_manager::deinit() {
+ if (!m_inited) return;
- 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);
+ // 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;
}
- return true;
+ m_dbus_ldr.error_free(&m_error);
+ m_inited = false;
}
-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 lk(meta.mutex);
- std::vector 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");
- }
+dbus_manager::~dbus_manager() { deinit(); }
- // 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 += ", ";
- }
- }
+DBusHandlerResult dbus_manager::filter_signals(DBusConnection* conn,
+ DBusMessage* msg,
+ void* userData) {
+ auto& manager = *reinterpret_cast(userData);
- it = entries_map.find("PlaybackStatus");
- if (it != entries_map.end()) {
- for (auto& kv : it->second) {
- if (kv.first == "PlaybackStatus")
- meta.playing = (kv.second == "Playing");
+ for (auto& sig : manager.m_signals) {
+ if (manager.m_dbus_ldr.message_is_signal(msg, sig.intf, sig.signal)) {
+ auto sender = manager.m_dbus_ldr.message_get_sender(msg);
+ if ((manager.*(sig.handler))(msg, sender))
+ return DBUS_HANDLER_RESULT_HANDLED;
+ else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
}
-
- if (meta.artists.size() || !meta.title.empty())
- meta.valid = true;
-
- meta.ticker.needs_recalc = true;
- meta.ticker.pos = 0;
- meta.ticker.longest = 0;
- meta.ticker.dir = -1;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
-bool dbus_get_name_owner(dbusmgr::dbus_manager& dbus_mgr, std::string& name_owner, const char *name)
-{
- auto& dbus = dbus_mgr.dbus();
- DBusError error;
+bool dbus_manager::handle_properties_changed(DBusMessage* msg,
+ const char* sender) {
+ std::string source;
- 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"))) {
- std::cerr << "MANGOHUD: " << __func__ << ": unable to allocate memory for dbus message\n";
- return false;
- }
+ metadata meta;
+ parse_mpris_properties(m_dbus_ldr, msg, source, meta);
+#ifndef NDEBUG
+ std::cerr << "PropertiesChanged Signal received:\n";
+ std::cerr << "\tSource: " << source << "\n";
+ std::cerr << "active_player: " << m_active_player << "\n";
+ std::cerr << "active_player's owner: " << m_name_owners[m_active_player]
+ << "\n";
+ std::cerr << "sender: " << sender << "\n";
+#endif
+ if (source != "org.mpris.MediaPlayer2.Player") return false;
- 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";
- return false;
+ if (m_active_player == "" or
+ (m_requested_player.empty() and not main_metadata.meta.playing)) {
+ select_active_player();
}
-
- dbus.error_init(&error);
- if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT, &error))) {
- dbus.message_unref(dbus_msg);
- std::cerr << "MANGOHUD: " << __func__ << ": "<< error.message << "\n";
- dbus.error_free(&error);
- return false;
+ else if (m_name_owners[m_active_player] == sender) {
+ onPlayerUpdate(meta);
}
-
- 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;
-
- std::vector names;
- std::string owner;
- DBusMessageIter iter;
+bool dbus_manager::handle_name_owner_changed(DBusMessage* _msg,
+ const char* sender) {
+ std::vector str;
- 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"))) {
- std::cerr << "MANGOHUD: " << __func__ << ": unable to allocate memory for dbus message\n";
- return false;
+ for (auto iter = DBusMessageIter_wrap(_msg, &m_dbus_ldr); iter;
+ iter.next()) {
+ str.push_back(iter.get_primitive());
}
- dbus.error_init(&error);
- if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT, &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;
+ // register new name
+ if (str.size() == 3 && starts_with(str[0], "org.mpris.MediaPlayer2.") &&
+ !str[2].empty()) {
+ m_name_owners[str[0]] = str[2];
+ if (str[0] == m_requested_player) {
+ select_active_player();
}
}
- dbus.message_unref(dbus_msg);
- dbus.message_unref(dbus_reply);
- dbus.error_free(&error);
- return true;
-}
-
-bool 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;
-
- 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"))) {
- std::cerr << "MANGOHUD: unable to allocate memory for dbus message" << std::endl;
- return false;
- }
-
- static 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)) {
- std::cerr << "MANGOHUD: dbus_message_append_args failed" << std::endl;
- dbus.message_unref(dbus_msg);
- return false;
- }
-
- dbus.error_init(&error);
- if (nullptr == (dbus_reply = dbus.connection_send_with_reply_and_block(dbus_mgr.get_conn(), dbus_msg, DBUS_TIMEOUT, &error))) {
- dbus.message_unref(dbus_msg);
- std::cerr << "MANGOHUD: " << error.message << std::endl;
- dbus.error_free(&error);
- return false;
- }
-
- 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);
- return true;
-}
-
-bool get_media_player_metadata(dbusmgr::dbus_manager& dbus, const std::string& name, metadata& meta)
-{
- meta.artists.clear();
- string_pair_vec_map entries;
- std::string dest = "org.mpris.MediaPlayer2." + name;
- if (!dbus_get_player_property(dbus, entries["Metadata"], dest.c_str(), "Metadata"))
- return false;
- dbus_get_player_property(dbus, entries["PlaybackStatus"], dest.c_str(), "PlaybackStatus");
- assign_metadata(meta, entries);
- return true;
-}
-
-namespace dbusmgr {
-bool dbus_manager::init(const std::string& dest)
-{
- if (m_inited)
- return true;
-
- if (!m_dbus_ldr.IsLoaded() && !m_dbus_ldr.Load("libdbus-1.so.3")) {
- std::cerr << "MANGOHUD: Could not load libdbus-1.so.3\n";
- return false;
- }
-
- 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)) ) {
- std::cerr << "MANGOHUD: " << m_error.message << std::endl;
- m_dbus_ldr.error_free(&m_error);
- return false;
+ // did a player quit?
+ if (str[2].empty()) {
+ if (str.size() == 3 && str[0] == m_active_player) {
+ m_name_owners.erase(str[0]);
+ select_active_player();
+ }
}
-
- std::cout << "MANGOHUD: Connected to D-Bus as \"" << m_dbus_ldr.bus_get_unique_name(m_dbus_conn) << "\"." << std::endl;
-
- m_dest = dest;
- dbus_list_name_to_owner(*this, m_name_owners);
-
- connect_to_signals();
- m_inited = true;
return 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()
-{
+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);
@@ -619,15 +331,18 @@ void dbus_manager::connect_to_signals()
::perror(m_error.name);
::perror(m_error.message);
m_dbus_ldr.error_free(&m_error);
- //return;
+ // return;
}
}
+ m_dbus_ldr.connection_add_filter(m_dbus_conn, filter_signals,
+ reinterpret_cast(this), nullptr);
start_thread();
}
-void dbus_manager::disconnect_from_signals()
-{
+void dbus_manager::disconnect_from_signals() {
+ m_dbus_ldr.connection_remove_filter(m_dbus_conn, filter_signals,
+ reinterpret_cast(this));
for (auto kv : m_signals) {
auto signal = format_signal(kv);
m_dbus_ldr.bus_remove_match(m_dbus_conn, signal.c_str(), &m_error);
@@ -641,132 +356,74 @@ void dbus_manager::disconnect_from_signals()
stop_thread();
}
-void dbus_manager::add_callback(CBENUM type, callback_func func)
-{
- m_callbacks[type] = func;
+bool dbus_manager::dbus_list_name_to_owner() {
+ auto reply =
+ DBusMessage_wrap::new_method_call(
+ "org.freedesktop.DBus", "/org/freedesktop/DBus",
+ "org.freedesktop.DBus", "ListNames", &dbus_mgr.dbus())
+ .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT);
+ if (not reply) return false;
+
+ auto iter = reply.iter();
+
+ if (not iter.is_array()) {
+ return false;
+ }
+ iter.array_for_each_value([&](std::string name) {
+ if (!starts_with(name.c_str(), "org.mpris.MediaPlayer2.")) return;
+ std::string owner;
+ if (dbus_get_name_owner(dbus_mgr, owner, name.c_str())) {
+ m_name_owners[name] = owner;
+ }
+ });
+ return true;
}
-void dbus_manager::stop_thread()
-{
+void dbus_manager::stop_thread() {
m_quit = true;
- if (m_thread.joinable())
- m_thread.join();
+ if (m_thread.joinable()) m_thread.join();
}
-void dbus_manager::start_thread()
-{
+void dbus_manager::start_thread() {
stop_thread();
m_quit = false;
- m_thread = std::thread(dbus_thread, this);
+ m_thread = std::thread(&dbus_manager::dbus_thread, this);
}
-void dbus_manager::dbus_thread(dbus_manager *pmgr)
-{
- (void)parse_property_changed;
- DBusMessage *msg = nullptr;
- auto& dbus = pmgr->dbus();
-
- const std::string main_dest = "org.mpris.MediaPlayer2." + pmgr->m_dest;
-
- // 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))
- {
+void dbus_manager::dbus_thread() {
+ using namespace std::chrono_literals;
+ while (!m_quit && m_dbus_ldr.connection_read_write_dispatch(m_dbus_conn, 0))
+ std::this_thread::sleep_for(10ms);
+}
- const char *sender = dbus.message_get_sender(msg);
-#ifndef NDEBUG
- std::cerr << __func__ << ": " << sig.intf << "::" << sig.signal << "\n";
- std::cerr << "Sender: " << sender << "\n";
-#endif
+void dbus_manager::onNoPlayer() {
+ std::lock_guard lck(main_metadata.mtx);
+ main_metadata.meta = {};
+ main_metadata.ticker = {};
+}
- switch (sig.type) {
- case ST_PROPERTIESCHANGED:
- {
- std::string source;
- string_pair_vec_map entries_map;
+void dbus_manager::onNewPlayer(metadata& meta) {
+ std::lock_guard lck(main_metadata.mtx);
+ main_metadata.meta = meta;
+ main_metadata.ticker = {};
+}
- //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[main_dest] == sender) {
- assign_metadata(main_metadata, 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 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] == main_dest
- ) {
- main_metadata.clear();
- } else {
- auto it = pmgr->m_name_owners.find(str[0]);
- if (it != pmgr->m_name_owners.end()
- && it->second == str[1]) {
- generic_mpris.clear();
- }
- }
- }
-
- }
- break;
- default:
- break;
- }
- }
+void dbus_manager::onPlayerUpdate(metadata& meta) {
+ std::lock_guard lck(main_metadata.mtx);
+ if (meta.got_song_data) {
+ // If the song has changed, reset the ticker
+ if (main_metadata.meta.artists != meta.artists ||
+ main_metadata.meta.album != meta.album ||
+ main_metadata.meta.title != meta.title) {
+ main_metadata.ticker = {};
}
- // free the message
- dbus.message_unref(msg);
+ main_metadata.meta = meta;
+ main_metadata.meta.playing = true;
+ }
+ if (meta.got_playback_data) {
+ main_metadata.meta.playing = meta.playing;
}
}
- dbus_manager dbus_mgr;
-}
+} // namespace dbusmgr
diff --git a/src/dbus_helpers.hpp b/src/dbus_helpers.hpp
new file mode 100644
index 00000000..341f20a3
--- /dev/null
+++ b/src/dbus_helpers.hpp
@@ -0,0 +1,363 @@
+#pragma once
+#ifndef MANGOHUD_DBUS_HELPERS
+#define MANGOHUD_DBUS_HELPERS
+
+#include
+
+#include "loaders/loader_dbus.h"
+
+namespace DBus_helpers {
+namespace detail {
+// clang-format off
+template struct dbus_type_traits{};
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_BOOLEAN; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_BYTE; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_UINT16; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_UINT32; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_UINT64; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_INT16; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_INT32; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_INT64; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_DOUBLE; const bool is_fixed = true; };
+template<> struct dbus_type_traits { const int value = DBUS_TYPE_STRING; const bool is_fixed = false; };
+// clang-format on
+
+template
+const int dbus_type_identifier = dbus_type_traits().value;
+
+template
+const bool is_fixed = dbus_type_traits().is_fiexd;
+} // namespace detail
+
+class DBusMessageIter_wrap {
+ public:
+ DBusMessageIter_wrap(DBusMessage* msg, libdbus_loader* loader);
+ DBusMessageIter_wrap(DBusMessageIter iter, libdbus_loader* loader);
+
+ // Type accessors
+ int type() const noexcept { return m_type; }
+ bool is_unsigned() const noexcept;
+ bool is_signed() const noexcept;
+ bool is_string() const noexcept;
+ bool is_double() const noexcept;
+ bool is_primitive() const noexcept;
+ bool is_array() const noexcept;
+ operator bool() const noexcept { return type() != DBUS_TYPE_INVALID; }
+
+ // Value accessors
+ // Primitives
+ template
+ auto get_primitive() -> T;
+ auto get_unsigned() -> uint64_t;
+ auto get_signed() -> int64_t;
+ auto get_stringified() -> std::string;
+ // Composites
+ auto get_array_iter() -> DBusMessageIter_wrap;
+ auto get_dict_entry_iter() -> DBusMessageIter_wrap;
+
+ // Looping
+ template
+ void array_for_each(Callable);
+ template
+ void array_for_each_stringify(Callable);
+ template
+ void array_for_each_value(Callable);
+
+ template
+ void string_map_for_each(Callable);
+ template
+ void string_multimap_for_each_stringify(Callable);
+
+ auto next() -> DBusMessageIter_wrap&;
+
+ private:
+ DBusMessageIter resolve_variants() {
+ auto iter = m_Iter;
+ auto field_type = m_DBus->message_iter_get_arg_type(&m_Iter);
+ while (field_type == DBUS_TYPE_VARIANT) {
+ m_DBus->message_iter_recurse(&iter, &iter);
+ field_type = m_DBus->message_iter_get_arg_type(&iter);
+ }
+ return iter;
+ }
+
+ DBusMessageIter m_Iter;
+ DBusMessageIter m_resolved_iter;
+ int m_type;
+ libdbus_loader* m_DBus;
+};
+
+DBusMessageIter_wrap::DBusMessageIter_wrap(DBusMessage* msg,
+ libdbus_loader* loader) {
+ m_DBus = loader;
+ if (msg) {
+ m_DBus->message_iter_init(msg, &m_Iter);
+ m_resolved_iter = resolve_variants();
+ m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter);
+ } else {
+ m_type = DBUS_TYPE_INVALID;
+ }
+}
+
+DBusMessageIter_wrap::DBusMessageIter_wrap(DBusMessageIter iter,
+ libdbus_loader* loader)
+ : m_Iter(iter), m_DBus(loader) {
+ m_resolved_iter = resolve_variants();
+ m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter);
+}
+
+bool DBusMessageIter_wrap::is_unsigned() const noexcept {
+ return ((type() == DBUS_TYPE_BYTE) || (type() == DBUS_TYPE_INT16) ||
+ (type() == DBUS_TYPE_INT32) || (type() == DBUS_TYPE_INT64));
+}
+
+bool DBusMessageIter_wrap::is_signed() const noexcept {
+ return ((type() == DBUS_TYPE_INT16) || (type() == DBUS_TYPE_INT32) ||
+ (type() == DBUS_TYPE_INT64));
+}
+
+bool DBusMessageIter_wrap::is_string() const noexcept {
+ return (type() == DBUS_TYPE_STRING);
+}
+
+bool DBusMessageIter_wrap::is_double() const noexcept {
+ return (type() == DBUS_TYPE_DOUBLE);
+}
+
+bool DBusMessageIter_wrap::is_primitive() const noexcept {
+ return (is_double() || is_signed() || is_unsigned() || is_string());
+}
+
+bool DBusMessageIter_wrap::is_array() const noexcept {
+ return (type() == DBUS_TYPE_ARRAY);
+}
+
+template
+auto DBusMessageIter_wrap::get_primitive() -> T {
+ auto requested_type = detail::dbus_type_identifier;
+ if (requested_type != type()) {
+ std::cerr << "Type mismatch: '" << ((char)requested_type) << "' vs '"
+ << (char)type() << "'\n";
+#ifndef NDEBUG
+ exit(-1);
+#else
+ return T();
+#endif
+ }
+
+ T ret;
+ m_DBus->message_iter_get_basic(&m_resolved_iter, &ret);
+ return ret;
+}
+
+template <>
+auto DBusMessageIter_wrap::get_primitive() -> std::string {
+ return std::string(get_primitive());
+}
+
+uint64_t DBusMessageIter_wrap::get_unsigned() {
+ auto t = type();
+ switch (t) {
+ case DBUS_TYPE_BYTE:
+ return get_primitive();
+ case DBUS_TYPE_UINT16:
+ return get_primitive();
+ case DBUS_TYPE_UINT32:
+ return get_primitive();
+ case DBUS_TYPE_UINT64:
+ return get_primitive();
+ default:
+ return 0;
+ }
+}
+
+int64_t DBusMessageIter_wrap::get_signed() {
+ auto t = type();
+ switch (t) {
+ case DBUS_TYPE_INT16:
+ return get_primitive();
+ case DBUS_TYPE_INT32:
+ return get_primitive();
+ case DBUS_TYPE_INT64:
+ return get_primitive();
+ default:
+ return 0;
+ }
+}
+
+auto DBusMessageIter_wrap::get_stringified() -> std::string {
+ if (is_string()) return get_primitive();
+ if (is_unsigned()) return std::to_string(get_unsigned());
+ if (is_signed()) return std::to_string(get_signed());
+ if (is_double()) return std::to_string(get_primitive());
+ std::cerr << "stringify failed\n";
+ return std::string();
+}
+
+auto DBusMessageIter_wrap::get_array_iter() -> DBusMessageIter_wrap {
+ if (not is_array()) {
+ std::cerr << "Not an array; " << (char)type() << "\n";
+ return DBusMessageIter_wrap(DBusMessageIter{}, m_DBus);
+ }
+
+ DBusMessageIter ret;
+ m_DBus->message_iter_recurse(&m_resolved_iter, &ret);
+ return DBusMessageIter_wrap(ret, m_DBus);
+}
+
+auto DBusMessageIter_wrap::get_dict_entry_iter() -> DBusMessageIter_wrap {
+ if (type() != DBUS_TYPE_DICT_ENTRY) {
+ std::cerr << "Not a dict entry" << (char)type() << "\n";
+ return DBusMessageIter_wrap(DBusMessageIter{}, m_DBus);
+ }
+
+ DBusMessageIter ret;
+ m_DBus->message_iter_recurse(&m_resolved_iter, &ret);
+ return DBusMessageIter_wrap(ret, m_DBus);
+}
+
+template
+void DBusMessageIter_wrap::array_for_each_value(Callable action) {
+ auto iter = get_array_iter();
+ for (; iter; iter.next()) {
+ action(iter.get_primitive());
+ }
+}
+
+template
+void DBusMessageIter_wrap::array_for_each(Callable action) {
+ auto iter = get_array_iter();
+ for (; iter; iter.next()) {
+ action(iter);
+ }
+}
+
+template
+void DBusMessageIter_wrap::array_for_each_stringify(Callable action) {
+ auto iter = get_array_iter();
+ for (; iter; iter.next()) {
+ action(iter.get_stringified());
+ }
+}
+
+template
+void DBusMessageIter_wrap::string_map_for_each(T action) {
+ auto iter = get_array_iter();
+ for (; iter; iter.next()) {
+ auto it = iter.get_dict_entry_iter();
+ auto key = it.get_primitive();
+
+ it.next();
+ action(key, it);
+ }
+}
+
+template
+void DBusMessageIter_wrap::string_multimap_for_each_stringify(T action) {
+ string_map_for_each([&action](const std::string& key, DBusMessageIter_wrap it) {
+ if (it.is_array()) {
+ it.array_for_each_stringify(
+ [&](const std::string& val) { action(key, val); });
+ } else if (it.is_primitive()) {
+ action(key, it.get_stringified());
+ }
+ });
+}
+
+auto DBusMessageIter_wrap::next() -> DBusMessageIter_wrap& {
+ if (not *this) return *this;
+ m_DBus->message_iter_next(&m_Iter);
+ // Resolve any variants
+ m_resolved_iter = resolve_variants();
+ m_type = m_DBus->message_iter_get_arg_type(&m_resolved_iter);
+ return *this;
+}
+
+
+class DBusMessage_wrap {
+ public:
+ DBusMessage_wrap(DBusMessage* msg, libdbus_loader* ldr, bool owning = false)
+ : m_owning(owning), m_msg(msg), m_DBus(ldr) {}
+
+ ~DBusMessage_wrap() { free_if_owning(); }
+
+ DBusMessage_wrap(const DBusMessage_wrap&) = delete;
+ DBusMessage_wrap(DBusMessage_wrap&&) = default;
+
+ operator bool() const noexcept { return m_msg != nullptr; }
+
+ template
+ DBusMessage_wrap& argument(T arg);
+
+ DBusMessage_wrap send_with_reply_and_block(DBusConnection* conn,
+ int timeout);
+
+ DBusMessageIter_wrap iter() { return DBusMessageIter_wrap(m_msg, m_DBus); }
+
+ static DBusMessage_wrap new_method_call(const std::string& bus_name,
+ const std::string& path,
+ const std::string& iface,
+ const std::string& method,
+ libdbus_loader* loader);
+
+ private:
+ void free_if_owning();
+ bool m_owning;
+ DBusMessage* m_msg;
+ libdbus_loader* m_DBus;
+ std::vector m_args;
+};
+
+template
+DBusMessage_wrap& DBusMessage_wrap::argument(T arg) {
+ if (not m_msg) return *this;
+ if (not m_DBus->message_append_args(m_msg, detail::dbus_type_identifier,
+ &arg, DBUS_TYPE_INVALID)) {
+ free_if_owning();
+ }
+ return *this;
+}
+
+template <>
+DBusMessage_wrap& DBusMessage_wrap::argument(
+ const std::string& str) {
+ return argument(str.c_str());
+}
+
+DBusMessage_wrap DBusMessage_wrap::send_with_reply_and_block(
+ DBusConnection* conn, int timeout) {
+ if (not m_msg) {
+ return DBusMessage_wrap(nullptr, m_DBus);
+ }
+ DBusError err;
+ m_DBus->error_init(&err);
+ auto reply = m_DBus->connection_send_with_reply_and_block(conn, m_msg,
+ timeout, &err);
+ if (reply == nullptr) {
+ std::cerr << "MangoHud[" << __func__ << "]: " << err.message << "\n";
+ free_if_owning();
+ m_DBus->error_free(&err);
+ }
+ return DBusMessage_wrap(reply, m_DBus, true);
+}
+
+DBusMessage_wrap DBusMessage_wrap::new_method_call(const std::string& bus_name,
+ const std::string& path,
+ const std::string& iface,
+ const std::string& method,
+ libdbus_loader* loader) {
+ auto msg = loader->message_new_method_call(
+ (bus_name.empty()) ? nullptr : bus_name.c_str(), path.c_str(),
+ (iface.empty()) ? nullptr : iface.c_str(), method.c_str());
+ return DBusMessage_wrap(msg, loader, true);
+}
+
+void DBusMessage_wrap::free_if_owning() {
+ if (m_msg and m_owning) {
+ m_DBus->message_unref(m_msg);
+ }
+ m_msg = nullptr;
+}
+} // namespace DBus_helpers
+
+#endif // MANGOHUD_DBUS_HELPERS
\ No newline at end of file
diff --git a/src/dbus_info.h b/src/dbus_info.h
index ad883a6f..f1b25332 100644
--- a/src/dbus_info.h
+++ b/src/dbus_info.h
@@ -1,137 +1,129 @@
#pragma once
+#ifndef MANGOHUD_DBUS_INFO_H
+#define MANGOHUD_DBUS_INFO_H
+
#include
-#include
-#include
#include
-#include
-#include
#include