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;