43 MPRISService(
const std::string& applicationName) : applicationName_(applicationName)
46 const char* introspection_xml = R
"XML(
47<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
48 "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
50 <interface name="org.mpris.MediaPlayer2">
51 <property name="Identity" type="s" access="read"/>
52 <property name="DesktopEntry" type="s" access="read"/>
53 <property name="SupportedUriSchemes" type="as" access="read"/>
54 <property name="SupportedMimeTypes" type="as" access="read"/>
55 <property name="CanQuit" type="b" access="read"/>
56 <property name="CanRaise" type="b" access="read"/>
57 <property name="HasTrackList" type="b" access="read"/>
59 <interface name="org.mpris.MediaPlayer2.Player">
60 <property name="PlaybackStatus" type="s" access="read"/>
61 <property name="LoopStatus" type="s" access="readwrite"/>
62 <property name="Rate" type="d" access="readwrite"/>
63 <property name="Shuffle" type="b" access="readwrite"/>
64 <property name="Metadata" type="a{sv}" access="read"/>
65 <property name="Volume" type="d" access="readwrite"/>
66 <property name="Position" type="x" access="read"/>
67 <property name="MinimumRate" type="d" access="read"/>
68 <property name="MaximumRate" type="d" access="read"/>
69 <property name="CanGoNext" type="b" access="read"/>
70 <property name="CanGoPrevious" type="b" access="read"/>
71 <property name="CanPlay" type="b" access="read"/>
72 <property name="CanPause" type="b" access="read"/>
73 <property name="CanSeek" type="b" access="read"/>
74 <property name="CanControl" type="b" access="read"/>
76 <method name="Previous"/>
77 <method name="Pause"/>
78 <method name="PlayPause"/>
82 <arg name="Offset" type="x" direction="in"/>
84 <method name="SetPosition">
85 <arg name="TrackId" type="o" direction="in"/>
86 <arg name="Position" type="x" direction="in"/>
88 <method name="OpenUri">
89 <arg name="Uri" type="s" direction="in"/>
94 GError* error = nullptr;
95 introspection_data_ = g_dbus_node_info_new_for_xml(introspection_xml, &error);
98 std::cerr <<
"Error creating introspection data: " << error->message << std::endl;
103 connection_ = g_bus_get_sync(G_BUS_TYPE_SESSION,
nullptr, &error);
104 if (error !=
nullptr)
106 std::cerr <<
"Error connecting to D-Bus: " << error->message << std::endl;
112 const GDBusInterfaceVTable root_vtable = {
113 handle_root_method_call, handle_root_get_property,
nullptr, {
nullptr}};
115 const GDBusInterfaceVTable player_vtable = {
116 handle_player_method_call, handle_player_get_property, handle_player_set_property, {
nullptr}};
118 std::string busName =
"org.mpris.MediaPlayer2." + applicationName_;
120 g_bus_own_name(G_BUS_TYPE_SESSION, busName.c_str(), G_BUS_NAME_OWNER_FLAGS_REPLACE,
121 on_bus_acquired,
nullptr,
nullptr,
this,
nullptr);
124 g_dbus_connection_register_object(connection_,
"/org/mpris/MediaPlayer2",
125 introspection_data_->interfaces[0],
126 &root_vtable,
this,
nullptr, &error);
128 g_dbus_connection_register_object(connection_,
"/org/mpris/MediaPlayer2",
129 introspection_data_->interfaces[1],
130 &player_vtable,
this,
nullptr, &error);
132 if (error !=
nullptr)
134 std::cerr <<
"Error registering object: " << error->message << std::endl;
139 updateMetadata(
"Unknown Song",
"Unknown Artist",
"Unknown Album", 0,
"No Comment",
"No genre",
152 g_object_unref(connection_);
153 connection_ =
nullptr;
155 if (introspection_data_)
157 g_dbus_node_info_unref(introspection_data_);
158 introspection_data_ =
nullptr;
160 if (current_metadata_)
162 g_variant_unref(current_metadata_);
163 current_metadata_ =
nullptr;
165 std::cout <<
"-- MPRISService cleaned up." << std::endl;
185 void updateMetadata(
const std::string& title,
const std::string& artist,
const std::string& album,
186 int64_t length,
const std::string& comment =
"",
187 const std::string& genre =
"",
int trackNumber = 0,
int discNumber = 0)
189 GVariantBuilder builder;
190 g_variant_builder_init(&builder, G_VARIANT_TYPE(
"a{sv}"));
193 std::string trackid =
"/org/mpris/MediaPlayer2/Track/" + std::to_string(rand());
194 g_variant_builder_add(&builder,
"{sv}",
"mpris:trackid",
195 g_variant_new_object_path(trackid.c_str()));
197 g_variant_builder_add(&builder,
"{sv}",
"xesam:title", g_variant_new_string(title.c_str()));
199 GVariantBuilder artist_builder;
200 g_variant_builder_init(&artist_builder, G_VARIANT_TYPE(
"as"));
201 g_variant_builder_add(&artist_builder,
"s", artist.c_str());
202 g_variant_builder_add(&builder,
"{sv}",
"xesam:artist", g_variant_builder_end(&artist_builder));
204 g_variant_builder_add(&builder,
"{sv}",
"xesam:album", g_variant_new_string(album.c_str()));
206 g_variant_builder_add(&builder,
"{sv}",
"mpris:length", g_variant_new_int64(length * 1000000));
209 if (!comment.empty())
211 g_variant_builder_add(&builder,
"{sv}",
"xesam:comment",
212 g_variant_new_string(comment.c_str()));
216 GVariantBuilder genre_builder;
217 g_variant_builder_init(&genre_builder, G_VARIANT_TYPE(
"as"));
218 g_variant_builder_add(&genre_builder,
"s", genre.c_str());
219 g_variant_builder_add(&builder,
"{sv}",
"xesam:genre", g_variant_builder_end(&genre_builder));
223 g_variant_builder_add(&builder,
"{sv}",
"xesam:trackNumber",
224 g_variant_new_int32(trackNumber));
228 g_variant_builder_add(&builder,
"{sv}",
"xesam:discNumber", g_variant_new_int32(discNumber));
232 if (current_metadata_)
233 g_variant_unref(current_metadata_);
234 current_metadata_ = g_variant_builder_end(&builder);
235 g_variant_ref(current_metadata_);
238 GVariantBuilder props_builder;
239 g_variant_builder_init(&props_builder, G_VARIANT_TYPE_ARRAY);
240 g_variant_builder_add(&props_builder,
"{sv}",
"Metadata", current_metadata_);
242 g_dbus_connection_emit_signal(
243 connection_,
nullptr,
"/org/mpris/MediaPlayer2",
"org.freedesktop.DBus.Properties",
245 g_variant_new(
"(sa{sv}as)",
"org.mpris.MediaPlayer2.Player", &props_builder,
nullptr),
260 static void on_bus_acquired(GDBusConnection* connection,
const gchar* name, gpointer user_data)
262 std::cout <<
"Acquired bus name: " << name << std::endl;
280 static void handle_root_method_call(GDBusConnection*,
const char*,
const char*,
const char*,
281 const char*, GVariant*, GDBusMethodInvocation* invocation,
284 g_dbus_method_invocation_return_value(invocation,
nullptr);
301 static void handle_player_method_call(GDBusConnection*,
const char*,
const char*,
const char*,
302 const char* method_name, GVariant*,
303 GDBusMethodInvocation* invocation,
void* user_data)
306 g_dbus_method_invocation_return_value(invocation,
nullptr);
323 static GVariant* handle_root_get_property(GDBusConnection* connection,
const char* sender,
324 const char* object_path,
const char* interface_name,
325 const char* property_name, GError** error,
330 if (g_strcmp0(property_name,
"Identity") == 0)
331 return g_variant_new_string(service->applicationName_.c_str());
332 if (g_strcmp0(property_name,
"CanQuit") == 0)
333 return g_variant_new_boolean(TRUE);
334 if (g_strcmp0(property_name,
"CanRaise") == 0)
335 return g_variant_new_boolean(TRUE);
336 if (g_strcmp0(property_name,
"HasTrackList") == 0)
337 return g_variant_new_boolean(FALSE);
340 g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED,
341 "Property '%s' is not recognized for interface '%s'.", property_name,
361 static GVariant* handle_player_get_property(GDBusConnection* connection,
const char* sender,
362 const char* object_path,
const char* interface_name,
363 const char* property_name, GError** error,
368 if (g_strcmp0(property_name,
"PlaybackStatus") == 0)
369 return g_variant_new_string(
"Playing");
370 if (g_strcmp0(property_name,
"LoopStatus") == 0)
371 return g_variant_new_string(
"None");
372 if (g_strcmp0(property_name,
"Rate") == 0)
373 return g_variant_new_double(1.0);
374 if (g_strcmp0(property_name,
"Shuffle") == 0)
375 return g_variant_new_boolean(FALSE);
376 if (g_strcmp0(property_name,
"Metadata") == 0)
377 return service->current_metadata_ ? g_variant_ref(service->current_metadata_)
378 : g_variant_new(
"a{sv}", nullptr);
379 if (g_strcmp0(property_name,
"Volume") == 0)
380 return g_variant_new_double(1.0);
381 if (g_strcmp0(property_name,
"Position") == 0)
382 return g_variant_new_int64(0);
383 if (g_strcmp0(property_name,
"MinimumRate") == 0)
384 return g_variant_new_double(1.0);
385 if (g_strcmp0(property_name,
"MaximumRate") == 0)
386 return g_variant_new_double(1.0);
387 if (g_strcmp0(property_name,
"CanGoNext") == 0)
388 return g_variant_new_boolean(TRUE);
389 if (g_strcmp0(property_name,
"CanGoPrevious") == 0)
390 return g_variant_new_boolean(TRUE);
391 if (g_strcmp0(property_name,
"CanPlay") == 0)
392 return g_variant_new_boolean(TRUE);
393 if (g_strcmp0(property_name,
"CanPause") == 0)
394 return g_variant_new_boolean(TRUE);
395 if (g_strcmp0(property_name,
"CanSeek") == 0)
396 return g_variant_new_boolean(FALSE);
397 if (g_strcmp0(property_name,
"CanControl") == 0)
398 return g_variant_new_boolean(TRUE);
401 g_set_error(error, G_IO_ERROR, G_IO_ERROR_FAILED,
402 "Property '%s' is not recognized for interface '%s'.", property_name,
420 static gboolean handle_player_set_property(GDBusConnection*,
const char*,
const char*,
421 const char*,
const char*, GVariant*, GError**,
void*)
426 std::string applicationName_;
427 GDBusNodeInfo* introspection_data_ =
nullptr;
428 GDBusConnection* connection_ =
nullptr;
429 GVariant* current_metadata_ =
nullptr;