From 34112366977f8510420392bf9b65005e083f5698 Mon Sep 17 00:00:00 2001 From: Erik Reider <35975961+ErikReider@users.noreply.github.com> Date: Fri, 13 May 2022 21:30:45 +0200 Subject: [PATCH] Initial implementation --- include/factory.hpp | 3 + include/modules/gamemode.hpp | 75 +++++++++++ meson.build | 5 + src/factory.cpp | 5 + src/modules/gamemode.cpp | 233 +++++++++++++++++++++++++++++++++++ 5 files changed, 321 insertions(+) create mode 100644 include/modules/gamemode.hpp create mode 100644 src/modules/gamemode.cpp diff --git a/include/factory.hpp b/include/factory.hpp index 2d07b4bc..1d0f147f 100644 --- a/include/factory.hpp +++ b/include/factory.hpp @@ -42,6 +42,9 @@ #ifdef HAVE_LIBEVDEV #include "modules/keyboard_state.hpp" #endif +#ifdef HAVE_GAMEMODE +#include "modules/gamemode.hpp" +#endif #ifdef HAVE_UPOWER #include "modules/upower/upower.hpp" #endif diff --git a/include/modules/gamemode.hpp b/include/modules/gamemode.hpp new file mode 100644 index 00000000..16995f1f --- /dev/null +++ b/include/modules/gamemode.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +#include "ALabel.hpp" +#include "giomm/dbusconnection.h" +#include "giomm/dbusproxy.h" +#include "glibconfig.h" +#include "gtkmm/box.h" +#include "gtkmm/image.h" +#include "gtkmm/label.h" +#include "gtkmm/overlay.h" + +namespace waybar::modules { + +class Gamemode : public AModule { + public: + Gamemode(const std::string &, const Json::Value &); + ~Gamemode(); + auto update() -> void; + + private: + const std::string DEFAULT_ICON_NAME = "input-gaming-symbolic"; + const std::string DEFAULT_FORMAT = "{glyph}"; + const std::string DEFAULT_FORMAT_ALT = "{glyph} {count}"; + const std::string DEFAULT_GLYPH = ""; + + void appear(const Glib::RefPtr &connection, const Glib::ustring &name, + const Glib::ustring &name_owner); + void disappear(const Glib::RefPtr &connection, const Glib::ustring &name); + void prepareForSleep_cb(const Glib::RefPtr &connection, + const Glib::ustring &sender_name, const Glib::ustring &object_path, + const Glib::ustring &interface_name, const Glib::ustring &signal_name, + const Glib::VariantContainerBase ¶meters); + void notify_cb(const Glib::ustring &sender_name, const Glib::ustring &signal_name, + const Glib::VariantContainerBase &arguments); + + void getData(); + bool handleToggle(GdkEventButton *const &); + + // Config + std::string format = DEFAULT_FORMAT; + std::string format_alt = DEFAULT_FORMAT_ALT; + std::string glyph = DEFAULT_GLYPH; + bool tooltip = true; + bool notRunningHide = true; + bool useIcon = true; + uint iconSize = 20; + uint iconSpacing = 4; + std::string iconName = DEFAULT_ICON_NAME; + + Gtk::Box box_; + Gtk::Image icon_; + Gtk::Label label_; + + const std::string dbus_name = "com.feralinteractive.GameMode"; + const std::string dbus_obj_path = "/com/feralinteractive/GameMode"; + const std::string dbus_interface = "org.freedesktop.DBus.Properties"; + const std::string dbus_get_interface = "com.feralinteractive.GameMode"; + + uint gameCount = 0; + + std::string lastStatus; + bool showAltText = false; + + guint login1_id; + Glib::RefPtr gamemode_proxy; + Glib::RefPtr system_connection; + bool gamemodeRunning; + guint gamemodeWatcher_id; +}; + +} // namespace waybar::modules diff --git a/meson.build b/meson.build index b3a4a4cb..ea63c902 100644 --- a/meson.build +++ b/meson.build @@ -204,6 +204,11 @@ if libnl.found() and libnlgen.found() src_files += 'src/modules/network.cpp' endif +if (giounix.found() and not get_option('logind').disabled()) + add_project_arguments('-DHAVE_GAMEMODE', language: 'cpp') + src_files += 'src/modules/gamemode.cpp' +endif + if (upower_glib.found() and giounix.found() and not get_option('logind').disabled()) add_project_arguments('-DHAVE_UPOWER', language: 'cpp') src_files += 'src/modules/upower/upower.cpp' diff --git a/src/factory.cpp b/src/factory.cpp index 5f2d755d..9defb4e7 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -12,6 +12,11 @@ waybar::AModule* waybar::Factory::makeModule(const std::string& name) const { return new waybar::modules::Battery(id, config_[name]); } #endif +#ifdef HAVE_GAMEMODE + if (ref == "gamemode") { + return new waybar::modules::Gamemode(id, config_[name]); + } +#endif #ifdef HAVE_UPOWER if (ref == "upower") { return new waybar::modules::upower::UPower(id, config_[name]); diff --git a/src/modules/gamemode.cpp b/src/modules/gamemode.cpp new file mode 100644 index 00000000..dc3764ba --- /dev/null +++ b/src/modules/gamemode.cpp @@ -0,0 +1,233 @@ +#include "modules/gamemode.hpp" + +#include +#include + +#include +#include +#include + +#include "AModule.hpp" +#include "giomm/dbusconnection.h" +#include "giomm/dbusinterface.h" +#include "giomm/dbusproxy.h" +#include "giomm/dbuswatchname.h" +#include "glibmm/error.h" +#include "glibmm/ustring.h" +#include "glibmm/variant.h" +#include "glibmm/varianttype.h" +#include "gtkmm/icontheme.h" +#include "gtkmm/label.h" +#include "gtkmm/tooltip.h" + +namespace waybar::modules { +Gamemode::Gamemode(const std::string& id, const Json::Value& config) + : AModule(config, "gamemode", id), box_(Gtk::ORIENTATION_HORIZONTAL, 0), icon_(), label_() { + box_.pack_start(icon_); + box_.pack_start(label_); + box_.set_name(name_); + event_box_.add(box_); + + // Tooltip + if (config_["tooltip"].isBool()) { + tooltip = config_["tooltip"].asBool(); + } + box_.set_has_tooltip(tooltip); + + // Hide when game count is 0 + if (config_["not-running-hide"].isBool()) { + notRunningHide = config_["not-running-hide"].asBool(); + } + + // Icon Name + if (config_["icon-name"].isString()) { + iconName = config_["icon-name"].asString(); + } + + // Icon Spacing + if (config_["icon-spacing"].isUInt()) { + iconSpacing = config_["icon-spacing"].asUInt(); + } + box_.set_spacing(iconSpacing); + + // Wether to use icon or not + if (config_["use-icon"].isBool()) { + useIcon = config_["use-icon"].asBool(); + } + + // Icon Size + if (config_["icon-size"].isUInt()) { + iconSize = config_["icon-size"].asUInt(); + } + icon_.set_pixel_size(iconSize); + + // Format + if (config_["format"].isString()) { + format = config_["format"].asString(); + } + + // Format Alt + if (config_["format-alt"].isString()) { + format_alt = config_["format-alt"].asString(); + } + + // Glyph + if (config_["glyph"].isString()) { + glyph = config_["glyph"].asString(); + } + + gamemodeWatcher_id = Gio::DBus::watch_name( + Gio::DBus::BUS_TYPE_SESSION, dbus_name, sigc::mem_fun(*this, &Gamemode::appear), + sigc::mem_fun(*this, &Gamemode::disappear), + Gio::DBus::BusNameWatcherFlags::BUS_NAME_WATCHER_FLAGS_AUTO_START); + + // Connect to gamemode + gamemode_proxy = Gio::DBus::Proxy::create_for_bus_sync(Gio::DBus::BusType::BUS_TYPE_SESSION, + dbus_name, dbus_obj_path, dbus_interface); + if (!gamemode_proxy) { + throw std::runtime_error("Unable to connect to gamemode DBus!..."); + } else { + gamemode_proxy->signal_signal().connect(sigc::mem_fun(*this, &Gamemode::notify_cb)); + } + + // Connect to Login1 PrepareForSleep signal + system_connection = Gio::DBus::Connection::get_sync(Gio::DBus::BusType::BUS_TYPE_SYSTEM); + if (!system_connection) { + throw std::runtime_error("Unable to connect to the SYSTEM Bus!..."); + } else { + login1_id = system_connection->signal_subscribe( + sigc::mem_fun(*this, &Gamemode::prepareForSleep_cb), "org.freedesktop.login1", + "org.freedesktop.login1.Manager", "PrepareForSleep", "/org/freedesktop/login1"); + } + + event_box_.signal_button_press_event().connect(sigc::mem_fun(*this, &Gamemode::handleToggle)); +} + +Gamemode::~Gamemode() { + if (gamemode_proxy) gamemode_proxy->unreference(); + if (gamemodeWatcher_id > 0) { + Gio::DBus::unwatch_name(gamemodeWatcher_id); + gamemodeWatcher_id = 0; + } + if (login1_id > 0) { + system_connection->signal_unsubscribe(login1_id); + login1_id = 0; + } +} + +// Gets the DBus ClientCount +void Gamemode::getData() { + if (gamemodeRunning && gamemode_proxy) { + try { + // Get game count + auto parameters = Glib::VariantContainerBase( + g_variant_new("(ss)", dbus_get_interface.c_str(), "ClientCount")); + Glib::VariantContainerBase data = gamemode_proxy->call_sync("Get", parameters); + if (data && data.is_of_type(Glib::VariantType("(v)"))) { + Glib::VariantBase variant; + g_variant_get(data.gobj_copy(), "(v)", &variant); + if (variant && variant.is_of_type(Glib::VARIANT_TYPE_INT32)) { + g_variant_get(variant.gobj_copy(), "i", &gameCount); + return; + } + } + } catch (Glib::Error& e) { + spdlog::error("Gamemode Error {}", e.what().c_str()); + } + } + gameCount = 0; +} + +// Whenever the DBus ClientCount changes +void Gamemode::notify_cb(const Glib::ustring& sender_name, const Glib::ustring& signal_name, + const Glib::VariantContainerBase& arguments) { + if (signal_name == "PropertiesChanged") { + getData(); + dp.emit(); + } +} + +void Gamemode::prepareForSleep_cb(const Glib::RefPtr& connection, + const Glib::ustring& sender_name, + const Glib::ustring& object_path, + const Glib::ustring& interface_name, + const Glib::ustring& signal_name, + const Glib::VariantContainerBase& parameters) { + if (parameters.is_of_type(Glib::VariantType("(b)"))) { + gboolean sleeping; + g_variant_get(parameters.gobj_copy(), "(b)", &sleeping); + if (!sleeping) { + getData(); + dp.emit(); + } + } +} + +// When the gamemode name appears +void Gamemode::appear(const Glib::RefPtr& connection, + const Glib::ustring& name, const Glib::ustring& name_owner) { + gamemodeRunning = true; + event_box_.set_visible(true); + getData(); + dp.emit(); +} +// When the gamemode name disappears +void Gamemode::disappear(const Glib::RefPtr& connection, + const Glib::ustring& name) { + gamemodeRunning = false; + event_box_.set_visible(false); +} + +bool Gamemode::handleToggle(GdkEventButton* const& event) { + showAltText = !showAltText; + dp.emit(); + return true; +} + +auto Gamemode::update() -> void { + // Don't update widget if the Gamemode service isn't running + if (!gamemodeRunning || (gameCount <= 0 && notRunningHide)) { + event_box_.set_visible(false); + return; + } + + // Show the module + if (!event_box_.get_visible()) event_box_.set_visible(true); + + // CSS status class + const std::string status = gamemodeRunning && gameCount > 0 ? "running" : ""; + // Remove last status if it exists + if (!lastStatus.empty() && box_.get_style_context()->has_class(lastStatus)) { + box_.get_style_context()->remove_class(lastStatus); + } + // Add the new status class to the Box + if (!status.empty() && !box_.get_style_context()->has_class(status)) { + box_.get_style_context()->add_class(status); + } + lastStatus = status; + + // Tooltip + if (tooltip) { + std::string text = "Games running: "; + box_.set_tooltip_text(text + std::to_string(gameCount)); + box_.set_has_tooltip(gameCount > 0); + } + + // Label format + std::string str = + fmt::format(showAltText ? format_alt : format, fmt::arg("glyph", useIcon ? "" : glyph), + fmt::arg("count", gameCount > 0 ? std::to_string(gameCount) : "")); + label_.set_markup(str); + + if (useIcon) { + if (!Gtk::IconTheme::get_default()->has_icon(iconName)) { + iconName = DEFAULT_ICON_NAME; + } + icon_.set_from_icon_name(iconName, Gtk::ICON_SIZE_INVALID); + } + + // Call parent update + AModule::update(); +} + +} // namespace waybar::modules