41  MPRISService(std::string applicationName) : applicationName_(std::move(applicationName))
 
   44    const char* introspection_xml = R
"XML( 
   45<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" 
   46    "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> 
   48    <interface name="org.mpris.MediaPlayer2"> 
   49        <property name="Identity" type="s" access="read"/> 
   50        <property name="DesktopEntry" type="s" access="read"/> 
   51        <property name="SupportedUriSchemes" type="as" access="read"/> 
   52        <property name="SupportedMimeTypes" type="as" access="read"/> 
   53        <property name="CanQuit" type="b" access="read"/> 
   54        <property name="CanRaise" type="b" access="read"/> 
   55        <property name="HasTrackList" type="b" access="read"/> 
   57    <interface name="org.mpris.MediaPlayer2.Player"> 
   58        <property name="PlaybackStatus" type="s" access="read"/> 
   59        <property name="LoopStatus" type="s" access="readwrite"/> 
   60        <property name="Rate" type="d" access="readwrite"/> 
   61        <property name="Shuffle" type="b" access="readwrite"/> 
   62        <property name="Metadata" type="a{sv}" access="read"/> 
   63        <property name="Volume" type="d" access="readwrite"/> 
   64        <property name="Position" type="x" access="read"/> 
   65        <property name="MinimumRate" type="d" access="read"/> 
   66        <property name="MaximumRate" type="d" access="read"/> 
   67        <property name="CanGoNext" type="b" access="read"/> 
   68        <property name="CanGoPrevious" type="b" access="read"/> 
   69        <property name="CanPlay" type="b" access="read"/> 
   70        <property name="CanPause" type="b" access="read"/> 
   71        <property name="CanSeek" type="b" access="read"/> 
   72        <property name="CanControl" type="b" access="read"/> 
   74        <method name="Previous"/> 
   75        <method name="Pause"/> 
   76        <method name="PlayPause"/> 
   80            <arg name="Offset" type="x" direction="in"/> 
   82        <method name="SetPosition"> 
   83            <arg name="TrackId" type="o" direction="in"/> 
   84            <arg name="Position" type="x" direction="in"/> 
   86        <method name="OpenUri"> 
   87            <arg name="Uri" type="s" direction="in"/> 
   92    GError* error       = nullptr;
 
   93    introspection_data_ = g_dbus_node_info_new_for_xml(introspection_xml, &error);
 
   96      std::cerr << 
"Error creating introspection data: " << error->message << std::endl;
 
  101    connection_ = g_bus_get_sync(G_BUS_TYPE_SESSION, 
nullptr, &error);
 
  102    if (error != 
nullptr)
 
  104      std::cerr << 
"Error connecting to D-Bus: " << error->message << std::endl;
 
  110    const GDBusInterfaceVTable root_vtable = {
 
  111      handle_root_method_call, handle_root_get_property, 
nullptr, {
nullptr}};
 
  113    const GDBusInterfaceVTable player_vtable = {
 
  114      handle_player_method_call, handle_player_get_property, handle_player_set_property, {
nullptr}};
 
  116    std::string busName = 
"org.mpris.MediaPlayer2." + applicationName_;
 
  118      g_bus_own_name(G_BUS_TYPE_SESSION, busName.c_str(), G_BUS_NAME_OWNER_FLAGS_REPLACE,
 
  119                     on_bus_acquired, 
nullptr, 
nullptr, 
this, 
nullptr);
 
  122    g_dbus_connection_register_object(connection_, 
"/org/mpris/MediaPlayer2",
 
  123                                      introspection_data_->interfaces[0], 
 
  124                                      &root_vtable, 
this, 
nullptr, &error);
 
  126    g_dbus_connection_register_object(connection_, 
"/org/mpris/MediaPlayer2",
 
  127                                      introspection_data_->interfaces[1], 
 
  128                                      &player_vtable, 
this, 
nullptr, &error);
 
  130    if (error != 
nullptr)
 
  132      std::cerr << 
