diff --git a/src/dbus.cpp b/src/dbus.cpp index b00774f1..bb665ee2 100644 --- a/src/dbus.cpp +++ b/src/dbus.cpp @@ -150,18 +150,7 @@ bool dbus_manager::get_media_player_metadata(metadata& meta, std::string name) { return true; } -bool dbus_manager::init(const std::string& requested_player) { - - if (!requested_player.empty()) { - m_requested_player = "org.mpris.MediaPlayer2." + requested_player; - } else - m_requested_player.clear(); - - if (m_inited) { - select_active_player(); - return true; - } - +bool dbus_manager::init_internal() { if (!m_dbus_ldr.IsLoaded() && !m_dbus_ldr.Load("libdbus-1.so.3")) { SPDLOG_ERROR("Could not load libdbus-1.so.3"); return false; @@ -182,13 +171,34 @@ bool dbus_manager::init(const std::string& requested_player) { m_dbus_ldr.bus_get_unique_name(m_dbus_conn)); dbus_list_name_to_owner(); - connect_to_signals(); - select_active_player(); - m_inited = true; return true; } +bool dbus_manager::init(Service srv) { + if (!m_inited && !init_internal()) + return false; + + connect_to_signals(srv); + m_active_srvs |= srv; + return true; +} + +bool dbus_manager::init_mpris(const std::string& requested_player) { + if (!requested_player.empty()) { + m_requested_player = "org.mpris.MediaPlayer2." + requested_player; + } else + m_requested_player.clear(); + + if (m_active_srvs & SRV_MPRIS) { + select_active_player(); + return true; + } + + SPDLOG_WARN("D-Bus hasn't been inited yet."); + return false; +} + bool dbus_manager::select_active_player() { auto old_active_player = m_active_player; m_active_player = ""; @@ -234,20 +244,23 @@ bool dbus_manager::select_active_player() { } } -void dbus_manager::deinit() { +void dbus_manager::deinit(Service srv) { if (!m_inited) return; + m_active_srvs &= ~srv; + if (m_dbus_conn) + disconnect_from_signals(srv); + // unreference system bus connection instead of closing it - if (m_dbus_conn) { - disconnect_from_signals(); + if (m_dbus_conn && !m_active_srvs) { m_dbus_ldr.connection_unref(m_dbus_conn); m_dbus_conn = nullptr; + m_dbus_ldr.error_free(&m_error); + m_inited = false; } - m_dbus_ldr.error_free(&m_error); - m_inited = false; } -dbus_manager::~dbus_manager() { deinit(); } +dbus_manager::~dbus_manager() { deinit(SRV_ALL); } DBusHandlerResult dbus_manager::filter_signals(DBusConnection* conn, DBusMessage* msg, @@ -320,8 +333,46 @@ bool dbus_manager::handle_name_owner_changed(DBusMessage* _msg, return true; } -void dbus_manager::connect_to_signals() { +bool dbus_manager::gamemode_enabled(int32_t pid) { + if (!m_inited) + return false; + + auto reply = + DBusMessage_wrap::new_method_call( + "com.feralinteractive.GameMode", "/com/feralinteractive/GameMode", + "com.feralinteractive.GameMode", "QueryStatus", &dbus_mgr.dbus()) + .argument(pid) + .send_with_reply_and_block(dbus_mgr.get_conn(), DBUS_TIMEOUT); + if (!reply) return false; + + auto iter = reply.iter(); + if (!iter.is_signed()) return false; + return !!iter.get_primitive(); +} + +bool dbus_manager::handle_game_registered(DBusMessage* _msg, + const char* sender) { + auto iter = DBusMessageIter_wrap(_msg, &m_dbus_ldr); + auto pid = iter.get_primitive(); + iter.next(); + auto path = iter.get_primitive(); + SPDLOG_INFO("Game registered: {} '{}'", pid, path); + return true; +} + +bool dbus_manager::handle_game_unregistered(DBusMessage* _msg, + const char* sender) { + auto iter = DBusMessageIter_wrap(_msg, &m_dbus_ldr); + auto pid = iter.get_primitive(); + iter.next(); + auto path = iter.get_primitive(); + SPDLOG_INFO("Game unregistered: {} '{}'", pid, path); + return true; +} + +void dbus_manager::connect_to_signals(Service srv) { for (auto kv : m_signals) { + if (!(kv.srv & srv)) continue; 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)) { @@ -336,10 +387,11 @@ void dbus_manager::connect_to_signals() { start_thread(); } -void dbus_manager::disconnect_from_signals() { +void dbus_manager::disconnect_from_signals(Service srv) { m_dbus_ldr.connection_remove_filter(m_dbus_conn, filter_signals, reinterpret_cast(this)); for (auto kv : m_signals) { + if (!(kv.srv & srv)) continue; 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)) { diff --git a/src/dbus_helpers.h b/src/dbus_helpers.h index 063dbb48..a7649896 100644 --- a/src/dbus_helpers.h +++ b/src/dbus_helpers.h @@ -137,7 +137,10 @@ bool DBusMessageIter_wrap::is_array() const noexcept { template auto DBusMessageIter_wrap::get_primitive() -> T { auto requested_type = detail::dbus_type_identifier; - if (requested_type != type()) { + if (type() == DBUS_TYPE_OBJECT_PATH && requested_type == DBUS_TYPE_STRING) { + // no special type, just a string + } + else if (requested_type != type()) { SPDLOG_ERROR("Type mismatch: '{}' vs '{}'", ((char)requested_type), (char)type()); #ifndef NDEBUG diff --git a/src/dbus_info.h b/src/dbus_info.h index 36e48dcb..762b8b31 100644 --- a/src/dbus_info.h +++ b/src/dbus_info.h @@ -55,7 +55,16 @@ namespace dbusmgr { class dbus_manager; using signal_handler_func = bool (dbus_manager::*)(DBusMessage*, const char*); +enum Service +{ + SRV_NONE = 0, + SRV_MPRIS = (1ul << 0), + SRV_GAMEMODE = (1ul << 1), + SRV_ALL = 0xFFFFFFFF, +}; + struct DBusSignal { + Service srv; const char* intf; const char* signal; signal_handler_func handler; @@ -67,16 +76,20 @@ class dbus_manager { ~dbus_manager(); - bool init(const std::string& requested_player); - void deinit(); + bool init(Service srv); + bool init_mpris(const std::string& requested_player); + void deinit(Service srv); bool get_media_player_metadata(metadata& meta, std::string name = ""); - void connect_to_signals(); - void disconnect_from_signals(); + void connect_to_signals(Service srv); + void disconnect_from_signals(Service srv); DBusConnection* get_conn() const { return m_dbus_conn; } + bool gamemode_enabled(int32_t pid); + libdbus_loader& dbus() { return m_dbus_ldr; } protected: + bool init_internal(); void stop_thread(); void start_thread(); void dbus_thread(); @@ -89,6 +102,8 @@ class dbus_manager { bool handle_properties_changed(DBusMessage*, const char*); bool handle_name_owner_changed(DBusMessage*, const char*); + bool handle_game_registered(DBusMessage*, const char*); + bool handle_game_unregistered(DBusMessage*, const char*); void onNewPlayer( metadata& meta); // A different player has become the active player @@ -105,12 +120,17 @@ class dbus_manager { std::unordered_map m_name_owners; std::string m_requested_player; std::string m_active_player; + uint32_t m_active_srvs = SRV_NONE; const std::array m_signals{{ - {"org.freedesktop.DBus", "NameOwnerChanged", + {SRV_MPRIS, "org.freedesktop.DBus", "NameOwnerChanged", &dbus_manager::handle_name_owner_changed}, - {"org.freedesktop.DBus.Properties", "PropertiesChanged", + {SRV_MPRIS, "org.freedesktop.DBus.Properties", "PropertiesChanged", &dbus_manager::handle_properties_changed}, +// {SRV_GAMEMODE, "com.feralinteractive.GameMode", "GameRegistered", +// &dbus_manager::handle_game_registered}, +// {SRV_GAMEMODE, "com.feralinteractive.GameMode", "GameUnregistered", +// &dbus_manager::handle_game_unregistered}, }}; }; diff --git a/src/overlay_params.cpp b/src/overlay_params.cpp index 04282fe0..227b9662 100644 --- a/src/overlay_params.cpp +++ b/src/overlay_params.cpp @@ -7,6 +7,7 @@ #include #ifdef __gnu_linux__ #include +#include #endif #include "imgui.h" #include @@ -780,11 +781,21 @@ parse_overlay_config(struct overlay_params *params, #ifdef HAVE_DBUS if (params->enabled[OVERLAY_PARAM_ENABLED_media_player]) { - dbusmgr::dbus_mgr.init(params->media_player_name); + if (dbusmgr::dbus_mgr.init(dbusmgr::SRV_MPRIS)) + dbusmgr::dbus_mgr.init_mpris(params->media_player_name); } else { - dbusmgr::dbus_mgr.deinit(); + dbusmgr::dbus_mgr.deinit(dbusmgr::SRV_MPRIS); main_metadata.meta.valid = false; } + + if (params->enabled[OVERLAY_PARAM_ENABLED_gamemode]) + { + if (dbusmgr::dbus_mgr.init(dbusmgr::SRV_GAMEMODE)) + HUDElements.gamemode_bol = dbusmgr::dbus_mgr.gamemode_enabled(getpid()); + } + else + dbusmgr::dbus_mgr.deinit(dbusmgr::SRV_GAMEMODE); + #endif if(!params->output_file.empty()) {