[mpd] idle command
This commit is contained in:
parent
bb01616315
commit
600e48842f
|
@ -119,7 +119,8 @@ forked_daapd_SOURCES = main.c \
|
||||||
scan-wma.c \
|
scan-wma.c \
|
||||||
$(SPOTIFY_SRC) $(LASTFM_SRC) \
|
$(SPOTIFY_SRC) $(LASTFM_SRC) \
|
||||||
$(MPD_SRC) \
|
$(MPD_SRC) \
|
||||||
$(FLAC_SRC) $(MUSEPACK_SRC)
|
$(FLAC_SRC) $(MUSEPACK_SRC) \
|
||||||
|
listener.c listener.h
|
||||||
|
|
||||||
nodist_forked_daapd_SOURCES = \
|
nodist_forked_daapd_SOURCES = \
|
||||||
$(ANTLR_SOURCES)
|
$(ANTLR_SOURCES)
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Christian Meffert <christian.meffert@googlemail.com>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "logger.h"
|
||||||
|
#include "listener.h"
|
||||||
|
|
||||||
|
struct listener
|
||||||
|
{
|
||||||
|
notify notify_cb;
|
||||||
|
struct listener *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct listener *listener_list = NULL;
|
||||||
|
|
||||||
|
int
|
||||||
|
listener_add(notify notify_cb)
|
||||||
|
{
|
||||||
|
struct listener *listener;
|
||||||
|
|
||||||
|
listener = (struct listener*)malloc(sizeof(struct listener));
|
||||||
|
listener->notify_cb = notify_cb;
|
||||||
|
listener->next = listener_list;
|
||||||
|
listener_list = listener;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
listener_notify(enum listener_event_type type)
|
||||||
|
{
|
||||||
|
struct listener *listener;
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Notify event type %d\n", type);
|
||||||
|
|
||||||
|
listener = listener_list;
|
||||||
|
while (listener)
|
||||||
|
{
|
||||||
|
listener->notify_cb(type);
|
||||||
|
listener = listener->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
#ifndef __LISTENER_H__
|
||||||
|
#define __LISTENER_H__
|
||||||
|
|
||||||
|
enum listener_event_type
|
||||||
|
{
|
||||||
|
LISTENER_NONE = 0,
|
||||||
|
LISTENER_DATABASE = 1,
|
||||||
|
LISTENER_PLAYER = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef void (*notify)(enum listener_event_type type);
|
||||||
|
|
||||||
|
int
|
||||||
|
listener_add(notify notify_cb);
|
||||||
|
|
||||||
|
int
|
||||||
|
listener_notify(enum listener_event_type type);
|
||||||
|
|
||||||
|
#endif /* !__LISTENER_H__ */
|
303
src/mpd.c
303
src/mpd.c
|
@ -47,6 +47,7 @@
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "conffile.h"
|
#include "conffile.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "listener.h"
|
||||||
#include "mpd.h"
|
#include "mpd.h"
|
||||||
|
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
@ -59,6 +60,26 @@ struct event_base *evbase_mpd;
|
||||||
static int g_exit_pipe[2];
|
static int g_exit_pipe[2];
|
||||||
static struct event *g_exitev;
|
static struct event *g_exitev;
|
||||||
|
|
||||||
|
static int g_cmd_pipe[2];
|
||||||
|
static struct event *g_cmdev;
|
||||||
|
|
||||||
|
struct mpd_command;
|
||||||
|
|
||||||
|
typedef int (*cmd_func)(struct mpd_command *cmd);
|
||||||
|
|
||||||
|
struct mpd_command
|
||||||
|
{
|
||||||
|
pthread_mutex_t lck;
|
||||||
|
pthread_cond_t cond;
|
||||||
|
|
||||||
|
cmd_func func;
|
||||||
|
|
||||||
|
enum listener_event_type arg_evtype;
|
||||||
|
int nonblock;
|
||||||
|
|
||||||
|
int ret;
|
||||||
|
};
|
||||||
|
|
||||||
#define COMMAND_ARGV_MAX 37
|
#define COMMAND_ARGV_MAX 37
|
||||||
|
|
||||||
/* MPD error codes (taken from ack.h) */
|
/* MPD error codes (taken from ack.h) */
|
||||||
|
@ -121,6 +142,52 @@ free_outputs(struct output *outputs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct idle_client
|
||||||
|
{
|
||||||
|
struct evbuffer *evbuffer;
|
||||||
|
enum listener_event_type *types;
|
||||||
|
unsigned int types_len;
|
||||||
|
|
||||||
|
struct idle_client *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct idle_client *idle_clients;
|
||||||
|
|
||||||
|
/* ---------------------------- COMMAND EXECUTION -------------------------- */
|
||||||
|
|
||||||
|
static int
|
||||||
|
send_command(struct mpd_command *cmd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!cmd->func)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "BUG: cmd->func is NULL!\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = write(g_cmd_pipe[1], &cmd, sizeof(cmd));
|
||||||
|
if (ret != sizeof(cmd))
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Could not send command: %s\n", strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nonblock_command(struct mpd_command *cmd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = send_command(cmd);
|
||||||
|
if (ret < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
thread_exit(void)
|
thread_exit(void)
|
||||||
{
|
{
|
||||||
|
@ -554,13 +621,83 @@ mpd_command_currentsong(struct evbuffer *evbuf, int argc, char **argv, char **er
|
||||||
static int
|
static int
|
||||||
mpd_command_idle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
mpd_command_idle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||||
{
|
{
|
||||||
DPRINTF(E_WARN, L_MPD, "Idle command is not supported by forked-daapd, there will be no notifications about changes\n");
|
struct idle_client *client;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
client = (struct idle_client*)malloc(sizeof(struct idle_client));
|
||||||
|
if (!client)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Out of memory for idle_client\n");
|
||||||
|
return ACK_ERROR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
client->evbuffer = evbuf;
|
||||||
|
client->types = NULL;
|
||||||
|
client->types_len = 0;
|
||||||
|
client->next = idle_clients;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
{
|
||||||
|
client->types_len = argc - 1;
|
||||||
|
client->types = (enum listener_event_type*)malloc(client->types_len * sizeof(enum listener_event_type));
|
||||||
|
|
||||||
|
if (!client->types)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Out of memory for types\n");
|
||||||
|
return ACK_ERROR_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i < argc; i++)
|
||||||
|
{
|
||||||
|
if (0 == strcmp(argv[i], "player"))
|
||||||
|
{
|
||||||
|
client->types[i - 1] = LISTENER_PLAYER;
|
||||||
|
}
|
||||||
|
else if (0 == strcmp(argv[i], "database"))
|
||||||
|
{
|
||||||
|
client->types[i - 1] = LISTENER_DATABASE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Idle command for '%s' not supported\n", argv[i]);
|
||||||
|
client->types[i - 1] = LISTENER_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idle_clients = client;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mpd_command_noidle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
mpd_command_noidle(struct evbuffer *evbuf, int argc, char **argv, char **errmsg)
|
||||||
{
|
{
|
||||||
|
struct idle_client *client;
|
||||||
|
struct idle_client *prev;
|
||||||
|
|
||||||
|
client = idle_clients;
|
||||||
|
prev = NULL;
|
||||||
|
|
||||||
|
while (client)
|
||||||
|
{
|
||||||
|
if (client->evbuffer == evbuf)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Found idle client for noidle command\n");
|
||||||
|
|
||||||
|
if (prev)
|
||||||
|
prev->next = client->next;
|
||||||
|
else
|
||||||
|
idle_clients = client->next;
|
||||||
|
|
||||||
|
free(client);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
prev = client;
|
||||||
|
client = client->next;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3648,6 +3785,134 @@ mpd_accept_error_cb(struct evconnlistener *listener, void *ctx)
|
||||||
DPRINTF(E_LOG, L_MPD, "Error occured %d (%s) on the listener.\n", err, evutil_socket_error_to_string(err));
|
DPRINTF(E_LOG, L_MPD, "Error occured %d (%s) on the listener.\n", err, evutil_socket_error_to_string(err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mpd_notify_idle_client(struct idle_client *client, enum listener_event_type type)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int has_type;
|
||||||
|
|
||||||
|
if (client->types_len == 0)
|
||||||
|
has_type = 1;
|
||||||
|
else
|
||||||
|
has_type = 0;
|
||||||
|
|
||||||
|
for (i = 0; !has_type && (i < client->types_len); i++)
|
||||||
|
{
|
||||||
|
has_type = (client->types[i] == type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_type)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Client not listening for event: %d\n", type);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
evbuffer_add(client->evbuffer, "changed: player\n", 16);
|
||||||
|
evbuffer_add(client->evbuffer, "OK\n", 3);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
mpd_notify_idle(struct mpd_command *cmd)
|
||||||
|
{
|
||||||
|
struct idle_client *client;
|
||||||
|
struct idle_client *prev;
|
||||||
|
struct idle_client *next;
|
||||||
|
int i;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Notify clients waiting for idle results: %d\n", cmd->arg_evtype);
|
||||||
|
|
||||||
|
prev = NULL;
|
||||||
|
next = NULL;
|
||||||
|
i = 0;
|
||||||
|
client = idle_clients;
|
||||||
|
while (client)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Notify client #%d\n", i);
|
||||||
|
|
||||||
|
next = client->next;
|
||||||
|
|
||||||
|
ret = mpd_notify_idle_client(client, cmd->arg_evtype);
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
{
|
||||||
|
if (prev)
|
||||||
|
prev->next = next;
|
||||||
|
else
|
||||||
|
idle_clients = next;
|
||||||
|
|
||||||
|
free(client);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev = client;
|
||||||
|
}
|
||||||
|
|
||||||
|
client = next;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mpd_listener_cb(enum listener_event_type type)
|
||||||
|
{
|
||||||
|
DPRINTF(E_DBG, L_MPD, "Listener callback called with event type %d.\n", type);
|
||||||
|
struct mpd_command *cmd;
|
||||||
|
|
||||||
|
cmd = (struct mpd_command *)malloc(sizeof(struct mpd_command));
|
||||||
|
if (!cmd)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Could not allocate cache_command\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(cmd, 0, sizeof(struct mpd_command));
|
||||||
|
|
||||||
|
cmd->nonblock = 1;
|
||||||
|
|
||||||
|
cmd->func = mpd_notify_idle;
|
||||||
|
cmd->arg_evtype = type;
|
||||||
|
|
||||||
|
nonblock_command(cmd);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
command_cb(int fd, short what, void *arg)
|
||||||
|
{
|
||||||
|
struct mpd_command *cmd;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = read(g_cmd_pipe[0], &cmd, sizeof(cmd));
|
||||||
|
if (ret != sizeof(cmd))
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Could not read command! (read %d): %s\n", ret, (ret < 0) ? strerror(errno) : "-no error-");
|
||||||
|
goto readd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cmd->nonblock)
|
||||||
|
{
|
||||||
|
cmd->func(cmd);
|
||||||
|
|
||||||
|
free(cmd);
|
||||||
|
goto readd;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_lock(&cmd->lck);
|
||||||
|
|
||||||
|
ret = cmd->func(cmd);
|
||||||
|
cmd->ret = ret;
|
||||||
|
|
||||||
|
pthread_cond_signal(&cmd->cond);
|
||||||
|
pthread_mutex_unlock(&cmd->lck);
|
||||||
|
|
||||||
|
readd:
|
||||||
|
event_add(g_cmdev, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Thread: main */
|
/* Thread: main */
|
||||||
int mpd_init(void)
|
int mpd_init(void)
|
||||||
|
@ -3682,6 +3947,17 @@ int mpd_init(void)
|
||||||
goto exit_fail;
|
goto exit_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# if defined(__linux__)
|
||||||
|
ret = pipe2(g_cmd_pipe, O_CLOEXEC);
|
||||||
|
# else
|
||||||
|
ret = pipe(g_cmd_pipe);
|
||||||
|
# endif
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Could not create command pipe: %s\n", strerror(errno));
|
||||||
|
goto cmd_fail;
|
||||||
|
}
|
||||||
|
|
||||||
evbase_mpd = event_base_new();
|
evbase_mpd = event_base_new();
|
||||||
if (!evbase_mpd)
|
if (!evbase_mpd)
|
||||||
{
|
{
|
||||||
|
@ -3698,6 +3974,16 @@ int mpd_init(void)
|
||||||
|
|
||||||
event_add(g_exitev, NULL);
|
event_add(g_exitev, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
g_cmdev = event_new(evbase_mpd, g_cmd_pipe[0], EV_READ, command_cb, NULL);
|
||||||
|
if (!g_cmdev)
|
||||||
|
{
|
||||||
|
DPRINTF(E_LOG, L_MPD, "Could not create cmd event\n");
|
||||||
|
goto evnew_fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
event_add(g_cmdev, NULL);
|
||||||
|
|
||||||
if (v6enabled)
|
if (v6enabled)
|
||||||
{
|
{
|
||||||
saddr_length = sizeof(sin6);
|
saddr_length = sizeof(sin6);
|
||||||
|
@ -3743,6 +4029,9 @@ int mpd_init(void)
|
||||||
goto thread_fail;
|
goto thread_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idle_clients = NULL;
|
||||||
|
listener_add(mpd_listener_cb);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
|
@ -3753,6 +4042,10 @@ int mpd_init(void)
|
||||||
evbase_mpd = NULL;
|
evbase_mpd = NULL;
|
||||||
|
|
||||||
evbase_fail:
|
evbase_fail:
|
||||||
|
close(g_cmd_pipe[0]);
|
||||||
|
close(g_cmd_pipe[1]);
|
||||||
|
|
||||||
|
cmd_fail:
|
||||||
close(g_exit_pipe[0]);
|
close(g_exit_pipe[0]);
|
||||||
close(g_exit_pipe[1]);
|
close(g_exit_pipe[1]);
|
||||||
|
|
||||||
|
@ -3763,6 +4056,7 @@ int mpd_init(void)
|
||||||
/* Thread: main */
|
/* Thread: main */
|
||||||
void mpd_deinit(void)
|
void mpd_deinit(void)
|
||||||
{
|
{
|
||||||
|
struct idle_client *temp;
|
||||||
unsigned short port;
|
unsigned short port;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
@ -3782,6 +4076,13 @@ void mpd_deinit(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (idle_clients)
|
||||||
|
{
|
||||||
|
temp = idle_clients;
|
||||||
|
idle_clients = idle_clients->next;
|
||||||
|
free(temp);
|
||||||
|
}
|
||||||
|
|
||||||
// Free event base (should free events too)
|
// Free event base (should free events too)
|
||||||
event_base_free(evbase_mpd);
|
event_base_free(evbase_mpd);
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
#include "raop.h"
|
#include "raop.h"
|
||||||
#include "laudio.h"
|
#include "laudio.h"
|
||||||
#include "worker.h"
|
#include "worker.h"
|
||||||
|
#include "listener.h"
|
||||||
|
|
||||||
#ifdef LASTFM
|
#ifdef LASTFM
|
||||||
# include "lastfm.h"
|
# include "lastfm.h"
|
||||||
|
@ -302,6 +303,8 @@ status_update(enum play_status status)
|
||||||
if (update_handler)
|
if (update_handler)
|
||||||
update_handler();
|
update_handler();
|
||||||
|
|
||||||
|
listener_notify(LISTENER_PLAYER);
|
||||||
|
|
||||||
if (status == PLAY_PLAYING)
|
if (status == PLAY_PLAYING)
|
||||||
dev_autoselect = 0;
|
dev_autoselect = 0;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue