void (*fill)(struct choosenode *));
static void fill_root_node(struct choosenode *cn);
static void fill_directory_node(struct choosenode *cn);
-static void got_files(void *v, int nvec, char **vec);
+static void got_files(void *v, const char *error, int nvec, char **vec);
static void got_resolved_file(void *v, const char *error, const char *track);
-static void got_dirs(void *v, int nvec, char **vec);
+static void got_dirs(void *v, const char *error, int nvec, char **vec);
static void expand_node(struct choosenode *cn, int contingent);
static void contract_node(struct choosenode *cn);
/** @brief Fill the root */
static void fill_root_node(struct choosenode *cn) {
- struct callbackdata *cbd;
-
D(("fill_root_node"));
clear_children(cn);
/* More de-duping possible here */
if(cn->flags & CN_GETTING_ANY)
return;
gtk_label_set_text(GTK_LABEL(report_label), "getting files");
- cbd = xmalloc(sizeof *cbd);
- cbd->u.choosenode = cn;
- disorder_eclient_dirs(client, got_dirs, "", 0, cbd);
- cbd = xmalloc(sizeof *cbd);
- cbd->u.choosenode = cn;
- disorder_eclient_files(client, got_files, "", 0, cbd);
+ disorder_eclient_dirs(client, got_dirs, "", 0, cn);
+ disorder_eclient_files(client, got_files, "", 0, cn);
cn->flags |= CN_GETTING_FILES|CN_GETTING_DIRS;
gets_in_flight += 2;
}
}
/** @brief Called with a list of files just below some node */
-static void got_files(void *v, int nvec, char **vec) {
- struct callbackdata *cbd = v;
- struct choosenode *cn = cbd->u.choosenode;
+static void got_files(void *v, const char *error, int nvec, char **vec) {
+ struct choosenode *cn = v;
int n;
+ if(error) {
+ popup_protocol_error(0, error);
+ return;
+ }
+
D(("got_files %d files for %s %s", nvec, cn->path, cnflags(cn)));
/* Complicated by the need to resolve aliases. */
cn->flags &= ~CN_GETTING_FILES;
}
/** @brief Called with a list of directories just below some node */
-static void got_dirs(void *v, int nvec, char **vec) {
- struct callbackdata *cbd = v;
- struct choosenode *cn = cbd->u.choosenode;
+static void got_dirs(void *v, const char *error, int nvec, char **vec) {
+ struct choosenode *cn = v;
int n;
+
+ if(error) {
+ popup_protocol_error(0, error);
+ return;
+ }
D(("got_dirs %d dirs for %s %s", nvec, cn->path, cnflags(cn)));
/* TODO this depends on local configuration for trackname_transform().
* and also from initiate_seatch with an always empty list to indicate that
* we're not searching for anything in particular. */
static void search_completed(void attribute((unused)) *v,
+ const char *error,
int nvec, char **vec) {
int n;
char *s;
+ if(error) {
+ popup_protocol_error(0, error);
+ return;
+ }
+
search_in_flight = 0;
/* Contract any choosenodes that were only expanded to show search
* results */
/* The search terms are bad! We treat this as if there were no search
* terms at all. Some kind of feedback would be handy. */
fprintf(stderr, "bad terms [%s]\n", terms); /* TODO */
- search_completed(0, 0, 0);
+ search_completed(0, 0, 0, 0);
} else {
search_in_flight = 1;
}
} else {
/* No search terms - we want to see all tracks */
- search_completed(0, 0, 0);
+ search_completed(0, 0, 0, 0);
}
}
/** @brief Called when a rtp-address command succeeds */
static void got_rtp_address(void attribute((unused)) *v,
+ const char *error,
int attribute((unused)) nvec,
char attribute((unused)) **vec) {
++suppress_actions;
rtp_address_in_flight = 0;
- rtp_supported = 1;
- rtp_is_running = rtp_running();
- control_monitor(0);
- --suppress_actions;
-}
-
-/** @brief Called when a rtp-address command fails */
-static void no_rtp_address(struct callbackdata attribute((unused)) *cbd,
- int attribute((unused)) code,
- const char attribute((unused)) *msg) {
- ++suppress_actions;
- rtp_address_in_flight = 0;
- rtp_supported = 0;
- rtp_is_running = 0;
+ if(error) {
+ /* An error just means that we're not using network play */
+ rtp_supported = 0;
+ rtp_is_running = 0;
+ } else {
+ rtp_supported = 1;
+ rtp_is_running = rtp_running();
+ control_monitor(0);
+ }
control_monitor(0);
--suppress_actions;
}
/** @brief Called to check whether RTP play is available */
static void check_rtp_address(void) {
- if(!rtp_address_in_flight) {
- struct callbackdata *const cbd = xmalloc(sizeof *cbd);
- cbd->onerror = no_rtp_address;
- disorder_eclient_rtp_address(client, got_rtp_address, cbd);
- }
+ if(!rtp_address_in_flight)
+ disorder_eclient_rtp_address(client, got_rtp_address, NULL);
}
/* main -------------------------------------------------------------------- */
* disobedience/queue.c requires @ref queue_entry structures with a valid and
* unique @c id field. This function fakes it.
*/
-static void new_completed(void attribute((unused)) *v, int nvec, char **vec) {
- struct queue_entry *q, *qh, *qlast = 0, **qq = &qh;
- int n;
-
- for(n = 0; n < nvec; ++n) {
- q = xmalloc(sizeof *q);
- q->prev = qlast;
- q->track = vec[n];
- q->id = vec[n];
- *qq = q;
- qq = &q->next;
- qlast = q;
+static void new_completed(void *v,
+ const char *error,
+ int nvec, char **vec) {
+ if(error)
+ popup_protocol_error(0, error);
+ else {
+ struct queuelist *ql = v;
+ /* Convert the vector result to a queue linked list */
+ struct queue_entry *q, *qh, *qlast = 0, **qq = &qh;
+ int n;
+
+ for(n = 0; n < nvec; ++n) {
+ q = xmalloc(sizeof *q);
+ q->prev = qlast;
+ q->track = vec[n];
+ q->id = vec[n];
+ *qq = q;
+ qq = &q->next;
+ qlast = q;
+ }
+ *qq = 0;
+ queuelike_completed(ql, 0, qh);
}
- *qq = 0;
- queuelike_completed(&ql_added, 0, qh);
}
/** @brief Update the newly-added list */
void added_update(void) {
- struct callbackdata *cbd;
D(("added_update"));
- cbd = xmalloc(sizeof *cbd);
- cbd->onerror = 0;
- cbd->u.ql = &ql_added;
gtk_label_set_text(GTK_LABEL(report_label),
"updating newly added track list");
- disorder_eclient_new_tracks(client, new_completed, 0/*all*/, cbd);
+ disorder_eclient_new_tracks(client, new_completed, 0/*all*/, &ql_added);
}
/* Main menu plumbing ------------------------------------------------------ */
*
* If users_deferred_select is set then that user is selected.
*/
-static void users_got_list(void attribute((unused)) *v, int nvec, char **vec) {
+static void users_got_list(void attribute((unused)) *v,
+ const char *error,
+ int nvec, char **vec) {
int n;
GtkTreeIter iter;
+ if(error) {
+ popup_protocol_error(0, error);
+ return;
+ }
/* Present users in alphabetical order */
qsort(vec, nvec, sizeof (char *), usercmp);
/* Set the list contents */
for(n = 0; n < c->vec.nvec; ++n) {
q = xmalloc(sizeof *q);
D(("queue_unmarshall %s", c->vec.vec[n]));
- if(!queue_unmarshall(q, c->vec.vec[n], eclient_queue_error, op)) {
+ if(!queue_unmarshall(q, c->vec.vec[n], NULL, op)) {
q->prev = qlast;
*qtail = q;
qtail = &q->next;
switch(c->rc % 10) {
case 2:
if(queue_unmarshall(q = xmalloc(sizeof *q), c->line + 4,
- eclient_queue_error, c))
+ NULL, c))
completed(op->v, "cannot parse result", 0);
else
completed(op->v, 0, q);
/* for commands that expect a list of some sort */
static void list_response_opcallback(disorder_eclient *c,
struct operation *op) {
+ disorder_eclient_list_response *const completed =
+ (disorder_eclient_list_response *)op->completed;
+
D(("list_response_callback"));
- if(c->rc / 100 == 2) {
- if(op->completed)
- ((disorder_eclient_list_response *)op->completed)(op->v,
- c->vec.nvec,
- c->vec.vec);
- } else
- /* TODO don't use protocol_error here */
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ if(c->rc / 100 == 2)
+ completed(op->v, NULL, c->vec.nvec, c->vec.vec);
+ else
+ completed(op->v, errorstring(c), 0, 0);
}
/* for volume */
static void rtp_response_opcallback(disorder_eclient *c,
struct operation *op) {
+ disorder_eclient_list_response *const completed =
+ (disorder_eclient_list_response *)op->completed;
D(("rtp_response_opcallback"));
if(c->rc / 100 == 2) {
- if(op->completed) {
- int nvec;
- char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0);
+ int nvec;
+ char **vec = split(c->line + 4, &nvec, SPLIT_QUOTES, 0, 0);
- ((disorder_eclient_list_response *)op->completed)(op->v, nvec, vec);
- }
+ if(vec)
+ completed(op->v, NULL, nvec, vec);
+ else
+ completed(op->v, "error parsing response", 0, 0);
} else
- /* TODO don't use protocol_error here */
- protocol_error(c, op, c->rc, "%s: %s", c->ident, c->line);
+ completed(op->v, errorstring(c), 0, 0);
}
/** @brief Determine the RTP target address
* @param error Error string or NULL on success
* @param q Head of queue data list
*
- * @p error will be NULL on success. In this case @p q be the (head of the)
- * result.
+ * @p error will be NULL on success. In this case @p q will be the (head of
+ * the) result.
*
* @p error will be non-NULL on failure. In this case @p q may be NULL but
* MIGHT also be some subset of the queue. For consistent behavior it should
const char *error,
struct queue_entry *q);
-typedef void disorder_eclient_list_response(void *v, int nvec, char **vec);
-/* completion callback for file listing etc */
+/** @brief List request completion callback
+ * @param v User data
+ * @param error Error string or NULL on success
+ * @param nvec Number of elements in response list
+ * @param vec Pointer to response list
+ *
+ * @p error will be NULL on success. In this case @p nvec and @p vec will give
+ * the result.
+ *
+ * @p error will be non-NULL on failure. In this case @p nvec and @p vec will
+ * be 0 and NULL.
+ */
+typedef void disorder_eclient_list_response(void *v,
+ const char *error,
+ int nvec, char **vec);
disorder_eclient *disorder_eclient_new(const disorder_eclient_callbacks *cb,
void *u);