Dbus with spotify

pull/131/head
FlightlessMango 4 years ago committed by jackun
parent cbb46fa21b
commit d975278396
No known key found for this signature in database
GPG Key ID: 119DB3F1D05A9ED3

@ -115,6 +115,7 @@ inc_common = [
dep_vulkan = dependency('vulkan', required: get_option('use_system_vulkan'))
dep_pthread = dependency('threads')
dep_dbus = dependency('dbus-1')
# Check for generic C arguments
c_args = []

@ -0,0 +1,585 @@
#include <cstdio>
#include <iostream>
#include <sstream>
#include <array>
#include "dbus_info.h"
using ms = std::chrono::milliseconds;
struct metadata spotify;
typedef std::vector<std::pair<std::string, std::string>> string_pair_vec;
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(DBusMessageIter *iter, int type)
{
int curr_type = DBUS_TYPE_INVALID;
if ((curr_type = dbus_message_iter_get_arg_type (iter)) != type) {
std::cerr << "Argument is not of type '" << (char)type << "' != '" << (char) curr_type << "'" << std::endl;
return false;
}
return true;
}
bool get_string_array(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) {
std::cerr << "Not an array: '" << (char)current_type << "'" << std::endl;
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(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 (&iter, DBUS_TYPE_VARIANT))
return false;
dbus_message_iter_recurse (&iter, &iter);
}
if (!check_msg_arg (&iter, DBUS_TYPE_STRING))
return false;
dbus_message_iter_get_basic(&iter, &str);
val = str;
return true;
}
static bool get_variant_string(DBusMessage *msg, std::string &val, bool key_or_value = false)
{
DBusMessageIter iter;
dbus_message_iter_init (msg, &iter);
return get_variant_string(&iter, val, key_or_value);
}
static void parse_mpris_metadata(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)
{
dbus_message_iter_next (&iter);
//std::cerr << "\ttype: " << (char)dbus_message_iter_get_arg_type(&iter) << std::endl;
if (!get_variant_string(&iter, key))
return;
dbus_message_iter_recurse (&iter, &subiter);
dbus_message_iter_next (&subiter);
//std::cerr << "\tkey: " << key << std::endl;
if (get_variant_string(&subiter, val)) {
//std::cerr << "\t\t" << val << std::endl;
entries.push_back({key, val});
}
else if (get_string_array(&subiter, list)) {
for (auto& s : list) {
//std::cerr << "\t\t" << s << std::endl;
entries.push_back({key, s});
}
}
}
}
static void parse_mpris_properties(DBusMessage *msg, std::string& source, string_pair_vec& entries)
{
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(&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(&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(&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(&iter, DBUS_TYPE_VARIANT))
continue;
dbus_message_iter_recurse (&iter, &iter);
if (!check_msg_arg(&iter, DBUS_TYPE_ARRAY))
continue;
dbus_message_iter_recurse (&iter, &iter);
parse_mpris_metadata(&iter, entries);
}
else if (key == "PlaybackStatus") {
dbus_message_iter_recurse (&stack.back(), &iter);
dbus_message_iter_next (&iter);
if (get_variant_string(&iter, val))
entries.push_back({key, val});
}
dbus_message_iter_next (&stack.back());
}
}
static void parse_property_changed(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> tabs;
tabs.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) {
std::cerr << __func__ << "First element is not a string" << std::endl;
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());
while ((type = dbus_message_iter_get_arg_type (&stack.back())) != DBUS_TYPE_INVALID) {
#ifndef NDEBUG
tabs.back() = ' ';
tabs.resize(stack.size() + 1, ' ');
tabs.back() = '\0';
std::cout << tabs.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(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) {
std::cerr << "Not an array " << (char)current_type << std::endl;
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& entries)
{
std::lock_guard<std::mutex> lk(meta.mutex);
meta.valid = false;
bool artists_cleared = false;
for (auto& kv : entries) {
#ifndef NDEBUG
std::cerr << kv.first << " = " << kv.second << std::endl;
#endif
if (kv.first == "xesam:artist") {
if (!artists_cleared) {
artists_cleared = true;
meta.artists.clear();
}
meta.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");
}
if (meta.artists.size() || !meta.title.empty())
meta.valid = meta.playing;
}
void dbus_get_spotify_property(dbusmgr::dbus_manager& dbus, string_pair_vec& entries, const char * prop)
{
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("org.mpris.MediaPlayer2.spotify", "/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",
};
std::cerr << __func__ << ": " << prop << std::endl;
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(error.message);
}
if (nullptr == (dbus_reply = ::dbus_connection_send_with_reply_and_block(dbus.get_conn(), dbus_msg, DBUS_TIMEOUT_USE_DEFAULT, &error))) {
::dbus_message_unref(dbus_msg);
throw dbusmgr::dbus_error(&error);
}
std::string entry;
if (get_dict_string_array(dbus_reply, entries)) {
// nothing
} else if (get_variant_string(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 entries;
dbus_get_spotify_property(dbus, entries, "Metadata");
dbus_get_spotify_property(dbus, entries, "PlaybackStatus");
assign_metadata(meta, entries);
}
namespace dbusmgr {
void dbus_manager::init()
{
::dbus_threads_init_default();
if ( nullptr == (m_dbus_conn = ::dbus_bus_get(DBUS_BUS_SESSION, &m_error)) ) {
throw dbus_error(&m_error);
}
std::cout << "Connected to D-Bus as \"" << ::dbus_bus_get_unique_name(m_dbus_conn) << "\"." << std::endl;
connect_to_signals();
m_inited = true;
}
dbus_manager::~dbus_manager()
{
// unreference system bus connection instead of closing it
if (m_dbus_conn) {
disconnect_from_signals();
::dbus_connection_unref(m_dbus_conn);
m_dbus_conn = nullptr;
}
::dbus_error_free(&m_error);
}
void dbus_manager::connect_to_signals()
{
for (auto kv : m_signals) {
auto signal = format_signal(kv);
::dbus_bus_add_match(m_dbus_conn, signal.c_str(), &m_error);
if (::dbus_error_is_set(&m_error)) {
::perror(m_error.name);
::perror(m_error.message);
::dbus_error_free(&m_error);
//return;
}
}
start_thread();
}
void dbus_manager::disconnect_from_signals()
{
for (auto kv : m_signals) {
auto signal = format_signal(kv);
::dbus_bus_remove_match(m_dbus_conn, signal.c_str(), &m_error);
if (dbus_error_is_set(&m_error)) {
::perror(m_error.name);
::perror(m_error.message);
::dbus_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)
{
DBusError error;
DBusMessage *msg = nullptr;
::dbus_error_init(&error);
// 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))
{
#ifndef NDEBUG
std::cerr << __func__ << ": " << sig.intf << "::" << sig.signal << std::endl;
#endif
switch (sig.type) {
case ST_PROPERTIESCHANGED:
{
std::string source;
std::vector<std::pair<std::string, std::string>> entries;
//parse_property_changed(msg, source, entries);
parse_mpris_properties(msg, source, entries);
#ifndef NDEBUG
std::cerr << "Source: " << source << std::endl;
#endif
if (source != "org.mpris.MediaPlayer2.Player")
break;
assign_metadata(spotify, entries);
}
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);
}
// did spotify quit?
if (str.size() == 3
&& str[0] == "org.mpris.MediaPlayer2.spotify"
&& str[2].empty()
)
{
spotify.valid = false;
}
}
break;
default:
break;
}
if (dbus_error_is_set(&error)) {
std::cerr << error.message << std::endl;
dbus_error_free(&error);
}
}
}
// free the message
dbus_message_unref(msg);
}
}
dbus_manager dbus_mgr;
}

