first commit, broken list in genlist attempt
This commit is contained in:
commit
8ad564eb3a
|
@ -0,0 +1,6 @@
|
|||
if [[ empdd.c -nt empdd ]] ; then
|
||||
gcc -o empdd empdd.c $(pkg-config --cflags --libs ecore ecore-con eldbus eina libmpdclient) -Wall -Wextra -g -O0
|
||||
fi
|
||||
eldbus-codegen empd.xml
|
||||
azy_parser -H empd.azy
|
||||
gcc -o empc empc.c eldbus_empd_empc.c Empd_Common.c Empd_Common_Azy.c $(pkg-config --cflags --libs ecore elementary evas eldbus azy) -Wall -Wextra -g -O0
|
|
@ -0,0 +1,609 @@
|
|||
#include "empd.h"
|
||||
#include "eldbus_empd_empc.h"
|
||||
#include <assert.h>
|
||||
#include <Eldbus.h>
|
||||
#include <Elementary.h>
|
||||
#include "Empd_Common_Azy.h"
|
||||
|
||||
#define WEIGHT evas_object_size_hint_weight_set
|
||||
#define ALIGN evas_object_size_hint_align_set
|
||||
#define EXPAND(X) WEIGHT((X), EVAS_HINT_EXPAND, EVAS_HINT_EXPAND)
|
||||
#define FILL(X) ALIGN((X), EVAS_HINT_FILL, EVAS_HINT_FILL)
|
||||
|
||||
enum
|
||||
{
|
||||
MPD_STATE_UNKNOWN = 0,
|
||||
MPD_STATE_STOP = 1,
|
||||
MPD_STATE_PLAY = 2,
|
||||
MPD_STATE_PAUSE = 3,
|
||||
};
|
||||
|
||||
typedef struct Empd_Empc_Album
|
||||
{
|
||||
Eina_Stringshare *artist;
|
||||
Eina_Stringshare *album;
|
||||
Elm_Object_Item *item;
|
||||
Evas_Object *list;
|
||||
Eina_List *songs;
|
||||
} Empd_Empc_Album;
|
||||
|
||||
static int empd_state = 0;
|
||||
static Evas_Object *win = NULL;
|
||||
static Eldbus_Proxy *empd_proxy = NULL;
|
||||
|
||||
static Eina_List *handlers = NULL;
|
||||
static Eina_Hash *empd_current_queue = NULL;
|
||||
static Eina_Hash *empd_current_queue_albums = NULL;
|
||||
|
||||
static int empd_songid = -1;
|
||||
static Eina_Stringshare *empd_song_title = NULL;
|
||||
static Eina_Stringshare *empd_song_artist = NULL;
|
||||
static Eina_Stringshare *empd_song_album = NULL;
|
||||
static unsigned int empd_song_length = 0;
|
||||
static unsigned int empd_song_elapsed = 0;
|
||||
static unsigned int empd_queue_length = 0;
|
||||
|
||||
static void
|
||||
empd_empc_album_item_free(Elm_Object_Item *it)
|
||||
{
|
||||
Empd_Empc_Album *al = elm_object_item_data_get(it);
|
||||
if (!al) return;
|
||||
eina_stringshare_del(al->artist);
|
||||
eina_stringshare_del(al->album);
|
||||
E_FREE_LIST(al->songs, Empd_Empc_Song_free);
|
||||
free(al);
|
||||
}
|
||||
|
||||
static void
|
||||
title_update(void)
|
||||
{
|
||||
char buf[4096];
|
||||
const char *st;
|
||||
|
||||
switch (empd_state)
|
||||
{
|
||||
case MPD_STATE_STOP:
|
||||
st = "Stopped";
|
||||
break;
|
||||
case MPD_STATE_PLAY:
|
||||
st = "Playing";
|
||||
break;
|
||||
case MPD_STATE_PAUSE:
|
||||
st = "Paused";
|
||||
break;
|
||||
case MPD_STATE_UNKNOWN:
|
||||
default:
|
||||
st = "Unknown State";
|
||||
break;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "EMPC | %s - %s (%s)", empd_song_artist, empd_song_title, st);
|
||||
elm_win_title_set(win, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
queue_list_item_text_update(Empd_Empc_Song *so, Elm_Object_Item *it)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
if (!so) so = elm_object_item_data_get(it);
|
||||
|
||||
if (so->track)
|
||||
snprintf(buf, sizeof(buf), "%d. %s (%.2ld:%.2ld)", so->track, so->title ?: so->uri, so->duration / 60, so->duration % 60);
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "%s (%.2ld:%.2ld)", so->title ?: so->uri, so->duration / 60, so->duration % 60);
|
||||
elm_object_item_text_set(it, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
queue_list_item_icon_update(Empd_Empc_Song *so, Elm_Object_Item *it)
|
||||
{
|
||||
Evas_Object *ic;
|
||||
|
||||
if (!so) so = elm_object_item_data_get(it);
|
||||
|
||||
if (so->songid == empd_songid)
|
||||
elm_object_item_part_content_set(it, "elm.swallow.icon", NULL);
|
||||
else
|
||||
{
|
||||
ic = elm_icon_add(win);
|
||||
elm_icon_standard_set(ic, "arrow_right");
|
||||
elm_object_item_part_content_set(it, "elm.swallow.icon", ic);
|
||||
}
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
status_update(int state)
|
||||
{
|
||||
Evas_Object *button, *ic;
|
||||
|
||||
if (state == empd_state) return EINA_FALSE;
|
||||
empd_state = state;
|
||||
button = evas_object_data_get(win, "play_button");
|
||||
ic = elm_object_content_get(button);
|
||||
switch (state)
|
||||
{
|
||||
case MPD_STATE_UNKNOWN:
|
||||
elm_icon_standard_set(ic, "media_player/play");
|
||||
break;
|
||||
case MPD_STATE_STOP:
|
||||
elm_icon_standard_set(ic, "media_player/play");
|
||||
break;
|
||||
case MPD_STATE_PLAY:
|
||||
elm_icon_standard_set(ic, "media_player/pause");
|
||||
break;
|
||||
case MPD_STATE_PAUSE:
|
||||
elm_icon_standard_set(ic, "media_player/play");
|
||||
break;
|
||||
}
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
songid_update(int songid)
|
||||
{
|
||||
Elm_Object_Item *it, *itp;
|
||||
|
||||
if (empd_songid == songid) return EINA_FALSE;
|
||||
itp = eina_hash_find(empd_current_queue, &empd_songid);
|
||||
empd_songid = songid;
|
||||
if (itp) queue_list_item_icon_update(NULL, itp);
|
||||
it = eina_hash_find(empd_current_queue, &empd_songid);
|
||||
if (it) queue_list_item_icon_update(NULL, it);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
time_update(Evas_Object *label)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%.2d:%.2d / %.2d:%.2d",
|
||||
empd_song_elapsed / 60, empd_song_elapsed % 60,
|
||||
empd_song_length / 60, empd_song_length % 60);
|
||||
elm_object_text_set(label, buf);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
empc_connected(void *d EINA_UNUSED, int t EINA_UNUSED, Empd_Empc_Connected_Data *ev)
|
||||
{
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
empc_disconnected(void *d EINA_UNUSED, int t EINA_UNUSED, Empd_Empc_Disconnected_Data *ev)
|
||||
{
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
empc_current_song(void *d EINA_UNUSED, int t EINA_UNUSED, Empd_Empc_CurrentSong_Data *ev)
|
||||
{
|
||||
/* yes, | is intentional. */
|
||||
if (songid_update(ev->songid) |
|
||||
eina_stringshare_replace(&empd_song_title, ev->title) |
|
||||
eina_stringshare_replace(&empd_song_artist, ev->artist))
|
||||
title_update();
|
||||
eina_stringshare_replace(&empd_song_album, ev->album);
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
empc_status(void *d EINA_UNUSED, int t EINA_UNUSED, Empd_Empc_Status_Data *ev)
|
||||
{
|
||||
Eina_Bool up = EINA_FALSE;
|
||||
Evas_Object *o;
|
||||
|
||||
up = (empd_song_length != ev->song_length) || (empd_song_elapsed != ev->song_elapsed);
|
||||
if (up)
|
||||
{
|
||||
empd_song_length = ev->song_length;
|
||||
empd_song_elapsed = ev->song_elapsed;
|
||||
o = evas_object_data_get(win, "slider");
|
||||
elm_slider_value_set(o, (double)ev->song_elapsed / (double)ev->song_length);
|
||||
time_update(evas_object_data_get(win, "label"));
|
||||
}
|
||||
up = songid_update(ev->songid);
|
||||
if (up)
|
||||
empd_empc_current_song_call(empd_proxy);
|
||||
up |= status_update(ev->state);
|
||||
if (up) title_update();
|
||||
if (empd_queue_length != ev->queue_length)
|
||||
{
|
||||
/* load current playlist incrementally (max 30):
|
||||
* - don't block daemon forever with large playlists
|
||||
* - GREATLY decreases startup delay when showing list
|
||||
* - don't block ui when receiving huge playlists
|
||||
*/
|
||||
//unsigned int i, step = (ev->queue_length / 4) % 31;
|
||||
//for (i = 0; i < ev->queue_length; i += step)
|
||||
//empd_empc_queue_list_range_call(empd_proxy, i, i + step);
|
||||
empd_empc_queue_list_range_call(empd_proxy, 0, 11);
|
||||
}
|
||||
empd_queue_length = ev->queue_length;
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
/*
|
||||
static void
|
||||
queue_list_item_mouse_in(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
||||
{
|
||||
Elm_Object_Item *it = data;
|
||||
Empd_Empc_Song *so = elm_object_item_data_get(it);
|
||||
|
||||
|
||||
}
|
||||
*/
|
||||
static void
|
||||
queue_list_double_click(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
|
||||
{
|
||||
Elm_Object_Item *it = event_info;
|
||||
Empd_Empc_Song *so;
|
||||
|
||||
so = elm_object_item_data_get(it);
|
||||
empd_empc_play_id_call(empd_proxy, so->songid);
|
||||
}
|
||||
|
||||
static void
|
||||
queue_list_item_del(Empd_Empc_Album *al, Evas_Object *obj EINA_UNUSED)
|
||||
{
|
||||
char buf[1024];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s%s", al->artist, al->album);
|
||||
eina_hash_del_by_key(empd_current_queue_albums, buf);
|
||||
}
|
||||
|
||||
static void
|
||||
queue_list_album_list_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
Empd_Empc_Album *al = data;
|
||||
Eina_List *l;
|
||||
Empd_Empc_Song *so;
|
||||
|
||||
fprintf(stderr, "LDEL: %p\n", obj);
|
||||
al->list = NULL;
|
||||
EINA_LIST_FOREACH(al->songs, l, so)
|
||||
eina_hash_del_by_key(empd_current_queue, &so->songid);
|
||||
}
|
||||
|
||||
static void
|
||||
lresz(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
||||
{
|
||||
int w, h;
|
||||
|
||||
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
|
||||
fprintf(stderr, "L(%p) %dx%d\n", obj, w, h);
|
||||
}
|
||||
|
||||
static void
|
||||
lchs(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
||||
{
|
||||
int w, h;
|
||||
|
||||
evas_object_size_hint_min_get(obj, &w, &h);
|
||||
fprintf(stderr, "LMIN(%p) %dx%d\n", obj, w, h);
|
||||
}
|
||||
static void
|
||||
resz(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
||||
{
|
||||
int w, h;
|
||||
|
||||
evas_object_geometry_get(obj, NULL, NULL, &w, &h);
|
||||
fprintf(stderr, "FR(%p) %dx%d\n", obj, w, h);
|
||||
}
|
||||
|
||||
static void
|
||||
chs(void *data, Evas *e, Evas_Object *obj, void *event_info)
|
||||
{
|
||||
int w, h;
|
||||
|
||||
evas_object_size_hint_min_get(obj, &w, &h);
|
||||
fprintf(stderr, "FRMIN(%p) %dx%d\n", obj, w, h);
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
queue_list_item_content_get(Empd_Empc_Album *al, Evas_Object *obj EINA_UNUSED, const char *part EINA_UNUSED)
|
||||
{
|
||||
Eina_List *l;
|
||||
Evas_Object *fr;
|
||||
Empd_Empc_Song *so;
|
||||
char buf[1024];
|
||||
|
||||
if (!al->songs) return NULL; //not ready yet
|
||||
|
||||
fr = elm_frame_add(win);
|
||||
EXPAND(fr);
|
||||
FILL(fr);
|
||||
elm_frame_autocollapse_set(fr, EINA_TRUE);
|
||||
al->list = elm_list_add(win);
|
||||
elm_list_mode_set(al->list, ELM_LIST_EXPAND);
|
||||
EXPAND(al->list);
|
||||
FILL(al->list);
|
||||
elm_object_content_set(fr, al->list);
|
||||
snprintf(buf, sizeof(buf), "%s - %s", al->artist, al->album);
|
||||
fprintf(stderr, "REALIZE %s\n", buf);
|
||||
elm_object_text_set(fr, buf);
|
||||
evas_object_event_callback_add(al->list, EVAS_CALLBACK_DEL, queue_list_album_list_del, al);
|
||||
|
||||
EINA_LIST_FOREACH(al->songs, l, so)
|
||||
{
|
||||
Elm_Object_Item *it;
|
||||
|
||||
it = elm_list_item_append(al->list, NULL, NULL, NULL, NULL, so);
|
||||
fprintf(stderr, "LIST ITEM: %s\n", so->uri);
|
||||
queue_list_item_text_update(so, it);
|
||||
queue_list_item_icon_update(so, it);
|
||||
eina_hash_add(empd_current_queue, &so->songid, it);
|
||||
}
|
||||
elm_list_go(al->list);
|
||||
evas_object_event_callback_add(al->list, EVAS_CALLBACK_CHANGED_SIZE_HINTS, lchs, NULL);
|
||||
evas_object_event_callback_add(al->list, EVAS_CALLBACK_RESIZE, lresz, NULL);
|
||||
evas_object_event_callback_add(fr, EVAS_CALLBACK_CHANGED_SIZE_HINTS, chs, NULL);
|
||||
evas_object_event_callback_add(fr, EVAS_CALLBACK_RESIZE, resz, NULL);
|
||||
evas_object_show(al->list);
|
||||
evas_object_show(fr);
|
||||
evas_object_smart_calculate(fr);
|
||||
elm_layout_sizing_eval(fr);
|
||||
evas_object_smart_calculate(al->list);
|
||||
elm_layout_sizing_eval(al->list);
|
||||
//evas_object_size_hint_min_set(fr, 100, 600);
|
||||
//evas_object_size_hint_min_set(al->list, 100, 600);
|
||||
return fr;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
empc_queue_list(void *d EINA_UNUSED, int t EINA_UNUSED, Empd_Empc_QueueList_Data *ev)
|
||||
{
|
||||
Evas_Object *list;
|
||||
Empd_Array_Songs *songs = NULL;
|
||||
Empd_Empc_Song *so, *soi, *sop = NULL;
|
||||
Empd_Empc_Album *al = NULL;
|
||||
char buf[1024];
|
||||
static Elm_Genlist_Item_Class queue_itc = {
|
||||
.item_style = "full",
|
||||
.func = {
|
||||
.text_get = NULL,
|
||||
.content_get = (Elm_Genlist_Item_Content_Get_Cb)queue_list_item_content_get,
|
||||
.del = (Elm_Genlist_Item_Del_Cb)queue_list_item_del
|
||||
},
|
||||
.version = ELM_GENLIST_ITEM_CLASS_VERSION
|
||||
};
|
||||
|
||||
list = evas_object_data_get(win, "list");
|
||||
if (!azy_value_to_Empd_Array_Songs(ev->value, &songs))
|
||||
{
|
||||
EINA_LOG_ERR("conversion failure");
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
EINA_LIST_FREE(songs->songs, so)
|
||||
{
|
||||
Elm_Object_Item *it;
|
||||
Eina_List *l;
|
||||
Eina_Bool found = EINA_FALSE;
|
||||
|
||||
fprintf(stderr, "FINDING SONG: %s\n", so->uri);
|
||||
if ((!al) || (!sop) || (so->album != sop->album) || (so->artist != sop->artist)) // continuing prev album
|
||||
{
|
||||
if (al && (!al->item)) //prev album
|
||||
{
|
||||
al->item = it = elm_genlist_item_append(list, &queue_itc, al, NULL, 0, NULL, NULL);
|
||||
eina_hash_add(empd_current_queue_albums, buf, it);
|
||||
fprintf(stderr, "ADDED ALBUM %s\n", buf);
|
||||
}
|
||||
al = NULL;
|
||||
snprintf(buf, sizeof(buf), "%s%s", so->artist, so->album);
|
||||
it = eina_hash_find(empd_current_queue_albums, buf);
|
||||
if (it) al = elm_object_item_data_get(it);
|
||||
}
|
||||
if (!al)
|
||||
{
|
||||
al = calloc(1, sizeof(Empd_Empc_Album));
|
||||
al->artist = eina_stringshare_ref(so->artist);
|
||||
al->album = eina_stringshare_ref(so->album);
|
||||
fprintf(stderr, "NEW ALBUM: %s\n", al->album);
|
||||
}
|
||||
EINA_LIST_REVERSE_FOREACH(al->songs, l, soi)
|
||||
{
|
||||
if ((soi->track == so->track) && (so->uri == soi->uri)) //update
|
||||
{
|
||||
fprintf(stderr, "SONG UPDATE: %s\n", so->uri);
|
||||
found = EINA_TRUE;
|
||||
if (Empd_Empc_Song_eq(so, soi))
|
||||
{
|
||||
/* no change */
|
||||
Empd_Empc_Song_free(so);
|
||||
break;
|
||||
}
|
||||
Empd_Empc_Song_free(soi);
|
||||
eina_list_data_set(l, so);
|
||||
it = eina_hash_find(empd_current_queue, &so->songid);
|
||||
if (it)
|
||||
{
|
||||
elm_object_item_data_set(it, so);
|
||||
queue_list_item_text_update(so, it);
|
||||
queue_list_item_icon_update(so, it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (soi->track < so->track)
|
||||
{
|
||||
fprintf(stderr, "SONG INSERT(%s): %s\n", al->album, so->uri);
|
||||
found = EINA_TRUE;
|
||||
al->songs = eina_list_append_relative_list(al->songs, so, l);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
fprintf(stderr, "SONG APPEND(%s): %s\n", al->album, so->uri);
|
||||
al->songs = eina_list_append(al->songs, so);
|
||||
}
|
||||
sop = so;
|
||||
}
|
||||
if (!al->item)
|
||||
{
|
||||
al->item = elm_genlist_item_append(list, &queue_itc, al, NULL, 0, NULL, NULL);
|
||||
eina_hash_add(empd_current_queue_albums, buf, al->item);
|
||||
fprintf(stderr, "ADDED ALBUM %s\n", buf);
|
||||
}
|
||||
free(songs);
|
||||
return ECORE_CALLBACK_RENEW;
|
||||
}
|
||||
|
||||
static Evas_Object *
|
||||
button_add(Evas_Object *win, const char *icon)
|
||||
{
|
||||
Evas_Object *o, *ic;
|
||||
|
||||
o = elm_button_add(win);
|
||||
EXPAND(o);
|
||||
FILL(o);
|
||||
ic = elm_icon_add(win);
|
||||
elm_image_resizable_set(ic, 0, 0);
|
||||
elm_icon_standard_set(ic, icon);
|
||||
elm_object_part_content_set(o, "icon", ic);
|
||||
evas_object_show(ic);
|
||||
evas_object_show(o);
|
||||
return o;
|
||||
}
|
||||
|
||||
static void
|
||||
control_skip_back(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
empd_empc_previous_call(empd_proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
control_stop(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
empd_empc_stop_call(empd_proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
control_toggle(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
if (empd_state == MPD_STATE_PLAY)
|
||||
empd_empc_pause_call(empd_proxy, EINA_TRUE);
|
||||
else
|
||||
empd_empc_play_call(empd_proxy);
|
||||
}
|
||||
|
||||
static void
|
||||
control_skip_forward(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
|
||||
{
|
||||
empd_empc_next_call(empd_proxy);
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
status_poller(void *d EINA_UNUSED)
|
||||
{
|
||||
empd_empc_status_call(empd_proxy);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
slider_seek(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
|
||||
{
|
||||
double val;
|
||||
|
||||
val = elm_slider_value_get(obj);
|
||||
empd_empc_seek_call(empd_proxy, empd_songid, lround(val * empd_song_length));
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
Evas_Object *o, *hbox, *vbox, *label;
|
||||
|
||||
eldbus_init();
|
||||
elm_init(argc, argv);
|
||||
|
||||
empd_current_queue = eina_hash_int32_new(NULL);
|
||||
empd_current_queue_albums = eina_hash_string_superfast_new((Eina_Free_Cb)empd_empc_album_item_free);
|
||||
|
||||
ecore_poller_add(ECORE_POLLER_CORE, 8, status_poller, NULL);
|
||||
|
||||
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
|
||||
|
||||
empd_proxy = empd_empc_proxy_get(eldbus_connection_get(ELDBUS_CONNECTION_TYPE_SESSION), EMPD_METHOD_BASE, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, EMPD_EMPC_CONNECTED_EVENT, empc_connected, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, EMPD_EMPC_DISCONNECTED_EVENT, empc_disconnected, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, EMPD_EMPC_CURRENT_SONG_EVENT, empc_current_song, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, EMPD_EMPC_STATUS_EVENT, empc_status, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, EMPD_EMPC_QUEUE_LIST_EVENT, empc_queue_list, NULL);
|
||||
|
||||
empd_empc_current_song_call(empd_proxy);
|
||||
empd_empc_status_call(empd_proxy);
|
||||
|
||||
win = elm_win_util_standard_add("empc", "empc");
|
||||
elm_win_autodel_set(win, 1);
|
||||
|
||||
o = elm_bg_add(win);
|
||||
EXPAND(o);
|
||||
FILL(o);
|
||||
elm_win_resize_object_add(win, o);
|
||||
evas_object_show(o);
|
||||
|
||||
hbox = elm_box_add(win);
|
||||
elm_box_horizontal_set(hbox, EINA_TRUE);
|
||||
elm_box_homogeneous_set(hbox, EINA_TRUE);
|
||||
WEIGHT(hbox, 0, 0);
|
||||
FILL(hbox);
|
||||
evas_object_show(hbox);
|
||||
|
||||
o = button_add(win, "media_player/prev");
|
||||
evas_object_smart_callback_add(o, "clicked", control_skip_back, NULL);
|
||||
elm_box_pack_end(hbox, o);
|
||||
|
||||
o = button_add(win, "media_player/stop");
|
||||
evas_object_smart_callback_add(o, "clicked", control_stop, NULL);
|
||||
elm_box_pack_end(hbox, o);
|
||||
|
||||
o = button_add(win, "media_player/play");
|
||||
evas_object_data_set(win, "play_button", o);
|
||||
evas_object_smart_callback_add(o, "clicked", control_toggle, NULL);
|
||||
elm_box_pack_end(hbox, o);
|
||||
|
||||
o = button_add(win, "media_player/next");
|
||||
evas_object_smart_callback_add(o, "clicked", control_skip_forward, NULL);
|
||||
elm_box_pack_end(hbox, o);
|
||||
|
||||
vbox = elm_box_add(win);
|
||||
EXPAND(vbox);
|
||||
FILL(vbox);
|
||||
elm_box_pack_end(vbox, hbox);
|
||||
elm_win_resize_object_add(win, vbox);
|
||||
|
||||
o = elm_slider_add(win);
|
||||
evas_object_data_set(win, "slider", o);
|
||||
WEIGHT(o, EVAS_HINT_EXPAND, 0);
|
||||
FILL(o);
|
||||
evas_object_smart_callback_add(o, "changed", slider_seek, NULL);
|
||||
elm_slider_min_max_set(o, 0.0, 1.0);
|
||||
elm_box_pack_end(vbox, o);
|
||||
evas_object_show(o);
|
||||
|
||||
label = elm_label_add(win);
|
||||
evas_object_data_set(win, "label", label);
|
||||
elm_box_pack_end(vbox, label);
|
||||
evas_object_show(label);
|
||||
|
||||
o = elm_genlist_add(win);
|
||||
//evas_object_smart_callback_add(o, "realized", queue_list_item_realize, NULL);
|
||||
//evas_object_smart_callback_add(o, "unrealized", queue_list_item_unrealize, NULL);
|
||||
//evas_object_smart_callback_add(o, "clicked,double", queue_list_double_click, NULL);
|
||||
elm_genlist_mode_set(o, ELM_LIST_COMPRESS);
|
||||
elm_scroller_bounce_set(o, 0, 0);
|
||||
elm_scroller_policy_set(o, ELM_SCROLLER_POLICY_OFF, ELM_SCROLLER_POLICY_AUTO);
|
||||
evas_object_data_set(win, "list", o);
|
||||
EXPAND(o);
|
||||
FILL(o);
|
||||
elm_box_pack_end(vbox, o);
|
||||
evas_object_show(o);
|
||||
|
||||
evas_object_show(vbox);
|
||||
|
||||
evas_object_show(win);
|
||||
evas_object_resize(win, 500, 400);
|
||||
|
||||
elm_run();
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
Azy_Namespace Empd;
|
||||
|
||||
struct Empc_Song
|
||||
{
|
||||
string uri;
|
||||
time last_modified;
|
||||
time duration;
|
||||
string artist;
|
||||
string title;
|
||||
string album;
|
||||
int track;
|
||||
string name;
|
||||
string date;
|
||||
string disc;
|
||||
int song_pos;
|
||||
int songid;
|
||||
};
|
||||
|
||||
struct Array_Songs
|
||||
{
|
||||
array<Empc_Song> songs;
|
||||
};
|
|
@ -0,0 +1,48 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include <Ecore.h>
|
||||
|
||||
# define E_FREE(p) do { free(p); p = NULL; } while (0)
|
||||
# define E_FREE_LIST(list, free) \
|
||||
do \
|
||||
{ \
|
||||
void *_tmp_; \
|
||||
EINA_LIST_FREE(list, _tmp_) \
|
||||
{ \
|
||||
free(_tmp_); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
# define E_LIST_REVERSE_FREE(list, data) \
|
||||
for (data = eina_list_last_data_get(list); \
|
||||
list; \
|
||||
list = eina_list_remove_list(list, eina_list_last(list)), \
|
||||
data = eina_list_last_data_get(list))
|
||||
|
||||
# define E_LIST_FOREACH(list, func) \
|
||||
do \
|
||||
{ \
|
||||
void *_tmp_; \
|
||||
const Eina_List *_list, *_list2; \
|
||||
EINA_LIST_FOREACH_SAFE(list, _list, _list2, _tmp_) \
|
||||
{ \
|
||||
func(_tmp_); \
|
||||
} \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
# define E_LIST_HANDLER_APPEND(list, type, callback, data) \
|
||||
do \
|
||||
{ \
|
||||
Ecore_Event_Handler *_eh; \
|
||||
_eh = ecore_event_handler_add(type, (Ecore_Event_Handler_Cb)callback, data); \
|
||||
assert(_eh); \
|
||||
list = eina_list_append(list, _eh); \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#define DEFAULT_PORT 6600
|
||||
#define EMPD_METHOD_BASE "org.empd.empc"
|
|
@ -0,0 +1,116 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<node name="/">
|
||||
<interface name="org.freedesktop.DBus.Introspectable">
|
||||
<method name="Introspect">
|
||||
<arg type="s" direction="out" />
|
||||
</method>
|
||||
</interface>
|
||||
<interface name="org.freedesktop.DBus.Properties">
|
||||
<method name="Get">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="v" direction="out" />
|
||||
</method>
|
||||
<method name="GetAll">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="a{sv}" direction="out" />
|
||||
</method>
|
||||
<method name="Set">
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="s" direction="in" />
|
||||
<arg type="v" direction="in" />
|
||||
</method>
|
||||
<signal name="PropertiesChanged">
|
||||
<arg type="s" />
|
||||
<arg type="a{sv}" />
|
||||
<arg type="as" />
|
||||
</signal>
|
||||
</interface>
|
||||
<interface name="org.empd.empc">
|
||||
<method name="Status">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="CurrentSong">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="Play">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="Pause">
|
||||
<arg type="b" name="mode" direction="in" />
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="TogglePause">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="Stop">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="Next">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="Previous">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="Seek">
|
||||
<arg type="u" name="songid" direction="in" />
|
||||
<arg type="u" name="seconds" direction="in" />
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="PlayId">
|
||||
<arg type="u" name="songid" direction="in" />
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="QueueList">
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<method name="QueueListRange">
|
||||
<arg type="i" name="start" direction="in" />
|
||||
<arg type="i" name="num" direction="in" />
|
||||
<annotation name="org.freedesktop.DBus.Method.NoReply" value="true" />
|
||||
</method>
|
||||
<signal name="Status">
|
||||
<arg type="i" name="volume" direction="out" /><!-- volume % -->
|
||||
<arg type="b" name="repeat" direction="out" />
|
||||
<arg type="b" name="random" direction="out" />
|
||||
<arg type="b" name="single" direction="out" />
|
||||
<arg type="b" name="consume" direction="out" />
|
||||
<arg type="u" name="queue_version" direction="out" />
|
||||
<arg type="u" name="queue_length" direction="out" />
|
||||
<arg type="d" name="mixrampdb" direction="out" />
|
||||
<arg type="u" name="state" direction="out" /><!-- MPD_STATE_UNKNOWN = 0, MPD_STATE_STOP, MPD_STATE_PLAY, MPD_STATE_PAUSE -->
|
||||
<arg type="i" name="song_pos" direction="out" />
|
||||
<arg type="i" name="songid" direction="out" />
|
||||
<arg type="u" name="song_length" direction="out" />
|
||||
<arg type="u" name="song_elapsed" direction="out" />
|
||||
<arg type="u" name="bitrate" direction="out" />
|
||||
<arg type="u" name="sample_rate" direction="out" />
|
||||
<arg type="u" name="sample_bits" direction="out" />
|
||||
<arg type="u" name="sample_channels" direction="out" />
|
||||
<arg type="i" name="next_song_pos" direction="out" />
|
||||
<arg type="i" name="next_songid" direction="out" />
|
||||
</signal>
|
||||
<signal name="CurrentSong">
|
||||
<arg type="s" name="uri" direction="out" />
|
||||
<arg type="t" name="last_modified" direction="out" />
|
||||
<arg type="u" name="duration" direction="out" />
|
||||
<arg type="s" name="artist" direction="out" />
|
||||
<arg type="s" name="title" direction="out" />
|
||||
<arg type="s" name="album" direction="out" />
|
||||
<arg type="i" name="track" direction="out" />
|
||||
<arg type="s" name="name" direction="out" />
|
||||
<arg type="s" name="date" direction="out" />
|
||||
<arg type="s" name="disc" direction="out" />
|
||||
<arg type="i" name="song_pos" direction="out" />
|
||||
<arg type="i" name="songid" direction="out" />
|
||||
</signal>
|
||||
<signal name="QueueList">
|
||||
<arg type="a(stusssisssii)" name="array_of_songs" direction="out" />
|
||||
</signal>
|
||||
<signal name="Connected">
|
||||
</signal>
|
||||
<signal name="Disconnected">
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -0,0 +1,611 @@
|
|||
#include "empd.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
|
||||
|
||||
typedef struct E_Slist E_Slist;
|
||||
struct E_Slist
|
||||
{
|
||||
E_Slist *next;
|
||||
unsigned int command;
|
||||
};
|
||||
|
||||
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_Array *pending;
|
||||
void *cur;
|
||||
E_Slist *cmds, *last;
|
||||
} EMPD;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EMPD_COMMAND_NONE = 0,
|
||||
EMPD_COMMAND_PASSWORD,
|
||||
EMPD_COMMAND_STATUS,
|
||||
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;
|
||||
|
||||
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_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)
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
EMPD_SIGNAL_CONNECTED,
|
||||
EMPD_SIGNAL_DISCONNECTED,
|
||||
EMPD_SIGNAL_STATUS,
|
||||
EMPD_SIGNAL_CURRENT_SONG,
|
||||
EMPD_SIGNAL_QUEUE_LIST,
|
||||
} EMPD_Signals;
|
||||
|
||||
static const Eldbus_Signal empd_signals[] =
|
||||
{
|
||||
[EMPD_SIGNAL_CONNECTED] = {"Connected", NULL, 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"}), 0},
|
||||
[EMPD_SIGNAL_CURRENT_SONG] = {"CurrentSong", ELDBUS_ARGS({"s", "uri"}, {"t", "last_modified"}, {"u", "duration"},
|
||||
{"s", "artist"}, {"s", "title"}, {"s", "album"}, {"i", "track"},
|
||||
{"s", "name"}, {"s", "date"}, {"s", "disc"}, {"i", "song_pos"}, {"i", "songid"}), 0},
|
||||
[EMPD_SIGNAL_QUEUE_LIST] = {"QueueList", ELDBUS_ARGS({"a(stusssisssii)", "array_of_songs"}), 0},
|
||||
{NULL, NULL, 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 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 void
|
||||
cmd_append(EMPD_Command cmd)
|
||||
{
|
||||
E_Slist *es;
|
||||
|
||||
es = eina_mempool_malloc(slist_mempool, sizeof(E_Slist));
|
||||
es->command = cmd;
|
||||
es->next = NULL;
|
||||
if (empd->cmds)
|
||||
empd->last->next = es;
|
||||
else
|
||||
empd->cmds = es;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
eina_mempool_free(slist_mempool, es);
|
||||
if (!empd->cmds)
|
||||
empd->last = NULL;
|
||||
empd->cur = NULL;
|
||||
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 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 Eina_Bool
|
||||
pinger_cb(void *d EINA_UNUSED)
|
||||
{
|
||||
mpd_async_send_command(empd->async, "status", NULL);
|
||||
cmd_append(EMPD_COMMAND_STATUS);
|
||||
fdh_update();
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
fdh_func(void *d EINA_UNUSED, Ecore_Fd_Handler *fdh EINA_UNUSED)
|
||||
{
|
||||
char *txt;
|
||||
|
||||
if (!mpd_async_io(empd->async, MPD_ASYNC_EVENT_READ | MPD_ASYNC_EVENT_WRITE))
|
||||
{
|
||||
ecore_main_loop_quit();
|
||||
return EINA_FALSE;
|
||||
}
|
||||
fdh_update();
|
||||
txt = mpd_async_recv_line(empd->async);
|
||||
if (!txt) return EINA_TRUE;
|
||||
while (txt)
|
||||
{
|
||||
const char *name, *value;
|
||||
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 (cmd_get() == EMPD_COMMAND_PASSWORD)
|
||||
{
|
||||
/* login failure */
|
||||
ecore_main_loop_quit();
|
||||
return EINA_TRUE;
|
||||
}
|
||||
if (mpd_async_get_error(empd->async) == MPD_ERROR_CLOSED)
|
||||
ecore_app_restart();
|
||||
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)
|
||||
{
|
||||
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_CONNECTED);
|
||||
empd->pinger = ecore_poller_add(ECORE_POLLER_CORE, 8, pinger_cb, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
switch (cmd_get())
|
||||
{
|
||||
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
|
||||
{
|
||||
const struct mpd_audio_format *af = mpd_status_get_audio_format(st);
|
||||
|
||||
fprintf(stderr, "[%s]|SENT\n", cmd_txt[cmd_get()]);
|
||||
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));
|
||||
mpd_status_free(st);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EMPD_COMMAND_CURRENT_SONG:
|
||||
case EMPD_COMMAND_QUEUE_LIST:
|
||||
{
|
||||
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 array
|
||||
* for sending all at once later
|
||||
*/
|
||||
if (!empd->pending)
|
||||
empd->pending = eina_array_new(1);
|
||||
eina_array_push(empd->pending, so);
|
||||
empd->cur = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cmd_get() == EMPD_COMMAND_CURRENT_SONG)
|
||||
{
|
||||
const char *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_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));
|
||||
mpd_song_free(so);
|
||||
}
|
||||
else if (cmd_get() == EMPD_COMMAND_QUEUE_LIST)
|
||||
{
|
||||
Eldbus_Message *msg;
|
||||
Eldbus_Message_Iter *iter, *array, *struc;
|
||||
Eina_Iterator *it;
|
||||
struct mpd_song *so;
|
||||
|
||||
fprintf(stderr, "QUEUE\n");
|
||||
|
||||
msg = eldbus_service_signal_new(empd_iface, EMPD_SIGNAL_QUEUE_LIST);
|
||||
iter = eldbus_message_iter_get(msg);
|
||||
array = eldbus_message_iter_container_new(iter, 'a', "(stusssisssii)");
|
||||
it = eina_array_iterator_new(empd->pending);
|
||||
EINA_ITERATOR_FOREACH(it, so)
|
||||
{
|
||||
/* holy shit. */
|
||||
const char *track = mpd_song_get_tag(so, MPD_TAG_TRACK, 0);
|
||||
|
||||
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));
|
||||
mpd_song_free(so);
|
||||
eldbus_message_iter_container_close(array, struc);
|
||||
}
|
||||
eina_iterator_free(it);
|
||||
eina_array_free(empd->pending);
|
||||
empd->pending = NULL;
|
||||
eldbus_message_iter_container_close(iter, array);
|
||||
fprintf(stderr, "[%s]|SENT\n", cmd_txt[cmd_get()]);
|
||||
eldbus_service_signal_send(empd_iface, msg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
if (res == MPD_PARSER_SUCCESS)
|
||||
cmd_remove();
|
||||
txt = mpd_async_recv_line(empd->async);
|
||||
fprintf(stderr, "[%s]|NEXT LINE\n", cmd_txt[cmd_get()]);
|
||||
}
|
||||
fprintf(stderr, "[%s]|FDH_DONE\n", cmd_txt[cmd_get()]);
|
||||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
con(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Add *ev)
|
||||
{
|
||||
return ECORE_CALLBACK_DONE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
del(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Del *ev)
|
||||
{
|
||||
if (!empd->async)
|
||||
ecore_main_loop_quit();
|
||||
return ECORE_CALLBACK_DONE;
|
||||
}
|
||||
|
||||
static Eina_Bool
|
||||
data(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_Con_Event_Server_Data *ev)
|
||||
{
|
||||
int fd, flags;
|
||||
char *txt;
|
||||
|
||||
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;
|
||||
empd->parser = mpd_parser_new();
|
||||
if (mpd_settings_get_password(empd->settings))
|
||||
{
|
||||
mpd_async_send_command(empd->async, "password", mpd_settings_get_password(empd->settings), NULL);
|
||||
cmd_append(EMPD_COMMAND_PASSWORD);
|
||||
fdh_update();
|
||||
}
|
||||
else
|
||||
{
|
||||
eldbus_service_signal_emit(empd_iface, EMPD_SIGNAL_CONNECTED);
|
||||
empd->pinger = ecore_poller_add(ECORE_POLLER_CORE, 8, 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) \
|
||||
{ \
|
||||
mpd_async_send_command(empd->async, S(name), NULL); \
|
||||
fdh_update(); \
|
||||
cmd_append(EMPD_COMMAND_##CMD); \
|
||||
return eldbus_message_method_return_new(msg); \
|
||||
}
|
||||
|
||||
DBUS_BASIC_CB(status, STATUS)
|
||||
DBUS_BASIC_CB(currentsong, CURRENT_SONG)
|
||||
DBUS_BASIC_CB(play, PLAY)
|
||||
|
||||
static Eldbus_Message *
|
||||
_dbus_pause_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
||||
{
|
||||
Eina_Bool mode;
|
||||
const char *sig;
|
||||
|
||||
sig = eldbus_message_signature_get(msg);
|
||||
if (sig && (!strcmp(sig, "b")) && eldbus_message_arguments_get(msg, "b", &mode))
|
||||
{
|
||||
mpd_async_send_command(empd->async, "pause", mode ? "1" : "0", NULL);
|
||||
cmd_append(EMPD_COMMAND_PAUSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
mpd_async_send_command(empd->async, "pause", NULL);
|
||||
cmd_append(EMPD_COMMAND_TOGGLE_PAUSE);
|
||||
}
|
||||
fdh_update();
|
||||
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 (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);
|
||||
mpd_async_send_command(empd->async, "seekid", id, pos, NULL);
|
||||
cmd_append(EMPD_COMMAND_SEEK);
|
||||
fdh_update();
|
||||
}
|
||||
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 (eldbus_message_arguments_get(msg, "u", &songid))
|
||||
{
|
||||
char id[256];
|
||||
|
||||
snprintf(id, sizeof(id), "%u", songid);
|
||||
mpd_async_send_command(empd->async, "playid", id, NULL);
|
||||
cmd_append(EMPD_COMMAND_PLAY_ID);
|
||||
fdh_update();
|
||||
}
|
||||
return eldbus_message_method_return_new(msg);
|
||||
}
|
||||
|
||||
static Eldbus_Message *
|
||||
_dbus_queue_list_cb(const Eldbus_Service_Interface *iface EINA_UNUSED, const Eldbus_Message *msg)
|
||||
{
|
||||
int start, end;
|
||||
const char *sig;
|
||||
|
||||
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 < start))
|
||||
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);
|
||||
mpd_async_send_command(empd->async, "playlistinfo", s, NULL);
|
||||
}
|
||||
else
|
||||
mpd_async_send_command(empd->async, "playlistinfo", NULL);
|
||||
cmd_append(EMPD_COMMAND_QUEUE_LIST);
|
||||
fdh_update();
|
||||
return eldbus_message_method_return_new(msg);
|
||||
}
|
||||
|
||||
|
||||
static const Eldbus_Method empd_methods[] =
|
||||
{
|
||||
{ "Status", NULL, NULL, _dbus_status_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},
|
||||
{ "QueueList", NULL, NULL, _dbus_queue_list_cb, 0},
|
||||
{ "QueueListRange", ELDBUS_ARGS({"i", "start"}, {"i", "num"}), NULL, _dbus_queue_list_cb, 0},
|
||||
{NULL, NULL, NULL, NULL, 0}
|
||||
};
|
||||
|
||||
static const Eldbus_Service_Interface_Desc base_desc =
|
||||
{
|
||||
EMPD_METHOD_BASE, empd_methods, empd_signals, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const char *h;
|
||||
struct mpd_settings *settings;
|
||||
|
||||
if (argc < 2)
|
||||
{
|
||||
int port = DEFAULT_PORT;
|
||||
char *p;
|
||||
|
||||
h = getenv("MPD_HOST");
|
||||
if (!h) return 1;
|
||||
p = strchr(h, ':');
|
||||
if (p)
|
||||
{
|
||||
char host[1024] = {0};
|
||||
port = strtol(p + 1, NULL, 10);
|
||||
memcpy(host, h, p - h);
|
||||
settings = mpd_settings_new(host, port, 3000, NULL, NULL);
|
||||
}
|
||||
else
|
||||
settings = mpd_settings_new(h, port, 3000, NULL, NULL);
|
||||
}
|
||||
else
|
||||
settings = mpd_settings_new(argv[1], atoi(argv[2]), 3000, NULL, NULL);
|
||||
|
||||
if (!settings) return 1;
|
||||
empd = calloc(1, sizeof(EMPD));
|
||||
empd->settings = settings;
|
||||
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, EMPD_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_ADD, con, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_SERVER_DEL, del, NULL);
|
||||
E_LIST_HANDLER_APPEND(handlers, ECORE_CON_EVENT_SERVER_DATA, data, NULL);
|
||||
|
||||
h = mpd_settings_get_host(settings);
|
||||
empd->svr = ecore_con_server_connect(conn_type_get(h), h, (int)mpd_settings_get_port(settings), empd);
|
||||
ecore_main_loop_begin();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue