diff --git a/src/koto-playerbar.c b/src/koto-playerbar.c index adbacc3..a3c3d94 100644 --- a/src/koto-playerbar.c +++ b/src/koto-playerbar.c @@ -132,6 +132,8 @@ static void koto_playerbar_constructed(GObject *obj) { g_signal_connect(playback_engine, "tick-duration", G_CALLBACK(koto_playerbar_handle_tick_duration), self); g_signal_connect(playback_engine, "tick-track", G_CALLBACK(koto_playerbar_handle_tick_track), self); g_signal_connect(playback_engine, "track-changed", G_CALLBACK(koto_playerbar_update_track_info), self); + g_signal_connect(playback_engine, "track-repeat-changed", G_CALLBACK(koto_playerbar_handle_track_repeat), self); + g_signal_connect(playback_engine, "track-shuffle-changed", G_CALLBACK(koto_playerbar_handle_track_shuffle), self); } static void koto_playerbar_init(KotoPlayerBar *self) { @@ -290,7 +292,6 @@ void koto_playerbar_handle_is_paused(KotoPlaybackEngine *engine, gpointer user_d void koto_playerbar_handle_progressbar_scroll_begin(GtkEventControllerScroll *controller, gpointer data){ (void) controller; - g_message("scroll-begin"); } void koto_playerbar_handle_progressbar_gesture_begin(GtkGesture *gesture, GdkEventSequence *seq, gpointer data) { @@ -301,7 +302,6 @@ void koto_playerbar_handle_progressbar_gesture_begin(GtkGesture *gesture, GdkEve return; } - g_message("Begin"); bar->is_progressbar_seeking = TRUE; } @@ -309,8 +309,6 @@ void koto_playerbar_handle_progressbar_gesture_end(GtkGesture *gesture, GdkEvent (void) gesture; (void) seq; KotoPlayerBar *bar = data; - g_message("Ended"); - if (!KOTO_IS_PLAYERBAR(bar)) { return; } @@ -325,7 +323,6 @@ void koto_playerbar_handle_progressbar_pressed(GtkGestureClick *gesture, int n_p return; } - g_message("Pressed"); bar->is_progressbar_seeking = TRUE; } @@ -342,7 +339,6 @@ void koto_playerbar_handle_progressbar_value_changed(GtkRange *progress_bar, gpo int desired_position = (int) gtk_range_get_value(progress_bar); - g_message("value changed"); koto_playback_engine_set_position(playback_engine, desired_position); // Update our position } @@ -377,6 +373,42 @@ void koto_playerbar_handle_tick_track(KotoPlaybackEngine *engine, gpointer user_ } } +void koto_playerbar_handle_track_repeat(KotoPlaybackEngine *engine, gpointer user_data) { + if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { + return; + } + + KotoPlayerBar *bar = user_data; + + if (!KOTO_IS_PLAYERBAR(bar)) { + return; + } + + if (koto_playback_engine_get_track_repeat(engine)) { // Is repeating + gtk_widget_add_css_class(GTK_WIDGET(bar->repeat_button), "active"); // Add active CSS class + } else { // Is not repeating + gtk_widget_remove_css_class(GTK_WIDGET(bar->repeat_button), "active"); // Remove active CSS class + } +} + +void koto_playerbar_handle_track_shuffle(KotoPlaybackEngine *engine, gpointer user_data) { + if (!KOTO_IS_PLAYBACK_ENGINE(engine)) { + return; + } + + KotoPlayerBar *bar = user_data; + + if (!KOTO_IS_PLAYERBAR(bar)) { + return; + } + + if (koto_playback_engine_get_track_shuffle(engine)) { // Is repeating + gtk_widget_add_css_class(GTK_WIDGET(bar->shuffle_button), "active"); // Add active CSS class + } else { // Is not repeating + gtk_widget_remove_css_class(GTK_WIDGET(bar->shuffle_button), "active"); // Remove active CSS class + } +} + void koto_playerbar_handle_volume_button_change(GtkScaleButton *button, double value, gpointer user_data) { (void) button; (void) user_data; koto_playback_engine_set_volume(playback_engine, value); @@ -409,38 +441,14 @@ void koto_playerbar_toggle_play_pause(GtkGestureClick *gesture, int n_press, dou } void koto_playerbar_toggle_playlist_shuffle(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { - (void) gesture; (void) n_press; (void) x; (void) y; - KotoPlayerBar *bar = data; + (void) gesture; (void) n_press; (void) x; (void) y; (void) data; - if (!KOTO_IS_PLAYERBAR(bar)) { - return; - } - - KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); - - if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently - gtk_widget_remove_css_class(GTK_WIDGET(bar->shuffle_button), "active"); // Remove active state - return; - } - - gboolean currently_shuffling = FALSE; - g_object_get(playlist, "is-shuffle-enabled", ¤tly_shuffling, NULL); // Get the current is-shuffle-enabled - - (currently_shuffling) ? gtk_widget_remove_css_class(GTK_WIDGET(bar->shuffle_button), "active") : gtk_widget_add_css_class(GTK_WIDGET(bar->shuffle_button), "active"); - g_object_set(playlist, "is-shuffle-enabled", !currently_shuffling, NULL); // Provide inverse value + koto_playback_engine_toggle_track_shuffle(playback_engine); // Call our playback engine's toggle track shuffle function } void koto_playerbar_toggle_track_repeat(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) { - (void) gesture; (void) n_press; (void) x; (void) y; - KotoPlayerBar *bar = data; - - if (koto_playback_engine_get_track_repeat(playback_engine)) { // Toggled on at the moment - gtk_widget_remove_css_class(GTK_WIDGET(bar->repeat_button), "active"); // Remove active CSS class - } else { - gtk_widget_add_css_class(GTK_WIDGET(bar->repeat_button), "active"); // Add active CSS class - } - - koto_playback_engine_toggle_track_repeat(playback_engine); // Toggle the state + (void) gesture; (void) n_press; (void) x; (void) y; (void) data; + koto_playback_engine_toggle_track_repeat(playback_engine); // Call our playback engine's toggle track repeat function } void koto_playerbar_update_track_info(KotoPlaybackEngine *engine, gpointer user_data) { diff --git a/src/koto-playerbar.h b/src/koto-playerbar.h index 70756dd..c45c0e9 100644 --- a/src/koto-playerbar.h +++ b/src/koto-playerbar.h @@ -42,6 +42,8 @@ void koto_playerbar_handle_progressbar_pressed(GtkGestureClick *gesture, int n_p void koto_playerbar_handle_progressbar_value_changed(GtkRange *progress_bar, gpointer data); void koto_playerbar_handle_tick_duration(KotoPlaybackEngine *engine, gpointer user_data); void koto_playerbar_handle_tick_track(KotoPlaybackEngine *engine, gpointer user_data); +void koto_playerbar_handle_track_repeat(KotoPlaybackEngine *engine, gpointer user_data); +void koto_playerbar_handle_track_shuffle(KotoPlaybackEngine *engine, gpointer user_data); void koto_playerbar_handle_volume_button_change(GtkScaleButton *button, double value, gpointer user_data); void koto_playerbar_reset_progressbar(KotoPlayerBar* bar); void koto_playerbar_set_progressbar_duration(KotoPlayerBar* bar, gint64 duration); diff --git a/src/main.c b/src/main.c index 0eed029..bdc464b 100644 --- a/src/main.c +++ b/src/main.c @@ -70,19 +70,10 @@ int main (int argc, char *argv[]) { supported_mimes = NULL; // Ensure our mimes GList is initialized koto_playback_engine_get_supported_mimetypes(supported_mimes); - g_message("Length: %d", g_list_length(supported_mimes)); - koto_maps = koto_cartographer_new(); // Create our new cartographer and their collection of maps open_db(); // Open our database setup_mpris_interfaces(); // Set up our MPRIS interfaces - GList *md; - md = NULL; - for (md = supported_mimes; md != NULL; md = md->next) { - g_message("Mimetype: %s", (gchar*) md->data); - } - g_list_free(md); - app = gtk_application_new ("com.github.joshstrobl.koto", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); g_signal_connect(app, "shutdown", G_CALLBACK(on_shutdown), NULL); diff --git a/src/playback/engine.c b/src/playback/engine.c index ce05cd3..7b5e403 100644 --- a/src/playback/engine.c +++ b/src/playback/engine.c @@ -31,6 +31,8 @@ enum { SIGNAL_TICK_DURATION, SIGNAL_TICK_TRACK, SIGNAL_TRACK_CHANGE, + SIGNAL_TRACK_REPEAT_CHANGE, + SIGNAL_TRACK_SHUFFLE_CHANGE, N_SIGNALS }; @@ -72,6 +74,8 @@ struct _KotoPlaybackEngineClass { void (* tick_duration) (KotoPlaybackEngine *engine); void (* tick_track) (KotoPlaybackEngine *engine); void (* track_changed) (KotoPlaybackEngine *engine); + void (* track_repeat_changed) (KotoPlaybackEngine *engine); + void (* track_shuffle_changed) (KotoPlaybackEngine *engine); }; G_DEFINE_TYPE(KotoPlaybackEngine, koto_playback_engine, G_TYPE_OBJECT); @@ -153,6 +157,30 @@ static void koto_playback_engine_class_init(KotoPlaybackEngineClass *c) { G_TYPE_NONE, 0 ); + + playback_engine_signals[SIGNAL_TRACK_REPEAT_CHANGE] = g_signal_new( + "track-repeat-changed", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(KotoPlaybackEngineClass, track_repeat_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0 + ); + + playback_engine_signals[SIGNAL_TRACK_SHUFFLE_CHANGE] = g_signal_new( + "track-shuffle-changed", + G_TYPE_FROM_CLASS(gobject_class), + G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET(KotoPlaybackEngineClass, track_shuffle_changed), + NULL, + NULL, + NULL, + G_TYPE_NONE, + 0 + ); } static void koto_playback_engine_init(KotoPlaybackEngine *self) { @@ -255,6 +283,20 @@ gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine *self) { return self->is_repeat_enabled; } +gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine *self) { + (void) self; + KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); + + if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently + return FALSE; + } + + gboolean currently_shuffling = FALSE; + g_object_get(playlist, "is-shuffle-enabled", ¤tly_shuffling, NULL); // Get the current is-shuffle-enabled + + return currently_shuffling; +} + gboolean koto_playback_engine_monitor_changed(GstBus *bus, GstMessage *msg, gpointer user_data) { (void) bus; KotoPlaybackEngine *self = user_data; @@ -308,19 +350,34 @@ void koto_playback_engine_play(KotoPlaybackEngine *self) { self->tick_track_timer_running = TRUE; g_timeout_add(100, koto_playback_engine_tick_track, self); // Create a 100ms track tick } + + koto_update_mpris_playback_state(GST_STATE_PLAYING); } void koto_playback_engine_pause(KotoPlaybackEngine *self) { self->is_playing = FALSE; gst_element_change_state(self->player, GST_STATE_CHANGE_PLAYING_TO_PAUSED); + koto_update_mpris_playback_state(GST_STATE_PAUSED); } void koto_playback_engine_set_position(KotoPlaybackEngine *self, int position) { gst_element_seek_simple(self->playbin, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, position * NS); } -void koto_playback_engine_set_track_repeat(KotoPlaybackEngine *self, gboolean enable) { - self->is_repeat_enabled = enable; +void koto_playback_engine_set_track_repeat(KotoPlaybackEngine *self, gboolean enable_repeat) { + self->is_repeat_enabled = enable_repeat; + g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_REPEAT_CHANGE], 0); // Emit our track repeat changed event +} + +void koto_playback_engine_set_track_shuffle(KotoPlaybackEngine *self, gboolean enable_shuffle) { + KotoPlaylist *playlist = koto_current_playlist_get_playlist(current_playlist); + + if (!KOTO_IS_PLAYLIST(playlist)) { // Don't have a playlist currently + return; + } + + g_object_set(playlist, "is-shuffle-enabled", enable_shuffle, NULL); // Set the is-shuffle-enabled on any existing playlist + g_signal_emit(self, playback_engine_signals[SIGNAL_TRACK_SHUFFLE_CHANGE], 0); // Emit our track shuffle changed event } void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *track_uuid) { @@ -370,6 +427,7 @@ void koto_playback_engine_stop(KotoPlaybackEngine *self) { } gst_pad_set_offset(pad, 0); // Change offset + koto_update_mpris_playback_state(GST_STATE_NULL); } void koto_playback_engine_toggle(KotoPlaybackEngine *self) { @@ -405,7 +463,11 @@ gboolean koto_playback_engine_tick_track(gpointer user_data) { } void koto_playback_engine_toggle_track_repeat(KotoPlaybackEngine *self) { - self->is_repeat_enabled = !self->is_repeat_enabled; + koto_playback_engine_set_track_repeat(self, !self->is_repeat_enabled); +} + +void koto_playback_engine_toggle_track_shuffle(KotoPlaybackEngine *self) { + koto_playback_engine_set_track_shuffle(self, !koto_playback_engine_get_track_shuffle(self)); // Invert the currently shuffling vale } KotoPlaybackEngine* koto_playback_engine_new() { diff --git a/src/playback/engine.h b/src/playback/engine.h index 0c503c1..fcc4ad5 100644 --- a/src/playback/engine.h +++ b/src/playback/engine.h @@ -50,17 +50,20 @@ gint64 koto_playback_engine_get_duration(KotoPlaybackEngine *self); GstState koto_playback_engine_get_state(KotoPlaybackEngine *self); gint64 koto_playback_engine_get_progress(KotoPlaybackEngine *self); gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine *self); +gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine *self); void koto_playback_engine_mute(KotoPlaybackEngine *self); gboolean koto_playback_engine_monitor_changed(GstBus *bus, GstMessage *msg, gpointer user_data); void koto_playback_engine_pause(KotoPlaybackEngine *self); void koto_playback_engine_play(KotoPlaybackEngine *self); void koto_playback_engine_toggle(KotoPlaybackEngine *self); void koto_playback_engine_set_position(KotoPlaybackEngine *self, int position); -void koto_playback_engine_set_track_repeat(KotoPlaybackEngine *self, gboolean enable); +void koto_playback_engine_set_track_repeat(KotoPlaybackEngine *self, gboolean enable_repeat); +void koto_playback_engine_set_track_shuffle(KotoPlaybackEngine *self, gboolean enable_shuffle); void koto_playback_engine_set_track_by_uuid(KotoPlaybackEngine *self, gchar *track_uuid); void koto_playback_engine_set_volume(KotoPlaybackEngine *self, gdouble volume); void koto_playback_engine_stop(KotoPlaybackEngine *self); void koto_playback_engine_toggle_track_repeat(KotoPlaybackEngine *self); +void koto_playback_engine_toggle_track_shuffle(KotoPlaybackEngine *self); void koto_playback_engine_update_duration(KotoPlaybackEngine *self); gboolean koto_playback_engine_tick_duration(gpointer user_data); diff --git a/src/playback/mpris.c b/src/playback/mpris.c index e0e5905..bf388a6 100644 --- a/src/playback/mpris.c +++ b/src/playback/mpris.c @@ -23,8 +23,9 @@ #include "../db/cartographer.h" #include "../playlist/current.h" #include "../playlist/playlist.h" -#include "mimes.h" #include "engine.h" +#include "mimes.h" +#include "mpris.h" extern KotoCartographer *koto_maps; extern KotoCurrentPlaylist *current_playlist; @@ -82,7 +83,7 @@ static const gchar introspection_xml[] = " " ""; -void handle_main_mpris_method_call( +void handle_method_call( GDBusConnection *connection, const gchar *sender, const gchar *object_path, @@ -92,6 +93,8 @@ void handle_main_mpris_method_call( GDBusMethodInvocation *invocation, gpointer user_data ) { + (void) connection; (void) sender; (void) object_path; (void) invocation; (void) user_data; + if (g_strcmp0(interface_name, "org.mpris.MediaPlayer2") == 0) { // Root mediaplayer2 interface if (g_strcmp0(method_name, "Raise") == 0) { // Raise the window gtk_window_unminimize(main_window); // Ensure we unminimize the window @@ -103,7 +106,7 @@ void handle_main_mpris_method_call( gtk_application_remove_window(app, main_window); // Remove the window, thereby closing the app return; } - } else if (g_strcmp0(interface_name, "org.mpris.MediaPlayer2.koto") == 0) { // Root mediaplayer2 interface + } else if (g_strcmp0(interface_name, "org.mpris.MediaPlayer2.Player") == 0) { // Root mediaplayer2 interface if (g_strcmp0(method_name, "Next") == 0) { // Next track koto_playback_engine_forwards(playback_engine); return; @@ -113,6 +116,26 @@ void handle_main_mpris_method_call( koto_playback_engine_backwards(playback_engine); return; } + + if (g_strcmp0(method_name, "Pause") == 0) { // Explicit pause + koto_playback_engine_pause(playback_engine); + return; + } + + if (g_strcmp0(method_name, "PlayPause") == 0) { // Explicit playpause + koto_playback_engine_toggle(playback_engine); + return; + } + + if (g_strcmp0(method_name, "Stop") == 0) { // Explicit stop + koto_playback_engine_stop(playback_engine); + return; + } + + if (g_strcmp0(method_name, "Play") == 0) { // Explicit play + koto_playback_engine_play(playback_engine); + return; + } } } @@ -125,11 +148,10 @@ GVariant* handle_get_property( GError **error, gpointer user_data ) { + (void) connection; (void) sender; (void) object_path; (void) interface_name; (void) error; (void) user_data; GVariant *ret; ret = NULL; - g_message("asking for %s", property_name); - if (g_strcmp0(property_name, "CanQuit") == 0) { // If property is CanQuit ret = g_variant_new_boolean(TRUE); // Allow quitting. You can escape Hotel California for now. } @@ -190,13 +212,16 @@ GVariant* handle_get_property( if ( (g_strcmp0(property_name, "CanPlay") == 0) || - (g_strcmp0(property_name, "CanPause") == 0) || - (g_strcmp0(property_name, "CanSeek") == 0) + (g_strcmp0(property_name, "CanPause") == 0) ) { KotoIndexedTrack *current_track = koto_playback_engine_get_current_track(playback_engine); ret = g_variant_new_boolean(KOTO_IS_INDEXED_TRACK(current_track)); } + if (g_strcmp0(property_name, "CanSeek") == 0) { // Can control position over mpris + ret = g_variant_new_boolean(FALSE); + } + if (g_strcmp0(property_name, "CanGoNext") == 0) { // Can Go Next // TODO: Add some actual logic here ret = g_variant_new_boolean(TRUE); @@ -226,6 +251,31 @@ GVariant* handle_get_property( return ret; } +gboolean handle_set_property( + GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data +) { + (void) connection; (void) sender; (void) interface_name; (void) object_path; (void) error; (void) user_data; + + if (g_strcmp0(property_name, "LoopStatus") == 0) { // Changing LoopStatus + koto_playback_engine_set_track_repeat(playback_engine, g_variant_get_boolean(value)); // Set the loop status state + return TRUE; + } + + if (g_strcmp0(property_name, "Shuffle") == 0) { // Changing Shuffle + koto_playback_engine_set_track_shuffle(playback_engine, g_variant_get_boolean(value)); // Set the shuffle state + return TRUE; + } + + return FALSE; +} + void koto_push_track_info_to_builder(GVariantBuilder *builder, KotoIndexedTrack *track) { if (!KOTO_IS_INDEXED_TRACK(track)) { return; @@ -266,9 +316,8 @@ void koto_push_track_info_to_builder(GVariantBuilder *builder, KotoIndexedTrack g_variant_builder_add(builder, "{sv}", "mpris:trackid", g_variant_new_string(track_uuid)); - g_message("Art path: %s", album_art_path); if (g_strcmp0(album_art_path, "") != 0) { // Not empty - album_art_path = g_strconcat("file://", album_art_path); // Prepend with file:// + album_art_path = g_strconcat("file://", album_art_path, NULL); // Prepend with file:// g_variant_builder_add(builder, "{sv}", "mpris:artUrl", g_variant_new_string(album_art_path)); } @@ -290,6 +339,27 @@ void koto_push_track_info_to_builder(GVariantBuilder *builder, KotoIndexedTrack g_variant_builder_add(builder, "{sv}", "xesam:trackNumber", g_variant_new_uint64(track_position)); } +void koto_update_mpris_playback_state(GstState state) { + GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); + + if (state == GST_STATE_PLAYING) { + g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Playing")); + } else if (state == GST_STATE_PAUSED) { + g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Paused")); + } else { + g_variant_builder_add(builder, "{sv}", "PlaybackStatus", g_variant_new_string("Stopped")); + } + + g_dbus_connection_emit_signal(dbus_conn, + NULL, + "/org/mpris/MediaPlayer2", + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + g_variant_new("(sa{sv}as)", "org.mpris.MediaPlayer2.Player", builder, NULL), + NULL + ); +} + void koto_update_mpris_info_for_track(KotoIndexedTrack *track) { if (!KOTO_IS_INDEXED_TRACK(track)) { return; @@ -297,7 +367,6 @@ void koto_update_mpris_info_for_track(KotoIndexedTrack *track) { GVariantBuilder *builder = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); GVariantBuilder *metadata_builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); - GError *error = NULL; koto_push_track_info_to_builder(metadata_builder, track); GVariant *metadata_ret = g_variant_builder_end(metadata_builder); @@ -309,16 +378,18 @@ void koto_update_mpris_info_for_track(KotoIndexedTrack *track) { "org.freedesktop.DBus.Properties", "PropertiesChanged", g_variant_new("(sa{sv}as)", "org.mpris.MediaPlayer2.Player", builder, NULL), - &error + NULL ); } static const GDBusInterfaceVTable main_mpris_interface_vtable = { - handle_main_mpris_method_call, + handle_method_call, handle_get_property, + handle_set_property, }; void on_main_mpris_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data) { + (void) name; (void) user_data; dbus_conn = connection; g_dbus_connection_register_object(dbus_conn, "/org/mpris/MediaPlayer2", diff --git a/src/playback/mpris.h b/src/playback/mpris.h index 97734c9..ce15e02 100644 --- a/src/playback/mpris.h +++ b/src/playback/mpris.h @@ -17,11 +17,15 @@ #include #include +#include +#include #include "../indexer/structs.h" void koto_push_track_info_to_builder(GVariantBuilder *builder, KotoIndexedTrack *track); +void koto_update_mpris_playback_state(GstState state); void koto_update_mpris_info_for_track(KotoIndexedTrack *track); -void handle_main_mpris_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data); +void handle_method_call(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *method_name, GVariant *parameters, GDBusMethodInvocation *invocation, gpointer user_data); GVariant* handle_get_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GError **error, gpointer user_data); +gboolean handle_set_property(GDBusConnection *connection, const gchar *sender, const gchar *object_path, const gchar *interface_name, const gchar *property_name, GVariant *value, GError **error, gpointer user_data); void on_main_mpris_bus_acquired(GDBusConnection *connection, const gchar *name, gpointer user_data); void setup_mpris_interfaces();