empc/src/bin/empdd.c

1496 lines
52 KiB
C

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "empdd.h"
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <Ecore_Con.h>
#include <Eldbus.h>
#include <mpd/client.h>
#include <mpd/parser.h>
#include <mpd/async.h>
#define STRING_SAFETY(X) X ?: ""
#define S(X) #X
#define RECONNECT_THRESH 5
typedef struct E_Slist E_Slist;
struct E_Slist
{
E_Slist *next;
unsigned int command;
void *data;
};
typedef struct EMPD
{
Ecore_Con_Server *svr;
Ecore_Fd_Handler *fdh;
Ecore_Poller *pinger;
struct mpd_settings *settings;
struct mpd_async *async;
struct mpd_parser *parser;
Eina_List *pending;
Eina_List *current_queue;
void *cur;
E_Slist *cmds, *last;
unsigned int reconnect_fail;
Eina_Bool connected;
Eina_Bool idle : 1;
} EMPD;
typedef enum
{
EMPD_COMMAND_NONE = 0,
EMPD_COMMAND_PASSWORD,
EMPD_COMMAND_STATUS,
EMPD_COMMAND_CONFIG,
EMPD_COMMAND_CURRENT_SONG,
EMPD_COMMAND_PLAY,
EMPD_COMMAND_PAUSE,
EMPD_COMMAND_TOGGLE_PAUSE,
EMPD_COMMAND_STOP,
EMPD_COMMAND_NEXT,
EMPD_COMMAND_PREVIOUS,
EMPD_COMMAND_SEEK,
EMPD_COMMAND_PLAY_ID,
EMPD_COMMAND_QUEUE_LIST,
EMPD_COMMAND_CLEAR_LIST,
EMPD_COMMAND_DELETE_LIST,
EMPD_COMMAND_ADD_LIST,
EMPD_COMMAND_ADDID_LIST,
EMPD_COMMAND_MOVE_LIST,
EMPD_COMMAND_SAVE_PLAYLIST,
EMPD_COMMAND_ADDTO_PLAYLIST,
EMPD_COMMAND_LOAD_PLAYLIST,
EMPD_COMMAND_REPEAT,
EMPD_COMMAND_RANDOM,
EMPD_COMMAND_SINGLE,
EMPD_COMMAND_CONSUME,
EMPD_COMMAND_LSINFO,
EMPD_COMMAND_UPDATE,
EMPD_COMMAND_FIND,
EMPD_COMMAND_IDLE,
EMPD_COMMAND_QUEUE_CHANGES_META,
} EMPD_Command;
static const char *cmd_txt[] =
{
#define CMD_TXT(X) \
[X] = S(X),
CMD_TXT(EMPD_COMMAND_NONE)
CMD_TXT(EMPD_COMMAND_PASSWORD)
CMD_TXT(EMPD_COMMAND_STATUS)
CMD_TXT(EMPD_COMMAND_CONFIG)
CMD_TXT(EMPD_COMMAND_CURRENT_SONG)
CMD_TXT(EMPD_COMMAND_PLAY)
CMD_TXT(EMPD_COMMAND_PAUSE)
CMD_TXT(EMPD_COMMAND_TOGGLE_PAUSE)
CMD_TXT(EMPD_COMMAND_STOP)
CMD_TXT(EMPD_COMMAND_NEXT)
CMD_TXT(EMPD_COMMAND_PREVIOUS)
CMD_TXT(EMPD_COMMAND_SEEK)
CMD_TXT(EMPD_COMMAND_PLAY_ID)
CMD_TXT(EMPD_COMMAND_QUEUE_LIST)
CMD_TXT(EMPD_COMMAND_CLEAR_LIST)
CMD_TXT(EMPD_COMMAND_DELETE_LIST)
CMD_TXT(EMPD_COMMAND_ADD_LIST)
CMD_TXT(EMPD_COMMAND_ADDID_LIST)
CMD_TXT(EMPD_COMMAND_MOVE_LIST)
CMD_TXT(EMPD_COMMAND_SAVE_PLAYLIST)
CMD_TXT(EMPD_COMMAND_ADDTO_PLAYLIST)
CMD_TXT(EMPD_COMMAND_LOAD_PLAYLIST)
CMD_TXT(EMPD_COMMAND_REPEAT)
CMD_TXT(EMPD_COMMAND_RANDOM)
CMD_TXT(EMPD_COMMAND_SINGLE)
CMD_TXT(EMPD_COMMAND_CONSUME)
CMD_TXT(EMPD_COMMAND_LSINFO)
CMD_TXT(EMPD_COMMAND_UPDATE)
CMD_TXT(EMPD_COMMAND_FIND)
CMD_TXT(EMPD_COMMAND_IDLE)
CMD_TXT(EMPD_COMMAND_QUEUE_CHANGES_META)
};
typedef enum
{
EMPD_SIGNAL_LOGIN_FAIL,
EMPD_SIGNAL_PERMISSION_FAIL,
EMPD_SIGNAL_CONNECTED,
EMPD_SIGNAL_DISCONNECTED,
EMPD_SIGNAL_STATUS,
EMPD_SIGNAL_CURRENT_SONG,
EMPD_SIGNAL_QUEUE_LIST,
EMPD_SIGNAL_QUEUE_CHANGES_META,
EMPD_SIGNAL_DATABASE_UPDATE_BEGIN,
EMPD_SIGNAL_DATABASE_UPDATE_END,
} EMPD_Signals;
static const Eldbus_Signal empd_signals[] =
{
[EMPD_SIGNAL_LOGIN_FAIL] = {"LoginFailed", ELDBUS_ARGS({"s", "host"}, {"u", "port"}), 0},
[EMPD_SIGNAL_PERMISSION_FAIL] = {"PermissionFailed", ELDBUS_ARGS({"s", "host"}, {"u", "port"}, {"u", "code"}), 0},
[EMPD_SIGNAL_CONNECTED] = {"Connected", ELDBUS_ARGS({"s", "host"}, {"u", "port"}), 0},
[EMPD_SIGNAL_DISCONNECTED] = {"Disconnected", NULL, 0},
[EMPD_SIGNAL_STATUS] = {"Status", ELDBUS_ARGS({"i", "volume"}, {"b", "repeat"}, {"b", "random"},
{"b", "single"}, {"b", "consume"}, {"u", "queue_version"}, {"u", "queue_length"},
{"d", "mixrampdb"}, {"u", "state"}, {"i", "song_pos"}, {"i", "songid"},
{"u", "song_length"}, {"u", "song_elapsed"}, {"u", "bitrate"},
{"u", "sample_rate"}, {"u", "sample_bits"}, {"u", "sample_channels"}, {"i", "next_song_pos"}, {"i", "next_songid"},
{"u", "update_id"}), 0},
[EMPD_SIGNAL_CURRENT_SONG] = {"CurrentSong", ELDBUS_ARGS({"s", "uri"}, {"t", "last_modified"}, {"u", "duration"},
{"s", "artist"}, {"s", "composer"}, {"s", "title"}, {"s", "album"}, {"i", "track"},
{"s", "name"}, {"s", "date"}, {"s", "disc"}, {"s", "genre"}, {"i", "song_pos"}, {"i", "songid"}), 0},
[EMPD_SIGNAL_QUEUE_LIST] = {"QueueList", ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), 0},
[EMPD_SIGNAL_QUEUE_CHANGES_META] = {"QueueChangesMeta", ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), 0},
[EMPD_SIGNAL_DATABASE_UPDATE_BEGIN] = {"DatabaseUpdateBegin", NULL, 0},
[EMPD_SIGNAL_DATABASE_UPDATE_END] = {"DatabaseUpdateEnd", NULL, 0},
{NULL, NULL, 0}
};
static double elapsed = 0;
static EMPD *empd = NULL;
static Eina_List *handlers = NULL;
static Eina_Mempool *slist_mempool = NULL;
static Eldbus_Connection *dbus_conn = NULL;
static Eldbus_Service_Interface *empd_iface = NULL;
static Eina_Bool fetching_queue = EINA_FALSE;
static unsigned int empd_queue_version = 0;
static unsigned int empd_queue_length = 0;
static int empd_songid = -1;
static int empd_dbupdate = 0;
static inline void
fdh_update(void)
{
int flags;
flags = mpd_async_events(empd->async);
flags &= ~MPD_ASYNC_EVENT_ERROR & ~MPD_ASYNC_EVENT_HUP;
ecore_main_fd_handler_active_set(empd->fdh, flags);
}
static inline E_Slist *
cmd_append(EMPD_Command cmd)
{
E_Slist *es;
es = eina_mempool_malloc(slist_mempool, sizeof(E_Slist));
es->command = cmd;
es->next = NULL;
es->data = NULL;
if (empd->cmds)
empd->last->next = es;
else
empd->cmds = es;
if (empd->idle && (cmd != EMPD_COMMAND_IDLE))
{
empd->idle = 0;
mpd_async_send_command(empd->async, "noidle", NULL);
fdh_update();
}
//fprintf(stderr, "CMD ADD: %s\n", cmd_txt[es->command]);
empd->last = es;
//es = empd->cmds;
//while (es)
//{
//fprintf(stderr, "CMD ADD CYCLE: %s\n", cmd_txt[es->command]);
//es = es->next;
//}
return es;
}
static inline void
cmd_remove(void)
{
E_Slist *es = empd->cmds;
if (!es) return;
//fprintf(stderr, "CMD REMOVE: %s\n", cmd_txt[es->command]);
//if (es->next)
//fprintf(stderr, "CMD NEXT: %s\n", cmd_txt[es->next->command]);
//else
//fprintf(stderr, "CMD END\n");
empd->cmds = es->next;
if (es->command == EMPD_COMMAND_QUEUE_LIST)
fetching_queue = EINA_FALSE;
eina_mempool_free(slist_mempool, es);
if (!empd->cmds)
empd->last = NULL;
empd->cur = NULL;
if (!empd->connected) return;
if (!empd->cmds)
{
mpd_async_send_command(empd->async, "idle database playlist player update options", NULL, NULL);
empd->idle = 1;
ecore_main_fd_handler_active_set(empd->fdh, ECORE_FD_WRITE);
cmd_append(EMPD_COMMAND_IDLE);
}
//es = empd->cmds;
//while (es)
//{
//fprintf(stderr, "CMD REMOVE CYCLE: %s\n", cmd_txt[es->command]);
//if (es == es->next) abort();
//es = es->next;
//}
}
static void
cmd_clear(void)
{
while (empd->cmds)
cmd_remove();
}
static inline EMPD_Command
cmd_get(void)
{
return empd->cmds ? empd->cmds->command : EMPD_COMMAND_NONE;
}
static inline Ecore_Con_Type
conn_type_get(const char *host)
{
if (host[0] == '/') return ECORE_CON_LOCAL_SYSTEM;
return ECORE_CON_REMOTE_TCP;
}
static void
conn_reset(void)
{
if (empd->connected)
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DISCONNECTED);
empd_queue_version = 0;
empd_songid = -1;
empd_dbupdate = 0;
empd->connected = 0;
ecore_con_server_del(empd->svr);
empd->svr = NULL;
if (empd->async)
mpd_async_free(empd->async);
empd->async = NULL;
ecore_main_fd_handler_del(empd->fdh);
empd->fdh = NULL;
ecore_poller_del(empd->pinger);
empd->pinger = NULL;
cmd_clear();
}
static void
reconnect(const char *host, int port, const char *pass)
{
struct mpd_settings *s = empd->settings;
const char *h = host;
int p = port;
if (pass && (!pass[0]))
pass = NULL;
if (s)
h = host && host[0] ? host : mpd_settings_get_host(s);
if (h && (h[0] == '/'))
p = -1;
else if (s)
p = port == -1 ? (int)mpd_settings_get_port(s) : port;
if (host || s)
s = mpd_settings_new(h, p, 3000, NULL, pass);
if ((empd->async || empd->svr) && empd->settings &&
((!host) || (!host[0]) || (!strcmp(host, mpd_settings_get_host(empd->settings)))) &&
((port == -1) || ((unsigned int)port == mpd_settings_get_port(empd->settings))))
{
if (empd->async)
{
if ((!empd->connected) && ((!pass) || (!pass[0])))
{
/* attempting to reconnect to current server with no password
* despite needing a password: paddlin.
*/
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_LOGIN_FAIL,
mpd_settings_get_host(s),
mpd_settings_get_port(s));
}
else
{
cmd_append(EMPD_COMMAND_PASSWORD);
mpd_async_send_command(empd->async, "password", mpd_settings_get_password(s), NULL);
fdh_update();
}
}
}
else
{
conn_reset();
h = mpd_settings_get_host(s);
empd->svr = ecore_con_server_connect(conn_type_get(h), h, p, empd);
}
if (empd->settings)
mpd_settings_free(empd->settings);
empd->settings = s;
}
static Eina_Bool
pinger_cb(void *d EINA_UNUSED)
{
cmd_append(EMPD_COMMAND_STATUS);
mpd_async_send_command(empd->async, "status", NULL);
fdh_update();
return EINA_TRUE;
}
static void
queue_list_info_send(Eldbus_Message *msg, Eina_List *queue_list, unsigned int start, long num, Eina_Bool sig)
{
Eldbus_Message_Iter *iter, *array, *struc;
Eina_List *l;
struct mpd_song *so;
unsigned int cur = 0;
iter = eldbus_message_iter_get(msg);
array = eldbus_message_iter_container_new(iter, 'a', "(stusssisssii)");
EINA_LIST_FOREACH(queue_list, l, so)
{
/* holy shit. */
const char *track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
if (cur++ < start) continue;
eldbus_message_iter_arguments_append(array, "(stusssisssii)", &struc);
eldbus_message_iter_arguments_append(struc, "stusssisssii",
STRING_SAFETY(mpd_song_get_uri(so)), mpd_song_get_last_modified(so), mpd_song_get_duration(so),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ARTIST, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_TITLE, 0)),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ALBUM, 0)),
track ? atoi(track) : 0, STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_NAME, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DATE, 0)),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DISC, 0)), mpd_song_get_pos(so), mpd_song_get_id(so));
eldbus_message_iter_container_close(array, struc);
if (mpd_song_get_pos(so) == num) break;
}
eldbus_message_iter_container_close(iter, array);
if (sig)
eldbus_service_signal_send(empd_iface, msg);
}
static void
queue_list_send(Eldbus_Message *msg, unsigned int start, long num)
{
Eina_Bool sig = !msg;
if (!msg)
msg = eldbus_service_signal_new(empd_iface, EMPD_SIGNAL_QUEUE_LIST);
queue_list_info_send(msg, empd->current_queue, start, num, sig);
}
static Eina_Bool
queue_fetch(void)
{
if (fetching_queue) return EINA_FALSE;
fetching_queue = EINA_TRUE;
cmd_append(EMPD_COMMAND_QUEUE_LIST);
mpd_async_send_command(empd->async, "playlistinfo", NULL);
fdh_update();
return EINA_TRUE;
}
static Eina_Bool
fdh_func(void *d EINA_UNUSED, Ecore_Fd_Handler *fdh EINA_UNUSED)
{
char *txt;
Eina_Bool status_pending = EINA_FALSE;
if (!mpd_async_io(empd->async, MPD_ASYNC_EVENT_READ | MPD_ASYNC_EVENT_WRITE))
{
Eina_Bool connected = empd->connected;
sleep(2);
conn_reset();
if (connected && (empd->reconnect_fail < RECONNECT_THRESH))
{
reconnect(NULL, -1, mpd_settings_get_password(empd->settings));
empd->reconnect_fail++;
}
return EINA_FALSE;
}
fdh_update();
txt = mpd_async_recv_line(empd->async);
if (!txt) return EINA_TRUE;
while (txt)
{
const char *name = NULL, *value = NULL;
enum mpd_parser_result res = mpd_parser_feed(empd->parser, txt);
switch (res)
{
case MPD_PARSER_MALFORMED: abort(); //FIXME
/**
* MPD has returned "ACK" with an error code. Call
* mpd_parser_get_server_error() to get the error code.
*/
case MPD_PARSER_ERROR:
if (mpd_parser_get_server_error(empd->parser) == MPD_SERVER_ERROR_PASSWORD)
{
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_LOGIN_FAIL,
mpd_settings_get_host(empd->settings),
mpd_settings_get_port(empd->settings));
empd->connected = 0;
goto end;
}
if (mpd_parser_get_server_error(empd->parser) == MPD_SERVER_ERROR_PERMISSION)
{
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_PERMISSION_FAIL,
mpd_settings_get_host(empd->settings),
mpd_settings_get_port(empd->settings),
mpd_parser_get_at(empd->parser));
}
if (mpd_async_get_error(empd->async) == MPD_ERROR_CLOSED)
{
sleep(2);
conn_reset();
return EINA_FALSE;
}
break;
/**
* MPD has returned a name-value pair. Call
* mpd_parser_get_name() and mpd_parser_get_value().
*/
case MPD_PARSER_PAIR:
/**
* MPD has returned "OK" or "list_OK" (check with
* mpd_parser_is_discrete()).
*/
name = mpd_parser_get_name(empd->parser);
value = mpd_parser_get_value(empd->parser);
//fprintf(stderr, "[%s]|%s: %s\n", cmd_txt[cmd_get()], name, value);
break;
case MPD_PARSER_SUCCESS:
//fprintf(stderr, "[%s]|SUCCESS\n", cmd_txt[cmd_get()]);
if (cmd_get() == EMPD_COMMAND_PASSWORD)
pinger_cb(NULL);
break;
}
switch (cmd_get())
{
case EMPD_COMMAND_IDLE:
{
if (!name) break; //ending idle
switch (mpd_idle_name_parse(value))
{
case MPD_IDLE_DATABASE:
if (empd_dbupdate)
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_END);
empd_dbupdate = 0;
break;
case MPD_IDLE_QUEUE:
if (!empd->pinger)
pinger_cb(NULL);
break;
case MPD_IDLE_UPDATE:
if (!empd_dbupdate)
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_BEGIN);
empd_dbupdate = -1;
case MPD_IDLE_PLAYER:
case MPD_IDLE_OPTIONS:
if (status_pending) break;
pinger_cb(NULL);
status_pending = EINA_TRUE;
break;
default: break;
}
break;
}
case EMPD_COMMAND_STATUS:
{
struct mpd_status *st;
if (!empd->cur)
empd->cur = mpd_status_begin();
st = empd->cur;
//fprintf(stderr, "[%s]|HIT\n", cmd_txt[cmd_get()]);
if (res == MPD_PARSER_PAIR)
{
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
mpd_status_feed(st, &(struct mpd_pair){name, value});
}
else if (res == MPD_PARSER_SUCCESS)
{
const struct mpd_audio_format *af = mpd_status_get_audio_format(st);
int update;
if (!empd->connected)
{
empd->connected = 1;
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_CONNECTED,
mpd_settings_get_host(empd->settings),
mpd_settings_get_port(empd->settings));
}
//fprintf(stderr, "[%s]|SENT\n", cmd_txt[cmd_get()]);
if (empd_songid != mpd_status_get_song_id(st))
{
cmd_append(EMPD_COMMAND_CURRENT_SONG);
mpd_async_send_command(empd->async, "currentsong", NULL);
}
empd_songid = mpd_status_get_song_id(st);
update = mpd_status_get_update_id(st);
if (empd_dbupdate && (!update))
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_END);
else if ((!empd_dbupdate) && update)
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_DATABASE_UPDATE_BEGIN);
empd_dbupdate = update;
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_STATUS,
mpd_status_get_volume(st), mpd_status_get_repeat(st), mpd_status_get_random(st),
mpd_status_get_single(st), mpd_status_get_consume(st), mpd_status_get_queue_version(st), mpd_status_get_queue_length(st),
mpd_status_get_mixrampdb(st), mpd_status_get_state(st), mpd_status_get_song_pos(st), mpd_status_get_song_id(st),
mpd_status_get_total_time(st), mpd_status_get_elapsed_time(st), mpd_status_get_kbit_rate(st),
af ? af->sample_rate : 0, af ? af->bits : 0, af ? af->channels : 0,
mpd_status_get_next_song_pos(st), mpd_status_get_next_song_id(st), empd_dbupdate);
empd_queue_length = mpd_status_get_queue_length(st);
/* if (no queue exists) */
if ((!empd->current_queue) && (!empd_queue_length))
empd_queue_version = mpd_status_get_queue_version(st);
/* if (queue exists but has not been fetched) */
else if ((!empd->current_queue) && (mpd_status_get_queue_length(st)))
{
queue_fetch();
empd_queue_version = mpd_status_get_queue_version(st);
}
else
{
unsigned int version = mpd_status_get_queue_version(st);
for (; empd_queue_version < version; empd_queue_version++)
{
char buf[64];
snprintf(buf, sizeof(buf), "%u", empd_queue_version);
cmd_append(EMPD_COMMAND_QUEUE_CHANGES_META);
mpd_async_send_command(empd->async, "plchanges", buf, NULL);
pinger_cb(NULL);
}
}
status_pending = EINA_FALSE;
switch (mpd_status_get_state(st))
{
case MPD_STATE_PLAY:
if (!empd->pinger)
empd->pinger = ecore_poller_add(ECORE_POLLER_CORE, 8, pinger_cb, NULL);
break;
default:
E_FREE_FUNC(empd->pinger, ecore_poller_del);
break;
}
mpd_status_free(st);
}
}
break;
case EMPD_COMMAND_CONFIG:
{
if (res == MPD_PARSER_PAIR)
{
Eldbus_Message *msg;
msg = empd->cmds->data;
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
eldbus_message_arguments_append(msg, "s", value);
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
}
else if (res == MPD_PARSER_ERROR)
{
Eldbus_Message *msg;
msg = empd->cmds->data;
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
eldbus_message_arguments_append(msg, "s", "");
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
}
}
case EMPD_COMMAND_CURRENT_SONG:
case EMPD_COMMAND_QUEUE_LIST:
case EMPD_COMMAND_QUEUE_CHANGES_META:
{
struct mpd_song *so;
so = empd->cur;
//fprintf(stderr, "[%s]|HIT: %d\n", cmd_txt[cmd_get()], res);
if (res == MPD_PARSER_PAIR)
{
if (!empd->cur)
{
empd->cur = mpd_song_begin(&(struct mpd_pair){name, value});
break;
}
mpd_song_feed(so, &(struct mpd_pair){name, value});
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
if (cmd_get() != EMPD_COMMAND_CURRENT_SONG)
{
if (!strcmp(name, "Id"))
{
/* at end of current song, push to list
* for sending all at once later
*/
empd->pending = eina_list_append(empd->pending, so);
empd->cur = NULL;
}
}
}
else if (cmd_get() == EMPD_COMMAND_CURRENT_SONG)
{
const char *track;
if (!so) break; //no current song
track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
//fprintf(stderr, "CURRENT\n");
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_CURRENT_SONG,
STRING_SAFETY(mpd_song_get_uri(so)), mpd_song_get_last_modified(so), mpd_song_get_duration(so),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ARTIST, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_COMPOSER, 0)),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_TITLE, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ALBUM, 0)),
track ? atoi(track) : 0, STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_NAME, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DATE, 0)),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DISC, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_GENRE, 0)),
mpd_song_get_pos(so), mpd_song_get_id(so));
mpd_song_free(so);
}
else if (cmd_get() == EMPD_COMMAND_QUEUE_CHANGES_META)
{
Eina_List *l, *ll;
Eldbus_Message *msg;
if (res != MPD_PARSER_SUCCESS)
break;
msg = eldbus_service_signal_new(empd_iface, EMPD_SIGNAL_QUEUE_CHANGES_META);
queue_list_info_send(msg, empd->pending, 0, -1, 1);
if (!empd->pending)
{
while (eina_list_count(empd->current_queue) > empd_queue_length)
{
mpd_song_free(eina_list_last_data_get(empd->current_queue));
empd->current_queue = eina_list_remove_list(empd->current_queue,
eina_list_last(empd->current_queue));
}
break;
}
so = eina_list_data_get(empd->pending);
EINA_LIST_FOREACH_SAFE(eina_list_nth_list(empd->current_queue, mpd_song_get_pos(so)), l, ll, so)
{
mpd_song_free(so);
empd->current_queue = eina_list_remove_list(empd->current_queue, l);
}
empd->current_queue = eina_list_merge(empd->current_queue, empd->pending);
empd->pending = NULL;
}
else if (cmd_get() == EMPD_COMMAND_QUEUE_LIST)
{
if (res != MPD_PARSER_SUCCESS)
break;
E_FREE_LIST(empd->current_queue, mpd_song_free);
empd->current_queue = empd->pending;
empd->pending = NULL;
if (empd->current_queue)
queue_list_send(NULL, 0, -1);
}
}
break;
case EMPD_COMMAND_LSINFO:
case EMPD_COMMAND_FIND:
{
//fprintf(stderr, "[%s]|HIT\n", cmd_txt[cmd_get()]);
if (res == MPD_PARSER_PAIR)
{
//fprintf(stderr, "[%s]|FEED\n", cmd_txt[cmd_get()]);
if ((!empd->cur) || (!mpd_entity_feed(empd->cur, &(struct mpd_pair){name, value})))
{
empd->cur = mpd_entity_begin(&(struct mpd_pair){name, value});
empd->pending = eina_list_append(empd->pending, empd->cur);
}
}
else if (res == MPD_PARSER_ERROR)
{
Eldbus_Message *msg;
Eldbus_Message_Iter *iter, *array;
Eldbus_Message_Iter *struc;
msg = empd->cmds->data;
iter = eldbus_message_iter_get(msg);
array = eldbus_message_iter_container_new(iter, 'a', "(iv)");
eldbus_message_iter_arguments_append(array, "(iv)", &struc);
eldbus_message_iter_container_close(array, struc);
eldbus_message_iter_container_close(iter, array);
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
}
else
{
struct mpd_entity *ent;
Eldbus_Message *msg;
Eldbus_Message_Iter *iter, *array;
//fprintf(stderr, "[%s]|"EINA_COLOR_BLUE"SEND"EINA_COLOR_RESET" %g\n", cmd_txt[cmd_get()], ecore_time_get() - elapsed);
elapsed = 0;
msg = empd->cmds->data;
iter = eldbus_message_iter_get(msg);
array = eldbus_message_iter_container_new(iter, 'a', "(iv)");
EINA_LIST_FREE(empd->pending, ent)
{
const struct mpd_song *so;
const struct mpd_playlist *pl;
const struct mpd_directory *dir;
const char *track;
Eldbus_Message_Iter *struc, *variant, *s2;
if (mpd_entity_get_type(ent) == MPD_ENTITY_TYPE_UNKNOWN) continue;
eldbus_message_iter_arguments_append(array, "(iv)", &struc);
eldbus_message_iter_basic_append(struc, 'i', mpd_entity_get_type(ent));
switch (mpd_entity_get_type(ent))
{
case MPD_ENTITY_TYPE_SONG:
so = mpd_entity_get_song(ent);
track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
variant = eldbus_message_iter_container_new(struc, 'v', "(stusssisss)");
eldbus_message_iter_arguments_append(variant, "(stusssisss)", &s2);
eldbus_message_iter_arguments_append(s2, "stusssisss",
STRING_SAFETY(mpd_song_get_uri(so)), mpd_song_get_last_modified(so), mpd_song_get_duration(so),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ARTIST, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_TITLE, 0)),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_ALBUM, 0)),
track ? atoi(track) : 0, STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_NAME, 0)), STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DATE, 0)),
STRING_SAFETY(mpd_song_get_tag(so, MPD_TAG_DISC, 0)));
break;
case MPD_ENTITY_TYPE_PLAYLIST:
pl = mpd_entity_get_playlist(ent);
variant = eldbus_message_iter_container_new(struc, 'v', "(st)");
eldbus_message_iter_arguments_append(variant, "(st)", &s2);
eldbus_message_iter_arguments_append(s2, "st",
mpd_playlist_get_path(pl), mpd_playlist_get_last_modified(pl));
break;
case MPD_ENTITY_TYPE_DIRECTORY:
dir = mpd_entity_get_directory(ent);
variant = eldbus_message_iter_container_new(struc, 'v', "(st)");
eldbus_message_iter_arguments_append(variant, "(st)", &s2);
eldbus_message_iter_arguments_append(s2, "st",
mpd_directory_get_path(dir), mpd_directory_get_last_modified(dir));
break;
default:
mpd_entity_free(ent);
continue;
}
eldbus_message_iter_container_close(variant, s2);
eldbus_message_iter_container_close(struc, variant);
eldbus_message_iter_container_close(array, struc);
mpd_entity_free(ent);
}
eldbus_message_iter_container_close(iter, array);
eldbus_connection_send(dbus_conn, msg, NULL, NULL, -1);
empd->cur = NULL;
}
}
break;
default: break;
}
end:
if ((res == MPD_PARSER_SUCCESS) || (res == MPD_PARSER_ERROR))
cmd_remove();
txt = mpd_async_recv_line(empd->async);
//fprintf(stderr, "[%s]|NEXT LINE\n", cmd_txt[cmd_get()]);
}
fdh_update();
//fprintf(stderr, "[%s]|FDH_DONE\n", cmd_txt[cmd_get()]);
return EINA_TRUE;
}
static Eina_Bool
del(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Del *ev EINA_UNUSED)
{
if (!empd->async)
{
empd->svr = NULL;
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_LOGIN_FAIL,
mpd_settings_get_host(empd->settings),
mpd_settings_get_port(empd->settings));
}
return ECORE_CALLBACK_DONE;
}
static Eina_Bool
data_cb(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Data *ev)
{
int fd, flags;
char *txt;
empd->reconnect_fail = 0;
fd = dup(ecore_con_server_fd_get(ev->server));
fcntl(fd, F_SETFL, O_NONBLOCK);
flags = fcntl(fd, F_GETFD);
flags |= FD_CLOEXEC;
fcntl(fd, F_SETFD, flags);
empd->fdh = ecore_main_fd_handler_add(fd, ECORE_FD_READ | ECORE_FD_ERROR, fdh_func, NULL, NULL, NULL);
empd->async = mpd_async_new(fd);
ecore_con_server_del(ev->server);
empd->svr = NULL;
/* this will always just be the first line
* OK MPD 0.18.0\n
*/
txt = ev->data;
txt[ev->size - 1] = 0;
if (!empd->parser)
empd->parser = mpd_parser_new();
if (mpd_settings_get_password(empd->settings))
{
cmd_append(EMPD_COMMAND_PASSWORD);
mpd_async_send_command(empd->async, "password", mpd_settings_get_password(empd->settings), NULL);
fdh_update();
}
else
{
pinger_cb(NULL);
}
return ECORE_CALLBACK_DONE;
}
static void
_dbus_request_name_cb(void *data EINA_UNUSED, const Eldbus_Message *msg, Eldbus_Pending *pending EINA_UNUSED)
{
unsigned int flag;
if (eldbus_message_error_get(msg, NULL, NULL))
fprintf(stderr, "Could not request bus name\n");
else if (!eldbus_message_arguments_get(msg, "u", &flag))
fprintf(stderr, "Could not get arguments on on_name_request\n");
else if (!(flag & ELDBUS_NAME_REQUEST_REPLY_PRIMARY_OWNER))
fprintf(stderr, "Name already in use\n");
}
#define DBUS_BASIC_CB(name, CMD) \
static Eldbus_Message * \
_dbus_##name##_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg) \
{ \
if (!empd->connected) \
return eldbus_message_method_return_new(msg); \
cmd_append(EMPD_COMMAND_##CMD); \
mpd_async_send_command(empd->async, S(name), NULL); \
pinger_cb(NULL); \
return eldbus_message_method_return_new(msg); \
}
DBUS_BASIC_CB(status, STATUS)
DBUS_BASIC_CB(currentsong, CURRENT_SONG)
static Eldbus_Message *
_dbus_pause_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eina_Bool mode;
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
{
cmd_append(EMPD_COMMAND_PAUSE);
mpd_async_send_command(empd->async, "pause", mode ? "1" : "0", NULL);
}
else
{
cmd_append(EMPD_COMMAND_TOGGLE_PAUSE);
mpd_async_send_command(empd->async, "pause", NULL);
}
pinger_cb(NULL);
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_repeat_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eina_Bool mode;
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
{
cmd_append(EMPD_COMMAND_REPEAT);
mpd_async_send_command(empd->async, "repeat", mode ? "1" : "0", NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_shuffle_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eina_Bool mode;
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
{
cmd_append(EMPD_COMMAND_RANDOM);
mpd_async_send_command(empd->async, "random", mode ? "1" : "0", NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_single_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eina_Bool mode;
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
{
cmd_append(EMPD_COMMAND_SINGLE);
mpd_async_send_command(empd->async, "single", mode ? "1" : "0", NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_consume_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eina_Bool mode;
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
{
cmd_append(EMPD_COMMAND_CONSUME);
mpd_async_send_command(empd->async, "consume", mode ? "1" : "0", NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
DBUS_BASIC_CB(stop, STOP)
DBUS_BASIC_CB(next, NEXT)
DBUS_BASIC_CB(previous, PREVIOUS)
static Eldbus_Message *
_dbus_seek_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
unsigned int songid, seconds;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "uu", &songid, &seconds))
{
char id[256], pos[256];
snprintf(id, sizeof(id), "%u", songid);
snprintf(pos, sizeof(pos), "%u", seconds);
cmd_append(EMPD_COMMAND_SEEK);
mpd_async_send_command(empd->async, "seekid", id, pos, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_playid_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
unsigned int songid;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "u", &songid))
{
char id[256];
snprintf(id, sizeof(id), "%u", songid);
cmd_append(EMPD_COMMAND_PLAY_ID);
mpd_async_send_command(empd->async, "playid", id, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_play_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "u")))
{
unsigned int songpos;
if (eldbus_message_arguments_get(msg, "u", &songpos))
{
char pos[256];
snprintf(pos, sizeof(pos), "%u", songpos);
cmd_append(EMPD_COMMAND_PLAY);
mpd_async_send_command(empd->async, "play", pos, NULL);
pinger_cb(NULL);
}
}
else
{
cmd_append(EMPD_COMMAND_PLAY);
mpd_async_send_command(empd->async, "play", NULL, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_queue_list_cached_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
int start, end;
const char *sig;
Eldbus_Message *reply;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (!empd->current_queue)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "ii")) && eldbus_message_arguments_get(msg, "ii", &start, &end))
{
if ((end != -1) && (end < 1))
return eldbus_message_method_return_new(msg);
reply = eldbus_message_method_return_new(msg);
queue_list_send(reply, start, start + end);
}
else
{
reply = eldbus_message_method_return_new(msg);
queue_list_send(reply, 0, -1);
}
return reply;
}
static Eldbus_Message *
_dbus_queue_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
int start, end;
const char *sig;
if (fetching_queue || (!empd->connected)) return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "ii")) && eldbus_message_arguments_get(msg, "ii", &start, &end))
{
char s[256];
if ((end != -1) && (end < 1))
return eldbus_message_method_return_new(msg);
if (end == -1)
snprintf(s, sizeof(s), "%d:", start);
else
snprintf(s, sizeof(s), "%d:%d", start, end);
cmd_append(EMPD_COMMAND_QUEUE_LIST);
mpd_async_send_command(empd->async, "playlistinfo", s, NULL);
}
else
queue_fetch();
fdh_update();
return eldbus_message_method_return_new(msg);
}
DBUS_BASIC_CB(clear, CLEAR_LIST)
static Eldbus_Message *
_dbus_delete_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
int start, num;
const char *sig;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
sig = eldbus_message_signature_get(msg);
if (sig && (!strcmp(sig, "ii")) && eldbus_message_arguments_get(msg, "ii", &start, &num))
{
char s[256];
if ((num != -1) && (num < 1)) //trying to delete less than 1 song...
return eldbus_message_method_return_new(msg);
if (num == -1)
snprintf(s, sizeof(s), "%d:", start);
else
snprintf(s, sizeof(s), "%d:%d", start, start + num);
cmd_append(EMPD_COMMAND_DELETE_LIST);
mpd_async_send_command(empd->async, "delete", s, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_add_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *uri;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "s", &uri))
{
cmd_append(EMPD_COMMAND_ADD_LIST);
mpd_async_send_command(empd->async, "add", uri, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_addid_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *uri;
unsigned int pos;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "su", &uri, &pos))
{
char buf[64];
cmd_append(EMPD_COMMAND_ADDID_LIST);
snprintf(buf, sizeof(buf), "%u", pos);
mpd_async_send_command(empd->async, "addid", uri, buf, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_move_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
unsigned int start, target;
int num;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "uiu", &start, &num, &target))
{
char s[256], s2[256];
if ((num != -1) && (num < 1)) //trying to move less than 1 song...
return eldbus_message_method_return_new(msg);
if (num == -1)
snprintf(s, sizeof(s), "%u:", start);
else
snprintf(s, sizeof(s), "%u:%d", start, start + num);
snprintf(s2, sizeof(s2), "%u", target);
cmd_append(EMPD_COMMAND_MOVE_LIST);
mpd_async_send_command(empd->async, "move", s, s2, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_save_playlist_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *uri;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "s", &uri))
{
cmd_append(EMPD_COMMAND_SAVE_PLAYLIST);
mpd_async_send_command(empd->async, "save", uri, NULL);
fdh_update();
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_addto_playlist_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *playlist, *uri;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "ss", &playlist, &uri))
{
cmd_append(EMPD_COMMAND_ADDTO_PLAYLIST);
mpd_async_send_command(empd->async, "playlistadd", playlist, uri, NULL);
fdh_update();
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_load_playlist_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *uri;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (eldbus_message_arguments_get(msg, "s", &uri))
{
cmd_append(EMPD_COMMAND_LOAD_PLAYLIST);
mpd_async_send_command(empd->async, "load", uri, NULL);
pinger_cb(NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_listinfo_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *path;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (!eldbus_message_arguments_get(msg, "s", &path))
return eldbus_message_method_return_new(msg);
cmd_append(EMPD_COMMAND_LSINFO)->data = eldbus_message_method_return_new(msg);
mpd_async_send_command(empd->async, "lsinfo", path, NULL);
fdh_update();
elapsed = ecore_time_get();
return NULL;
}
static Eldbus_Message *
_dbus_update_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *path;
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (!eldbus_message_arguments_get(msg, "s", &path))
return eldbus_message_method_return_new(msg);
cmd_append(EMPD_COMMAND_UPDATE);
mpd_async_send_command(empd->async, "update", path, NULL);
fdh_update();
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_isconnected_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
Eldbus_Message *ret;
const char *host = "";
unsigned int port = DEFAULT_PORT;
ret = eldbus_message_method_return_new(msg);
if (empd->settings)
{
host = mpd_settings_get_host(empd->settings);
port = mpd_settings_get_port(empd->settings);
}
eldbus_message_arguments_append(ret, "bsu", empd->connected, host, port);
return ret;
}
static Eldbus_Message *
_dbus_connect_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *host = NULL, *pass = NULL;
int port = DEFAULT_PORT;
if (!eldbus_message_arguments_get(msg, "sis", &host, &port, &pass))
return eldbus_message_method_return_new(msg);
if (empd->settings)
reconnect(host, port, pass);
else
{
empd->settings = mpd_settings_new(host, port, 3000, NULL, pass);
reconnect(NULL, -1, NULL);
}
return eldbus_message_method_return_new(msg);
}
static Eldbus_Message *
_dbus_config_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
if (!empd->connected)
return eldbus_message_method_return_new(msg);
cmd_append(EMPD_COMMAND_CONFIG)->data = eldbus_message_method_return_new(msg);
mpd_async_send_command(empd->async, "config", NULL);
fdh_update();
return NULL;
}
static Eldbus_Message *
_dbus_find_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
{
const char *param;
Empd_Search_Type type;
const char *args[] =
{
"file",
"any",
"base",
"modified-since",
};
char buf[4096];
if (!empd->connected)
return eldbus_message_method_return_new(msg);
if (!eldbus_message_arguments_get(msg, "us", &type, &param))
return eldbus_message_method_return_new(msg);
switch (type)
{
case EMPD_SEARCH_TYPE_MODIFIED:
break;
default: //FIXME
return eldbus_message_method_return_new(msg);
break;
}
cmd_append(EMPD_COMMAND_FIND)->data = eldbus_message_method_return_new(msg);
snprintf(buf, sizeof(buf), "find %s", args[type]);
mpd_async_send_command(empd->async, buf, param, NULL);
fdh_update();
return NULL;
}
static const Eldbus_Method empd_methods[] =
{
{ "Connect", ELDBUS_ARGS({"s", "host"}, {"i", "port"}, {"s", "password"}), NULL, _dbus_connect_cb, 0},
{ "IsConnected", NULL, ELDBUS_ARGS({"b", "isconnected"}, {"s", "host"}, {"u", "port"}), _dbus_isconnected_cb, 0},
{ "Status", NULL, NULL, _dbus_status_cb, 0},
{ "Config", NULL, ELDBUS_ARGS({"s", "music_directory"}), _dbus_config_cb, 0},
{ "CurrentSong", NULL, NULL, _dbus_currentsong_cb, 0},
{ "Play", NULL, NULL, _dbus_play_cb, 0},
{ "Pause", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_pause_cb, 0},
{ "TogglePause", NULL, NULL, _dbus_pause_cb, 0},
{ "Stop", NULL, NULL, _dbus_stop_cb, 0},
{ "Next", NULL, NULL, _dbus_next_cb, 0},
{ "Previous", NULL, NULL, _dbus_previous_cb, 0},
{ "Seek", ELDBUS_ARGS({"u", "songid"}, {"u", "position_in_seconds"}), NULL, _dbus_seek_cb, 0},
{ "PlayId", ELDBUS_ARGS({"u", "songid"}), NULL, _dbus_playid_cb, 0},
{ "PlayPos", ELDBUS_ARGS({"u", "songpos"}), NULL, _dbus_play_cb, 0},
{ "QueueList", NULL, NULL, _dbus_queue_list_cb, 0},
{ "QueueListCached", NULL, ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), _dbus_queue_list_cached_cb, 0},
{ "QueueListRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), NULL, _dbus_queue_list_cb, 0},
{ "QueueListCachedRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), _dbus_queue_list_cached_cb, 0},
{ "ClearList", NULL, NULL, _dbus_clear_cb, 0},
{ "DeleteListRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), NULL, _dbus_delete_list_cb, 0},
{ "AddList", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_add_list_cb, 0},
{ "AddIdList", ELDBUS_ARGS({"s", "path"}, {"u", "position"}), NULL, _dbus_addid_list_cb, 0},
{ "SavePlaylist", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_save_playlist_cb, 0},
{ "AddToPlaylist", ELDBUS_ARGS({"s", "playlist"}, {"s", "uri"}), NULL, _dbus_addto_playlist_cb, 0},
{ "LoadPlaylist", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_load_playlist_cb, 0},
{ "MoveList", ELDBUS_ARGS({"u", "start_pos"}, {"i", "number"}, {"u", "target_pos"}), NULL, _dbus_move_list_cb, 0},
{ "Repeat", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_repeat_cb, 0},
{ "Shuffle", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_shuffle_cb, 0},
{ "Single", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_single_cb, 0},
{ "Consume", ELDBUS_ARGS({"b", "mode"}), NULL, _dbus_consume_cb, 0},
{ "ListInfo", ELDBUS_ARGS({"s", "path"}), ELDBUS_ARGS({"a(iv)", "array_of_infos"}), _dbus_listinfo_cb, 0},
{ "Update", ELDBUS_ARGS({"s", "path"}), NULL, _dbus_update_cb, 0},
{ "Find", ELDBUS_ARGS({"u", "search type"}, {"s", "param"}), ELDBUS_ARGS({"a(iv)", "array_of_infos"}), _dbus_find_cb, 0},
{NULL, NULL, NULL, NULL, 0}
};
static const Eldbus_Service_Interface_Desc base_desc =
{
EMPDD_METHOD_BASE, empd_methods, empd_signals, NULL, NULL, NULL
};
int
main(int argc, char *argv[])
{
struct mpd_settings *settings = NULL;
const char *h, *pass, *p = NULL;
int i;
for (i = 0; i < argc; i++)
if (argv[i][0] == '-')
{
fprintf(stderr, "USAGE: %s <HOST<:PORT>> <PORT>\n", argv[0]);
return 1;
}
pass = getenv("MPD_PASSWORD");
p = getenv("MPD_PORT");
if (argc < 2)
{
int port = DEFAULT_PORT;
h = getenv("MPD_HOST");
if (h)
{
if (!p)
p = strchr(h, ':');
if (p)
{
char host[PATH_MAX] = {0};
if (p - h > PATH_MAX - 1)
{
fprintf(stderr, "MPD_HOST longer than 4096 chars? I don't believe you.\n");
exit(-1);
}
port = strtol(p + 1, NULL, 10);
memcpy(host, h, p - h);
settings = mpd_settings_new(host, port, 3000, NULL, pass);
}
else
settings = mpd_settings_new(h, port, 3000, NULL, pass);
}
else
{
struct stat st;
if (!stat("/var/lib/mpd/socket", &st))
settings = mpd_settings_new("/var/lib/mpd/socket", p ? strtol(p, NULL, 10) : port, 3000, NULL, pass);
else if (!stat("/run/mpd/socket", &st))
settings = mpd_settings_new("/run/mpd/socket", p ? strtol(p, NULL, 10) : port, 3000, NULL, pass);
}
}
else
settings = mpd_settings_new(argv[1], atoi(argv[2]), 3000, NULL, pass);
empd = calloc(1, sizeof(EMPD));
empd->settings = settings;
ecore_app_no_system_modules();
ecore_con_init();
eldbus_init();
ecore_app_args_set(argc, (const char**)argv);
{
const char *t;
t = getenv("EINA_MEMPOOL");
if ((!t) || (!t[0])) t = "chained_mempool";
slist_mempool = eina_mempool_add(t, "E_Slist", NULL, sizeof(E_Slist), 64);
if (!slist_mempool)
slist_mempool = eina_mempool_add("pass_through", "E_Slist", NULL, sizeof(E_Slist), 64);
}
dbus_conn = eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION);
eldbus_name_request(dbus_conn, EMPDD_METHOD_BASE, 0, _dbus_request_name_cb, NULL);
empd_iface = eldbus_service_interface_register(dbus_conn, "/", &base_desc);
E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_SERVER_DEL, del, NULL);
E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_SERVER_DATA, data_cb, NULL);
if (settings)
reconnect(NULL, -1, NULL);
ecore_main_loop_begin();
return 0;
(void)cmd_txt;
}