"Error registering object: " << error->message << std::endl;
 
  137    updateMetadata(
"Unknown Song", 
"Unknown Artist", 
"Unknown Album", 0, 
"No Comment", 
"No genre",
 
  150      g_object_unref(connection_);
 
  151      connection_ = 
nullptr;
 
  153    if (introspection_data_)
 
  155      g_dbus_node_info_unref(introspection_data_);
 
  156      introspection_data_ = 
nullptr;
 
  158    if (current_metadata_)
 
  160      g_variant_unref(current_metadata_);
 
  161      current_metadata_ = 
nullptr;
 
  163    std::cout << 
"-- MPRISService cleaned up." << std::endl;
 
  183  void updateMetadata(
const std::string& title, 
const std::string& artist, 
const std::string& album,
 
  184                      int64_t length, 
const std::string& comment = 
"",
 
  185                      const std::string& genre = 
"", 
int trackNumber = 0, 
int discNumber = 0)
 
  187    GVariantBuilder builder;
 
  188    g_variant_builder_init(&builder, G_VARIANT_TYPE(
"a{sv}"));
 
  191    std::string trackid = 
"/org/mpris/MediaPlayer2/Track/" + std::to_string(rand());
 
  192    g_variant_builder_add(&builder, 
"{sv}", 
"mpris:trackid",
 
  193                          g_variant_new_object_path(trackid.c_str()));
 
  195    g_variant_builder_add(&builder, 
"{sv}", 
"xesam:title", g_variant_new_string(title.c_str()));
 
  197    GVariantBuilder artist_builder;
 
  198    g_variant_builder_init(&artist_builder, G_VARIANT_TYPE(
"as"));
 
  199    g_variant_builder_add(&artist_builder, 
"s", artist.c_str());
 
  200    g_variant_builder_add(&builder, 
"{sv}", 
"xesam:artist", g_variant_builder_end(&artist_builder));
 
  202    g_variant_builder_add(&builder, 
"{sv}", 
"xesam:album", g_variant_new_string(album.c_str()));
 
  204    g_variant_builder_add(&builder, 
"{sv}", 
"mpris:length", g_variant_new_int64(length * 1000000));
 
  207    if (!comment.empty())
 
  209      g_variant_builder_add(&builder, 
"{sv}", 
"xesam:comment",
 
  210                            g_variant_new_string(comment.c_str()));
 
  214      GVariantBuilder genre_builder;
 
  215      g_variant_builder_init(&genre_builder, G_VARIANT_TYPE(
"as"));
 
  216      g_variant_builder_add(&genre_builder, 
"s", genre.c_str());
 
  217      g_variant_builder_add(&builder, 
"{sv}", 
"xesam:genre", g_variant_builder_end(&genre_builder));
 
  221      g_variant_builder_add(&builder, 
"{sv}", 
"xesam:trackNumber",
 
  222                            g_variant_new_int32(trackNumber));
 
  226      g_variant_builder_add(&builder, 
"{sv}", 
"xesam:discNumber", g_variant_new_int32(discNumber));
 
  230    if (current_metadata_)
 
  231      g_variant_unref(current_metadata_);
 
  232    current_metadata_ = g_variant_builder_end(&builder);
 
  233    g_variant_ref(current_metadata_);
 
  236    GVariantBuilder props_builder;
 
  237    g_variant_builder_init(&props_builder, G_VARIANT_TYPE_ARRAY);
 
  238    g_variant_builder_add(&props_builder, 
"{sv}", 
"Metadata", current_metadata_);
 
  240    g_dbus_connection_emit_signal(
 
  241      connection_, 
nullptr, 
"/org/mpris/MediaPlayer2", 
"org.freedesktop.DBus.Properties",
 
  243      g_variant_new(
"(sa{sv}as)", 
"org.mpris.MediaPlayer2.Player", &props_builder, 
nullptr),
 
  258  static void on_bus_acquired(GDBusConnection* connection, 
const gchar* name, gpointer user_data)
 
  260    std::cout << 
"Acquired bus name: " << name << std::endl;
 
  278  static void handle_root_method_call(GDBusConnection*, 
const char*, 
const char*, 
const char*,
 
  279                                      const char*, GVariant*, GDBusMethodInvocation* invocation,
 
  282    g_dbus_method_invocation_return_value(invocation, 
nullptr);
 
  299  static void handle_player_method_call(GDBusConnection*, 
const char*, 
const char*, 
const char*,
 
  300                                        const char*            method_name, GVariant*,
 
  301                                        GDBusMethodInvocation* invocation, 
void* user_data)
 
  304    g_dbus_method_invocation_return_value(invocation, 
nullptr);
 
  321  static auto handle_root_get_property(GDBusConnection* connection, 
const char* sender,
 
  322                                       const char* object_path, 
const char* interface_name,
 
  323                                       const char* property_name, GError** error,
 
  324                                       void* user_data) -> GVariant*
 
  328    if (g_strcmp0(property_name, 
"Identity") == 0)
 
  329      return g_variant_new_string(service->applicationName_.c_str());
 
  330    if (g_strcmp0(property_name, 
"CanQuit") == 0)
 
  331      return g_variant_new_boolean(TRUE);
 
  332    if (g_strcmp0(property_name, 
"CanRaise") == 0)
 
  333      return g_variant_new_boolean(TRUE);
 
  334    if (g_strcmp0(property_name, 
"HasTrackList") == 0)
 
  335      return g_variant_new_boolean(FALSE);
 
  338    g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED,
 
  339                "Property '%s' is not recognized for interface '%s'.", property_name,
 
  359  static auto handle_player_get_property(GDBusConnection* connection, 
const char* sender,
 
  360                                         const char* object_path, 
const char* interface_name,
 
  361                                         const char* property_name, GError** error,
 
  362                                         void* user_data) -> GVariant*
 
  366    if (g_strcmp0(property_name, 
"PlaybackStatus") == 0)
 
  367      return g_variant_new_string(
"Playing");
 
  368    if (g_strcmp0(property_name, 
"LoopStatus") == 0)
 
  369      return g_variant_new_string(
"None");
 
  370    if (g_strcmp0(property_name, 
"Rate") == 0)
 
  371      return g_variant_new_double(1.0);
 
  372    if (g_strcmp0(property_name, 
"Shuffle") == 0)
 
  373      return g_variant_new_boolean(FALSE);
 
  374    if (g_strcmp0(property_name, 
"Metadata") == 0)
 
  375      return service->current_metadata_ ? g_variant_ref(service->current_metadata_)
 
  376                                        : g_variant_new(
"a{sv}", nullptr);
 
  377    if (g_strcmp0(property_name, 
"Volume") == 0)
 
  378      return g_variant_new_double(1.0);
 
  379    if (g_strcmp0(property_name, 
"Position") == 0)
 
  380      return g_variant_new_int64(0);
 
  381    if (g_strcmp0(property_name, 
"MinimumRate") == 0)
 
  382      return g_variant_new_double(1.0);
 
  383    if (g_strcmp0(property_name, 
"MaximumRate") == 0)
 
  384      return g_variant_new_double(1.0);
 
  385    if (g_strcmp0(property_name, 
"CanGoNext") == 0)
 
  386      return g_variant_new_boolean(TRUE);
 
  387    if (g_strcmp0(property_name, 
"CanGoPrevious") == 0)
 
  388      return g_variant_new_boolean(TRUE);
 
  389    if (g_strcmp0(property_name, 
"CanPlay") == 0)
 
  390      return g_variant_new_boolean(TRUE);
 
  391    if (g_strcmp0(property_name, 
"CanPause") == 0)
 
  392      return g_variant_new_boolean(TRUE);
 
  393    if (g_strcmp0(property_name, 
"CanSeek") == 0)
 
  394      return g_variant_new_boolean(FALSE);
 
  395    if (g_strcmp0(property_name, 
"CanControl") == 0)
 
  396      return g_variant_new_boolean(TRUE);
 
  399    g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED,
 
  400                "Property '%s' is not recognized for interface '%s'.", property_name,
 
  418  static auto handle_player_set_property(GDBusConnection*, 
const char*, 
const char*, 
const char*,
 
  419                                         const char*, GVariant*, GError**, 
void*) -> gboolean
 
  424  std::string      applicationName_;
 
  425  GDBusNodeInfo*   introspection_data_ = 
nullptr;
 
  426  GDBusConnection* connection_         = 
nullptr;
 
  427  GVariant*        current_metadata_   = 
nullptr;