summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Withnall <philip@tecnocode.co.uk>2010-04-25 14:43:00 +0100
committerBastien Nocera <hadess@hadess.net>2010-04-27 12:51:50 +0100
commit81a459d8d28d3a4b8ca6463859a53b34dbd0d416 (patch)
tree5fee5193729281cf9b350d462697ad9e39caca14
parent44ec0266dfdb28976da064680e1b508d0fed584e (diff)
downloadtotem-81a459d8d28d3a4b8ca6463859a53b34dbd0d416.tar.gz
Bug 559628 — Support async loading of playlists
Add async playlist support. This converts most calls to the playlist add MRL function to be async, dealing with the cursor appropriately (such that concurrent operations using a busy cursor don't trample on each others' cursors when they finish). Closes: bgo#559628
-rw-r--r--src/plugins/publish/totem-publish.c2
-rw-r--r--src/totem-object.c79
-rw-r--r--src/totem-playlist.c166
-rw-r--r--src/totem-playlist.h18
-rw-r--r--src/totem-session.c4
-rw-r--r--src/totem-video-list.c2
6 files changed, 192 insertions, 79 deletions
diff --git a/src/plugins/publish/totem-publish.c b/src/plugins/publish/totem-publish.c
index 8399d645f..20ac7f55f 100644
--- a/src/plugins/publish/totem-publish.c
+++ b/src/plugins/publish/totem-publish.c
@@ -434,7 +434,7 @@ totem_publish_plugin_load_playlist (TotemPublishPlugin *self,
g_free (key);
if (mrl)
- totem_playlist_add_mrl (self->totem->playlist, mrl, title);
+ totem_playlist_add_mrl (self->totem->playlist, mrl, title, FALSE, NULL, NULL, NULL);
g_free (title);
g_free (mrl);
diff --git a/src/totem-object.c b/src/totem-object.c
index 57af6e80f..74aa06fb2 100644
--- a/src/totem-object.c
+++ b/src/totem-object.c
@@ -447,6 +447,45 @@ totem_get_current_time (Totem *totem)
return bacon_video_widget_get_current_time (totem->bvw);
}
+typedef struct {
+ Totem *totem;
+ gchar *uri;
+ gchar *display_name;
+ gboolean add_to_recent;
+} AddToPlaylistData;
+
+static void
+add_to_playlist_and_play_cb (TotemPlaylist *playlist, GAsyncResult *async_result, AddToPlaylistData *data)
+{
+ int end;
+ gboolean playlist_changed;
+
+ playlist_changed = totem_playlist_add_mrl_finish (playlist, async_result);
+
+ if (data->add_to_recent != FALSE)
+ gtk_recent_manager_add_item (data->totem->recent_manager, data->uri);
+ end = totem_playlist_get_last (playlist);
+
+ totem_signal_unblock_by_data (playlist, data->totem);
+
+ if (playlist_changed && end != -1) {
+ char *mrl, *subtitle;
+
+ subtitle = NULL;
+ totem_playlist_set_current (playlist, end);
+ mrl = totem_playlist_get_current_mrl (playlist, &subtitle);
+ totem_action_set_mrl_and_play (data->totem, mrl, subtitle);
+ g_free (mrl);
+ g_free (subtitle);
+ }
+
+ /* Free the closure data */
+ g_object_unref (data->totem);
+ g_free (data->uri);
+ g_free (data->display_name);
+ g_slice_free (AddToPlaylistData, data);
+}
+
/**
* totem_add_to_playlist_and_play:
* @totem: a #TotemObject
@@ -462,28 +501,21 @@ totem_add_to_playlist_and_play (Totem *totem,
const char *display_name,
gboolean add_to_recent)
{
- gboolean playlist_changed;
- int end;
+ AddToPlaylistData *data;
+ /* Block all signals from the playlist until we're finished. They're unblocked in the callback, add_to_playlist_and_play_cb.
+ * There are no concurrency issues here, since blocking the signals multiple times should require them to be unblocked the
+ * same number of times before they fire again. */
totem_signal_block_by_data (totem->playlist, totem);
- playlist_changed = totem_playlist_add_mrl_with_cursor (totem->playlist, uri, display_name);
- if (add_to_recent != FALSE)
- gtk_recent_manager_add_item (totem->recent_manager, uri);
- end = totem_playlist_get_last (totem->playlist);
-
- totem_signal_unblock_by_data (totem->playlist, totem);
-
- if (playlist_changed && end != -1) {
- char *mrl, *subtitle;
+ data = g_slice_new (AddToPlaylistData);
+ data->totem = g_object_ref (totem);
+ data->uri = g_strdup (uri);
+ data->display_name = g_strdup (display_name);
+ data->add_to_recent = add_to_recent;
- subtitle = NULL;
- totem_playlist_set_current (totem->playlist, end);
- mrl = totem_playlist_get_current_mrl (totem->playlist, &subtitle);
- totem_action_set_mrl_and_play (totem, mrl, subtitle);
- g_free (mrl);
- g_free (subtitle);
- }
+ totem_playlist_add_mrl (totem->playlist, uri, display_name, TRUE,
+ NULL, (GAsyncReadyCallback) add_to_playlist_and_play_cb, data);
}
/**
@@ -2217,7 +2249,7 @@ totem_action_drop_files (Totem *totem, GtkSelectionData *data,
}
}
- totem_playlist_add_mrl (totem->playlist, filename, title);
+ totem_playlist_add_mrl (totem->playlist, filename, title, FALSE, NULL, NULL, NULL);
}
bail:
@@ -2773,9 +2805,10 @@ totem_action_open_files_list (Totem *totem, GSList *list)
totem_action_load_media_device (totem, data);
changed = TRUE;
} else if (g_str_has_prefix (filename, "dvb:/") != FALSE) {
- totem_playlist_add_mrl (totem->playlist, data, NULL);
+ totem_playlist_add_mrl (totem->playlist, data, NULL, FALSE, NULL, NULL, NULL);
changed = TRUE;
- } else if (totem_playlist_add_mrl (totem->playlist, filename, NULL) != FALSE) {
+ } else {
+ totem_playlist_add_mrl (totem->playlist, filename, NULL, FALSE, NULL, NULL, NULL);
changed = TRUE;
}
}
@@ -3055,7 +3088,7 @@ totem_action_remote (Totem *totem, TotemRemoteCommand cmd, const char *url)
break;
case TOTEM_REMOTE_COMMAND_ENQUEUE:
g_assert (url != NULL);
- totem_playlist_add_mrl_with_cursor (totem->playlist, url, NULL);
+ totem_playlist_add_mrl (totem->playlist, url, NULL, TRUE, NULL, NULL, NULL);
break;
case TOTEM_REMOTE_COMMAND_REPLACE:
totem_playlist_clear (totem->playlist);
@@ -3072,7 +3105,7 @@ totem_action_remote (Totem *totem, TotemRemoteCommand cmd, const char *url)
/* FIXME b0rked */
totem_action_play_media (totem, MEDIA_TYPE_VCD, NULL);
} else {
- totem_playlist_add_mrl_with_cursor (totem->playlist, url, NULL);
+ totem_playlist_add_mrl (totem->playlist, url, NULL, TRUE, NULL, NULL, NULL);
}
break;
case TOTEM_REMOTE_COMMAND_SHOW:
diff --git a/src/totem-playlist.c b/src/totem-playlist.c
index 78479b701..06034b74d 100644
--- a/src/totem-playlist.c
+++ b/src/totem-playlist.c
@@ -95,6 +95,9 @@ struct TotemPlaylistPrivate
GtkTreePath *tree_path;
GtkTreeViewDropPosition drop_pos;
+ /* Cursor ref: 0 if the cursor is unbusy; positive numbers indicate the number of nested calls to set_waiting_cursor() */
+ guint cursor_ref;
+
/* This is a scratch list for when we're removing files */
GList *list;
guint current_to_be_removed : 1;
@@ -216,21 +219,17 @@ totem_playlist_get_toplevel (TotemPlaylist *playlist)
}
static void
-totem_playlist_set_waiting_cursor (TotemPlaylist *playlist)
+set_waiting_cursor (TotemPlaylist *playlist)
{
- GtkWidget *parent;
-
- parent = GTK_WIDGET (totem_playlist_get_toplevel (playlist));
- totem_gdk_window_set_waiting_cursor (gtk_widget_get_window (parent));
+ totem_gdk_window_set_waiting_cursor (gtk_widget_get_window (GTK_WIDGET (totem_playlist_get_toplevel (playlist))));
+ playlist->priv->cursor_ref++;
}
static void
-totem_playlist_unset_waiting_cursor (TotemPlaylist *playlist)
+unset_waiting_cursor (TotemPlaylist *playlist)
{
- GtkWidget *parent;
-
- parent = GTK_WIDGET (totem_playlist_get_toplevel (playlist));
- gdk_window_set_cursor (gtk_widget_get_window (parent), NULL);
+ if (--playlist->priv->cursor_ref < 1)
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (totem_playlist_get_toplevel (playlist))), NULL);
}
static void
@@ -486,6 +485,15 @@ gtk_tree_selection_has_selected (GtkTreeSelection *selection)
}
static void
+drop_finished_cb (TotemPlaylist *playlist, GAsyncResult *result, gpointer user_data)
+{
+ /* Emit the "changed" signal once the last dropped MRL has been added to the playlist */
+ g_signal_emit (G_OBJECT (playlist),
+ totem_playlist_table_signals[CHANGED], 0,
+ NULL);
+}
+
+static void
drop_cb (GtkWidget *widget,
GdkDragContext *context,
gint x,
@@ -525,8 +533,6 @@ drop_cb (GtkWidget *widget,
return;
}
- totem_playlist_set_waiting_cursor (playlist);
-
playlist->priv->tree_path = gtk_tree_path_new ();
gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (playlist->priv->treeview),
x, y,
@@ -559,7 +565,12 @@ drop_cb (GtkWidget *widget,
}
}
- totem_playlist_add_mrl (playlist, filename, title);
+ /* Add the MRL to the playlist asynchronously. If it's the last MRL, emit the "changed"
+ * signal once we're done adding it */
+ if (p->next == NULL)
+ totem_playlist_add_mrl (playlist, filename, title, TRUE, NULL, (GAsyncReadyCallback) drop_finished_cb, NULL);
+ else
+ totem_playlist_add_mrl (playlist, filename, title, TRUE, NULL, NULL, NULL);
g_free (filename);
}
@@ -569,12 +580,6 @@ drop_cb (GtkWidget *widget,
gtk_drag_finish (context, TRUE, FALSE, _time);
gtk_tree_path_free (playlist->priv->tree_path);
playlist->priv->tree_path = NULL;
-
- totem_playlist_unset_waiting_cursor (playlist);
-
- g_signal_emit (G_OBJECT (playlist),
- totem_playlist_table_signals[CHANGED], 0,
- NULL);
}
void
@@ -851,22 +856,18 @@ totem_playlist_add_files (GtkWidget *widget, TotemPlaylist *playlist)
{
GSList *filenames, *l;
- filenames = totem_add_files (totem_playlist_get_toplevel (playlist),
- NULL);
+ filenames = totem_add_files (totem_playlist_get_toplevel (playlist), NULL);
if (filenames == NULL)
return;
- totem_playlist_set_waiting_cursor (playlist);
-
for (l = filenames; l != NULL; l = l->next) {
char *mrl;
mrl = l->data;
- totem_playlist_add_mrl (playlist, mrl, NULL);
+ totem_playlist_add_mrl (playlist, mrl, NULL, TRUE, NULL, NULL, NULL);
g_free (mrl);
}
- totem_playlist_unset_waiting_cursor (playlist);
g_slist_free (filenames);
}
@@ -1822,38 +1823,36 @@ totem_playlist_add_one_mrl (TotemPlaylist *playlist,
return TRUE;
}
-gboolean
-totem_playlist_add_mrl_with_cursor (TotemPlaylist *playlist, const char *mrl,
- const char *display_name)
-{
- gboolean retval;
-
- totem_playlist_set_waiting_cursor (playlist);
- retval = totem_playlist_add_mrl (playlist, mrl, display_name);
- totem_playlist_unset_waiting_cursor (playlist);
+typedef struct {
+ GAsyncReadyCallback callback;
+ gpointer user_data;
+ gboolean cursor;
+ TotemPlaylist *playlist;
+ gchar *mrl;
+ gchar *display_name;
+} AddMrlData;
- return retval;
+static void
+add_mrl_data_free (AddMrlData *data)
+{
+ g_object_unref (data->playlist);
+ g_free (data->mrl);
+ g_free (data->display_name);
+ g_slice_free (AddMrlData, data);
}
-gboolean
-totem_playlist_add_mrl (TotemPlaylist *playlist, const char *mrl,
- const char *display_name)
+static gboolean
+handle_parse_result (TotemPlParserResult res, TotemPlaylist *playlist, const gchar *mrl, const gchar *display_name)
{
- TotemPlParserResult res;
-
- g_return_val_if_fail (mrl != NULL, FALSE);
-
- res = totem_pl_parser_parse (playlist->priv->parser, mrl, FALSE);
-
if (res == TOTEM_PL_PARSER_RESULT_UNHANDLED)
return totem_playlist_add_one_mrl (playlist, mrl, display_name);
- if (res == TOTEM_PL_PARSER_RESULT_ERROR)
- {
+ if (res == TOTEM_PL_PARSER_RESULT_ERROR) {
char *msg;
- msg = g_strdup_printf (_("The playlist '%s' could not be parsed, it might be damaged."), display_name ? display_name : mrl);
+ msg = g_strdup_printf (_("The playlist '%s' could not be parsed. It might be damaged."), display_name ? display_name : mrl);
totem_playlist_error (_("Playlist error"), msg, playlist);
g_free (msg);
+
return FALSE;
}
if (res == TOTEM_PL_PARSER_RESULT_IGNORED)
@@ -1862,6 +1861,79 @@ totem_playlist_add_mrl (TotemPlaylist *playlist, const char *mrl,
return TRUE;
}
+static void
+add_mrl_cb (TotemPlParser *parser, GAsyncResult *result, AddMrlData *data)
+{
+ TotemPlParserResult res;
+ GSimpleAsyncResult *async_result;
+ GError *error = NULL;
+
+ /* Finish parsing the playlist */
+ res = totem_pl_parser_parse_finish (parser, result, &error);
+
+ /* Remove the cursor, if one was set */
+ if (data->cursor)
+ unset_waiting_cursor (data->playlist);
+
+ /* Create an async result which will return the result to the code which called totem_playlist_add_mrl() */
+ if (error != NULL)
+ async_result = g_simple_async_result_new_from_error (G_OBJECT (data->playlist), data->callback, data->user_data, error);
+ else
+ async_result = g_simple_async_result_new (G_OBJECT (data->playlist), data->callback, data->user_data, totem_playlist_add_mrl);
+
+ /* Handle the various return cases from the playlist parser */
+ g_simple_async_result_set_op_res_gboolean (async_result, handle_parse_result (res, data->playlist, data->mrl, data->display_name));
+
+ /* Free the closure's data, now that we're finished with it */
+ add_mrl_data_free (data);
+
+ /* Synchronously call the calling code's callback function (i.e. what was passed to totem_playlist_add_mrl()'s @callback parameter)
+ * in the main thread to return the result */
+ g_simple_async_result_complete (async_result);
+}
+
+void
+totem_playlist_add_mrl (TotemPlaylist *playlist, const char *mrl, const char *display_name, gboolean cursor,
+ GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
+{
+ AddMrlData *data;
+
+ g_return_if_fail (mrl != NULL);
+
+ /* Display a waiting cursor if required */
+ if (cursor)
+ set_waiting_cursor (playlist);
+
+ /* Build the data struct to pass to the callback function */
+ data = g_slice_new (AddMrlData);
+ data->callback = callback;
+ data->user_data = user_data;
+ data->cursor = cursor;
+ data->playlist = g_object_ref (playlist);
+ data->mrl = g_strdup (mrl);
+ data->display_name = g_strdup (display_name);
+
+ /* Start parsing the playlist. Once this is complete, add_mrl_cb() is called, which will interpret the results and call @callback to
+ * finish the process. */
+ totem_pl_parser_parse_async (playlist->priv->parser, mrl, FALSE, cancellable, (GAsyncReadyCallback) add_mrl_cb, data);
+}
+
+gboolean
+totem_playlist_add_mrl_finish (TotemPlaylist *playlist, GAsyncResult *result)
+{
+ g_assert (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result)) == totem_playlist_add_mrl);
+
+ return g_simple_async_result_get_op_res_gboolean (G_SIMPLE_ASYNC_RESULT (result));
+}
+
+gboolean
+totem_playlist_add_mrl_sync (TotemPlaylist *playlist, const char *mrl, const char *display_name)
+{
+ g_return_val_if_fail (mrl != NULL, FALSE);
+
+ return handle_parse_result (totem_pl_parser_parse (playlist->priv->parser, mrl, FALSE), playlist, mrl, display_name);
+}
+
static gboolean
totem_playlist_clear_cb (GtkTreeModel *model,
GtkTreePath *path,
diff --git a/src/totem-playlist.h b/src/totem-playlist.h
index 0ec018bc4..d7e5a65a8 100644
--- a/src/totem-playlist.h
+++ b/src/totem-playlist.h
@@ -87,12 +87,18 @@ GtkWidget *totem_playlist_new (void);
* @display_name is if you have a preferred display string for the mrl,
* NULL otherwise
*/
-gboolean totem_playlist_add_mrl (TotemPlaylist *playlist,
- const char *mrl,
- const char *display_name);
-gboolean totem_playlist_add_mrl_with_cursor (TotemPlaylist *playlist,
- const char *mrl,
- const char *display_name);
+void totem_playlist_add_mrl (TotemPlaylist *playlist,
+ const char *mrl,
+ const char *display_name,
+ gboolean cursor,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean totem_playlist_add_mrl_finish (TotemPlaylist *playlist,
+ GAsyncResult *result);
+gboolean totem_playlist_add_mrl_sync (TotemPlaylist *playlist,
+ const char *mrl,
+ const char *display_name);
void totem_playlist_save_current_playlist (TotemPlaylist *playlist,
const char *output);
diff --git a/src/totem-session.c b/src/totem-session.c
index 19622464c..ad272f01e 100644
--- a/src/totem-session.c
+++ b/src/totem-session.c
@@ -147,7 +147,9 @@ totem_session_restore (Totem *totem, char **filenames)
totem_signal_block_by_data (totem->playlist, totem);
- if (totem_playlist_add_mrl_with_cursor (totem->playlist, uri, NULL) == FALSE) {
+ /* Possibly the only place in Totem where it makes sense to add an MRL to the playlist synchronously, since we haven't yet entered
+ * the GTK+ main loop, and thus can't freeze the application. */
+ if (totem_playlist_add_mrl_sync (totem->playlist, uri, NULL) == FALSE) {
totem_signal_unblock_by_data (totem->playlist, totem);
totem_action_set_mrl (totem, NULL, NULL);
g_free (uri);
diff --git a/src/totem-video-list.c b/src/totem-video-list.c
index 2bf6c1f8f..b5c61e2c9 100644
--- a/src/totem-video-list.c
+++ b/src/totem-video-list.c
@@ -482,7 +482,7 @@ add_to_playlist_action_callback (GtkAction *action, TotemVideoList *self)
continue;
}
- totem_playlist_add_mrl_with_cursor (playlist, mrl, display_name);
+ totem_playlist_add_mrl (playlist, mrl, display_name, TRUE, NULL, NULL, NULL);
g_free (mrl);
g_free (display_name);