@ -0,0 +1,102 @@
#pragma once
#include <dbus/dbus.h>
#include <stdexcept>
#include <thread>
#include <functional>
#include <vector>
#include <string>
#include <map>
#include <mutex>
struct metadata {
std::vector<std::string> artists;
std::string title;
std::string album;
std::string something;
std::string artUrl;
bool playing = false;
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;
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(DBusError *src) : std::runtime_error(src->message)
{
dbus_error_init(&error);
dbus_move_error (src, &error);
}
virtual ~dbus_error() { dbus_error_free (&error); }
private:
DBusError error;
};
class dbus_manager
{
public:
dbus_manager()
{
::dbus_error_init(&m_error);
}
~dbus_manager();
void init();
void add_callback(CBENUM type, callback_func func);
void connect_to_signals();
void disconnect_from_signals();
DBusConnection* get_conn() const {
return m_dbus_conn;
}
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;
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);

@ -1,7 +1,9 @@
#include <cstdlib>
#include <functional>
#include <thread>
#include <iostream>
#include "imgui_hud_shared.h"
#include "dbus_info.h"
namespace MangoHud { namespace GL {
@ -26,6 +28,14 @@ void imgui_init()
init_system_info();
cfg_inited = true;
init_cpu_stats(params);
if (params.enabled[OVERLAY_PARAM_ENABLED_media_player]) {
try {
dbusmgr::dbus_mgr.init();
get_spotify_metadata(dbusmgr::dbus_mgr, spotify);
} catch (std::runtime_error& e) {
std::cerr << "Failed to get initial Spotify metadata: " << e.what() << std::endl;
}
}
}
}} // namespaces

@ -53,6 +53,7 @@ vklayer_files = files(
'notify.cpp',
'elfhacks.cpp',
'real_dlsym.cpp',
'dbus.cpp',
)
opengl_files = files(
@ -177,6 +178,7 @@ vklayer_mesa_overlay = shared_library(
glimgui_egl_dep,
dep_dl,
dep_pthread,
dep_dbus,
dep_vulkan],
include_directories : [inc_common, inc_opengl],
link_args : cc.get_supported_link_arguments(['-Wl,-Bsymbolic-functions', '-Wl,-z,relro']),

@ -57,10 +57,12 @@
#include "loaders/loader_nvml.h"
#include "memory.h"
#include "notify.h"
#include "dbus_info.h"
bool open = false;
string gpuString;
float offset_x, offset_y, hudSpacing;
float hudTicker = 50.f, overflow = 50.f /* 3333ms * 0.5 / 16.6667 / 2 (to edge and back) */;
int hudFirstRow, hudSecondRow;
struct amdGpu amdgpu;
struct fps_limit fps_limit_stats;
@ -1000,6 +1002,8 @@ static void right_aligned_text(float off_x, const char *fmt, ...)
void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2& window_size, bool is_vulkan)
{
uint32_t f_idx = (data.n_frames - 1) % ARRAY_SIZE(data.frames_stats);
uint64_t frame_timing = data.frames_stats[f_idx].stats[OVERLAY_PLOTS_frame_timing];
static float char_width = ImGui::CalcTextSize("A").x;
window_size = ImVec2(params.width, params.height);
unsigned width = ImGui::GetIO().DisplaySize.x;
@ -1212,9 +1216,72 @@ void render_imgui(swapchain_stats& data, struct overlay_params& params, ImVec2&
if (params.enabled[OVERLAY_PARAM_ENABLED_frame_timing]){
ImGui::SameLine(0,1.0f);
ImGui::PushFont(data.font1);
ImGui::Text("%.1f ms", 1000 / data.fps);
ImGui::Text("%.1f ms", 1000 / data.fps); //frame_timing / 1000.f);
ImGui::PopFont();
}
{
scoped_lock lk(spotify.mutex);
if (spotify.valid) {
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8,0));
ImGui::Dummy(ImVec2(0.0f, 20.0f));
ImGui::PushFont(data.font1);
float tw = ImGui::CalcTextSize(spotify.title.c_str()).x;
float cw = ImGui::GetContentRegionAvailWidth();
//if (hudTicker < -tw)
// hudTicker = cw;
static int dir = 1;
float limited_pos, new_pos_x = ImGui::GetCursorPosX();
float left_limit = cw - tw + new_pos_x;
float right_limit = ImGui::GetCursorPosX();
if (cw < tw) {
if (hudTicker < left_limit - overflow * .25f - new_pos_x) {
dir = -1;
hudTicker = (left_limit - overflow * .25f) + 1.f - new_pos_x;
} else if (hudTicker > right_limit + overflow - new_pos_x) {
dir = 1;
hudTicker = (right_limit + overflow) - 1.f - new_pos_x;
}
hudTicker -= .5f * (frame_timing / 16666.7f) * dir;
new_pos_x += hudTicker;
// acts as a delay before it starts scrolling again
if (new_pos_x < left_limit)
limited_pos = left_limit;
else if (new_pos_x > right_limit)
limited_pos = right_limit;
else
limited_pos = new_pos_x;
} else {
limited_pos = new_pos_x;
hudTicker = overflow;
}
ImGui::SetCursorPosX(limited_pos);
ImGui::Text("%s", spotify.title.c_str());
//std::cerr << "ticker: " << hudTicker << ", " << left_limit << "<>" << right_limit << ", " << dir << std::endl;
for (size_t i = 0; i < spotify.artists.size(); i++) {
ImGui::Text("%s", spotify.artists[i].c_str());
if (i < spotify.artists.size() - 1) {
ImGui::SameLine(0, 1.0f);
ImGui::Text(",");
ImGui::SameLine(0, 1.0f);
}
}
//ImGui::NewLine();
if (!spotify.album.empty())
ImGui::Text("%s", spotify.album.c_str());
ImGui::PopFont();
ImGui::PopStyleVar();
}
}
window_size = ImVec2(window_size.x, ImGui::GetCursorPosY() + 10.0f);
ImGui::End();
}
@ -2452,6 +2519,15 @@ static VkResult overlay_CreateInstance(
init_cpu_stats(instance_data->params);
if (instance_data->params.enabled[OVERLAY_PARAM_ENABLED_media_player]) {
try {
dbusmgr::dbus_mgr.init();
get_spotify_metadata(dbusmgr::dbus_mgr, spotify);
} catch (std::runtime_error& e) {
std::cerr << "Failed to get initial Spotify metadata: " << e.what() << std::endl;
}
}
// Adjust height for DXVK/VKD3D version number
if (engineName == "DXVK" || engineName == "VKD3D"){
if (instance_data->params.font_size){

@ -64,6 +64,7 @@ typedef unsigned long KeySym;
OVERLAY_PARAM_BOOL(gpu_mem_clock) \
OVERLAY_PARAM_BOOL(gpu_core_clock) \
OVERLAY_PARAM_BOOL(arch) \
OVERLAY_PARAM_BOOL(media_player) \
OVERLAY_PARAM_CUSTOM(fps_sampling_period) \
OVERLAY_PARAM_CUSTOM(output_file) \
OVERLAY_PARAM_CUSTOM(font_file) \

Loading…
Cancel
Save