diff --git a/src/mpdclient.c b/src/mpdclient.c
index 8177efa72309637299a3524851d85dd9e9741939..ffa1a5d84408e86757b82d2dbde1617dbf07c583 100644 (file)
--- a/src/mpdclient.c
+++ b/src/mpdclient.c
#include "filelist.h"
#include "config.h"
#include "gidle.h"
#include "filelist.h"
#include "config.h"
#include "gidle.h"
+#include "charset.h"
+
+#ifdef ENABLE_ASYNC_CONNECT
+#include "aconnect.h"
+#endif
#include <mpd/client.h>
#include <mpd/client.h>
+#include <assert.h>
+
+static gboolean
+mpdclient_enter_idle_callback(gpointer user_data)
+{
+ struct mpdclient *c = user_data;
+ assert(c->enter_idle_source_id != 0);
+ assert(c->source != NULL);
+ assert(!c->idle);
+
+ c->enter_idle_source_id = 0;
+ c->idle = mpd_glib_enter(c->source);
+ return false;
+}
+
+static void
+mpdclient_schedule_enter_idle(struct mpdclient *c)
+{
+ assert(c != NULL);
+ assert(c->source != NULL);
+
+ if (c->enter_idle_source_id == 0)
+ /* automatically re-enter MPD "idle" mode */
+ c->enter_idle_source_id =
+ g_idle_add(mpdclient_enter_idle_callback, c);
+}
+
+static void
+mpdclient_cancel_enter_idle(struct mpdclient *c)
+{
+ if (c->enter_idle_source_id != 0) {
+ g_source_remove(c->enter_idle_source_id);
+ c->enter_idle_source_id = 0;
+ }
+}
+
+static void
+mpdclient_invoke_error_callback(enum mpd_error error,
+ const char *message)
+{
+ char *allocated;
+ if (error == MPD_ERROR_SERVER)
+ /* server errors are UTF-8, the others are locale */
+ message = allocated = utf8_to_locale(message);
+ else
+ allocated = NULL;
+
+ mpdclient_error_callback(message);
+ g_free(allocated);
+}
+
+static void
+mpdclient_invoke_error_callback1(struct mpdclient *c)
+{
+ assert(c != NULL);
+ assert(c->connection != NULL);
+
+ struct mpd_connection *connection = c->connection;
+
+ enum mpd_error error = mpd_connection_get_error(connection);
+ assert(error != MPD_ERROR_SUCCESS);
+
+ mpdclient_invoke_error_callback(error,
+ mpd_connection_get_error_message(connection));
+}
+
+static void
+mpdclient_gidle_callback(enum mpd_error error,
+ gcc_unused enum mpd_server_error server_error,
+ const char *message, enum mpd_idle events,
+ void *ctx)
+{
+ struct mpdclient *c = ctx;
+
+ c->idle = false;
+
+ assert(mpdclient_is_connected(c));
+
+ if (error != MPD_ERROR_SUCCESS) {
+ mpdclient_invoke_error_callback(error, message);
+ mpdclient_disconnect(c);
+ mpdclient_lost_callback();
+ return;
+ }
+
+ c->events |= events;
+ mpdclient_update(c);
+
+ mpdclient_idle_callback(c->events);
+
+ c->events = 0;
+
+ if (c->source != NULL)
+ mpdclient_schedule_enter_idle(c);
+}
+
/****************************************************************************/
/*** mpdclient functions ****************************************************/
/****************************************************************************/
/****************************************************************************/
/*** mpdclient functions ****************************************************/
/****************************************************************************/
mpdclient_auth_callback(c))
return true;
mpdclient_auth_callback(c))
return true;
- mpdclient_error_callback(mpd_connection_get_error_message(c->connection));
+ mpdclient_invoke_error_callback(error,
+ mpd_connection_get_error_message(c->connection));
- if (!mpd_connection_clear_error(c->connection))
+ if (!mpd_connection_clear_error(c->connection)) {
mpdclient_disconnect(c);
mpdclient_disconnect(c);
+ mpdclient_lost_callback();
+ }
return false;
}
return false;
}
+#ifdef ENABLE_ASYNC_CONNECT
+#ifndef WIN32
+
+static bool
+is_local_socket(const char *host)
+{
+ return *host == '/' || *host == '@';
+}
+
+static bool
+settings_is_local_socket(const struct mpd_settings *settings)
+{
+ const char *host = mpd_settings_get_host(settings);
+ return host != NULL && is_local_socket(host);
+}
+
+#endif
+#endif
+
struct mpdclient *
struct mpdclient *
-mpdclient_new(void)
+mpdclient_new(const gchar *host, unsigned port,
+ unsigned timeout_ms, const gchar *password)
{
struct mpdclient *c = g_new0(struct mpdclient, 1);
{
struct mpdclient *c = g_new0(struct mpdclient, 1);
+
+#ifdef ENABLE_ASYNC_CONNECT
+ c->settings = mpd_settings_new(host, port, timeout_ms,
+ NULL, NULL);
+ if (c->settings == NULL)
+ g_error("Out of memory");
+
+#ifndef WIN32
+ c->settings2 = host == NULL && port == 0 &&
+ settings_is_local_socket(c->settings)
+ ? mpd_settings_new(host, 6600, timeout_ms, NULL, NULL)
+ : NULL;
+#endif
+
+#else
+ c->host = host;
+ c->port = port;
+#endif
+
+ c->timeout_ms = timeout_ms;
+ c->password = password;
+
playlist_init(&c->playlist);
c->volume = -1;
c->events = 0;
playlist_init(&c->playlist);
c->volume = -1;
c->events = 0;
+ c->playing = false;
return c;
}
return c;
}
mpdclient_playlist_free(&c->playlist);
mpdclient_playlist_free(&c->playlist);
+#ifdef ENABLE_ASYNC_CONNECT
+ mpd_settings_free(c->settings);
+
+#ifndef WIN32
+ if (c->settings2 != NULL)
+ mpd_settings_free(c->settings2);
+#endif
+#endif
+
g_free(c);
}
g_free(c);
}
+static char *
+settings_name(const struct mpd_settings *settings)
+{
+ assert(settings != NULL);
+
+ const char *host = mpd_settings_get_host(settings);
+ if (host == NULL)
+ host = "unknown";
+
+ if (host[0] == '/')
+ return g_strdup(host);
+
+ unsigned port = mpd_settings_get_port(settings);
+ if (port == 0 || port == 6600)
+ return g_strdup(host);
+
+ return g_strdup_printf("%s:%u", host, port);
+}
+
+char *
+mpdclient_settings_name(const struct mpdclient *c)
+{
+ assert(c != NULL);
+
+#ifdef ENABLE_ASYNC_CONNECT
+ return settings_name(c->settings);
+#else
+ struct mpd_settings *settings =
+ mpd_settings_new(c->host, c->port, 0, NULL, NULL);
+ if (settings == NULL)
+ return g_strdup("unknown");
+
+ char *name = settings_name(settings);
+ mpd_settings_free(settings);
+ return name;
+#endif
+}
+
+static void
+mpdclient_status_free(struct mpdclient *c)
+{
+ if (c->status == NULL)
+ return;
+
+ mpd_status_free(c->status);
+ c->status = NULL;
+
+ c->volume = -1;
+ c->playing = false;
+}
+
void
mpdclient_disconnect(struct mpdclient *c)
{
void
mpdclient_disconnect(struct mpdclient *c)
{
+#ifdef ENABLE_ASYNC_CONNECT
+ if (c->async_connect != NULL) {
+ aconnect_cancel(c->async_connect);
+ c->async_connect = NULL;
+ }
+#endif
+
+ mpdclient_cancel_enter_idle(c);
+
if (c->source != NULL) {
mpd_glib_free(c->source);
c->source = NULL;
if (c->source != NULL) {
mpd_glib_free(c->source);
c->source = NULL;
}
c->connection = NULL;
}
c->connection = NULL;
- if (c->status)
- mpd_status_free(c->status);
- c->status = NULL;
+ mpdclient_status_free(c);
playlist_clear(&c->playlist);
playlist_clear(&c->playlist);
c->events |= MPD_IDLE_ALL;
}
c->events |= MPD_IDLE_ALL;
}
-bool
-mpdclient_connect(struct mpdclient *c,
- const gchar *host,
- gint port,
- unsigned timeout_ms,
- const gchar *password)
+static bool
+mpdclient_connected(struct mpdclient *c,
+ struct mpd_connection *connection)
{
{
- /* close any open connection */
- if (c->connection)
- mpdclient_disconnect(c);
+ c->connection = connection;
- /* connect to MPD */
- c->connection = mpd_connection_new(host, port, timeout_ms);
- if (c->connection == NULL)
- g_error("Out of memory");
-
- if (mpd_connection_get_error(c->connection) != MPD_ERROR_SUCCESS) {
- mpdclient_handle_error(c);
+ if (mpd_connection_get_error(connection) != MPD_ERROR_SUCCESS) {
+ mpdclient_invoke_error_callback1(c);
mpdclient_disconnect(c);
mpdclient_disconnect(c);
+ mpdclient_failed_callback();
return false;
}
return false;
}
+#ifdef ENABLE_ASYNC_CONNECT
+ if (c->timeout_ms > 0)
+ mpd_connection_set_timeout(connection, c->timeout_ms);
+#endif
+
/* send password */
/* send password */
- if (password != NULL && !mpd_run_password(c->connection, password)) {
- mpdclient_handle_error(c);
+ if (c->password != NULL &&
+ !mpd_run_password(connection, c->password)) {
+ mpdclient_invoke_error_callback1(c);
mpdclient_disconnect(c);
mpdclient_disconnect(c);
+ mpdclient_failed_callback();
return false;
}
return false;
}
- c->source = mpd_glib_new(c->connection,
- mpdclient_idle_callback, c);
+ c->source = mpd_glib_new(connection,
+ mpdclient_gidle_callback, c);
+ mpdclient_schedule_enter_idle(c);
++c->connection_id;
++c->connection_id;
+ mpdclient_connected_callback();
return true;
}
return true;
}
+#ifdef ENABLE_ASYNC_CONNECT
+
+static void
+mpdclient_aconnect_start(struct mpdclient *c,
+ const struct mpd_settings *settings);
+
+static void
+mpdclient_connect_success(struct mpd_connection *connection, void *ctx)
+{
+ struct mpdclient *c = ctx;
+ assert(c->async_connect != NULL);
+ c->async_connect = NULL;
+
+ mpdclient_connected(c, connection);
+}
+
+static void
+mpdclient_connect_error(const char *message, void *ctx)
+{
+ struct mpdclient *c = ctx;
+ assert(c->async_connect != NULL);
+ c->async_connect = NULL;
+
+#ifndef WIN32
+ if (!c->connecting2 && c->settings2 != NULL) {
+ c->connecting2 = true;
+ mpdclient_aconnect_start(c, c->settings2);
+ return;
+ }
+#endif
+
+ mpdclient_error_callback(message);
+ mpdclient_failed_callback();
+}
+
+static const struct aconnect_handler mpdclient_connect_handler = {
+ .success = mpdclient_connect_success,
+ .error = mpdclient_connect_error,
+};
+
+static void
+mpdclient_aconnect_start(struct mpdclient *c,
+ const struct mpd_settings *settings)
+{
+ aconnect_start(&c->async_connect,
+ mpd_settings_get_host(settings),
+ mpd_settings_get_port(settings),
+ &mpdclient_connect_handler, c);
+}
+
+#endif
+
+void
+mpdclient_connect(struct mpdclient *c)
+{
+ /* close any open connection */
+ mpdclient_disconnect(c);
+
+#ifdef ENABLE_ASYNC_CONNECT
+#ifndef WIN32
+ c->connecting2 = false;
+#endif
+ mpdclient_aconnect_start(c, c->settings);
+#else
+ /* connect to MPD */
+ struct mpd_connection *connection =
+ mpd_connection_new(c->host, c->port, c->timeout_ms);
+ if (connection == NULL)
+ g_error("Out of memory");
+
+ mpdclient_connected(c, connection);
+#endif
+}
+
bool
mpdclient_update(struct mpdclient *c)
{
struct mpd_connection *connection = mpdclient_get_connection(c);
bool
mpdclient_update(struct mpdclient *c)
{
struct mpd_connection *connection = mpdclient_get_connection(c);
- c->volume = -1;
-
if (connection == NULL)
return false;
/* free the old status */
if (connection == NULL)
return false;
/* free the old status */
- if (c->status)
- mpd_status_free(c->status);
+ mpdclient_status_free(c);
/* retrieve new status */
c->status = mpd_run_status(connection);
if (c->status == NULL)
return mpdclient_handle_error(c);
/* retrieve new status */
c->status = mpd_run_status(connection);
if (c->status == NULL)
return mpdclient_handle_error(c);
- c->update_id = mpd_status_get_update_id(c->status);
-
c->volume = mpd_status_get_volume(c->status);
c->volume = mpd_status_get_volume(c->status);
+ c->playing = mpd_status_get_state(c->status) == MPD_STATE_PLAY;
/* check if the playlist needs an update */
if (c->playlist.version != mpd_status_get_queue_version(c->status)) {
/* check if the playlist needs an update */
if (c->playlist.version != mpd_status_get_queue_version(c->status)) {
if (c->source != NULL && c->idle) {
c->idle = false;
mpd_glib_leave(c->source);
if (c->source != NULL && c->idle) {
c->idle = false;
mpd_glib_leave(c->source);
+
+ mpdclient_schedule_enter_idle(c);
}
return c->connection;
}
}
return c->connection;
}
-void
-mpdclient_put_connection(struct mpdclient *c)
-{
- assert(c->source == NULL || c->connection != NULL);
-
- if (c->source != NULL && !c->idle) {
- c->idle = mpd_glib_enter(c->source);
- }
-}
-
static struct mpd_status *
mpdclient_recv_status(struct mpdclient *c)
{
static struct mpd_status *
mpdclient_recv_status(struct mpdclient *c)
{
bool
mpdclient_cmd_volume_up(struct mpdclient *c)
{
bool
mpdclient_cmd_volume_up(struct mpdclient *c)
{
+ if (c->volume < 0 || c->volume >= 100)
+ return true;
+
struct mpd_connection *connection = mpdclient_get_connection(c);
if (connection == NULL)
return false;
struct mpd_connection *connection = mpdclient_get_connection(c);
if (connection == NULL)
return false;
- if (c->status == NULL ||
- mpd_status_get_volume(c->status) == -1)
- return true;
-
- if (c->volume < 0)
- c->volume = mpd_status_get_volume(c->status);
-
- if (c->volume >= 100)
- return true;
-
return mpdclient_cmd_volume(c, ++c->volume);
}
bool
mpdclient_cmd_volume_down(struct mpdclient *c)
{
return mpdclient_cmd_volume(c, ++c->volume);
}
bool
mpdclient_cmd_volume_down(struct mpdclient *c)
{
+ if (c->volume <= 0)
+ return true;
+
struct mpd_connection *connection = mpdclient_get_connection(c);
if (connection == NULL)
return false;
struct mpd_connection *connection = mpdclient_get_connection(c);
if (connection == NULL)
return false;
- if (c->status == NULL || mpd_status_get_volume(c->status) < 0)
- return true;
-
- if (c->volume < 0)
- c->volume = mpd_status_get_volume(c->status);
-
- if (c->volume <= 0)
- return true;
-
return mpdclient_cmd_volume(c, --c->volume);
}
return mpdclient_cmd_volume(c, --c->volume);
}