Mercurial > audlegacy
diff src/audacious/controlsocket.c @ 2313:3149d4b1a9a9 trunk
[svn] - objective-make autodepend fixes
- move all sourcecode into src/ and adjust Makefiles accordingly
| author | nenolod |
|---|---|
| date | Fri, 12 Jan 2007 11:43:40 -0800 |
| parents | |
| children | f24ae4f40e29 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/audacious/controlsocket.c Fri Jan 12 11:43:40 2007 -0800 @@ -0,0 +1,792 @@ +/* Audacious + * Copyright (C) 2005-2007 Audacious development team. + * + * Based on BMP: + * Copyright (C) 2003-2004 BMP development team. + * + * Based on XMMS: + * Copyright (C) 1998-2003 XMMS development team. + * + * 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; under version 2 of the License. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "controlsocket.h" + +#include <glib.h> +#include <stdlib.h> +#include <string.h> + +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <netinet/in.h> +#include <unistd.h> +#include <grp.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <sys/resource.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <arpa/inet.h> + +#include "main.h" +#include "ui_equalizer.h" +#include "ui_main.h" +#include "input.h" +#include "playback.h" +#include "playlist.h" +#include "ui_playlist.h" +#include "ui_preferences.h" +#include "libaudacious/beepctrl.h" +#include "memorypool.h" + +#define CTRLSOCKET_BACKLOG 100 +#define CTRLSOCKET_TIMEOUT 100000 + + +static gint session_id = 0; + +static gint ctrl_fd = 0; +static gchar *socket_name = NULL; + +static gpointer ctrlsocket_func(gpointer); +static GThread *ctrlsocket_thread; + +static GList *packet_list = NULL; +static GMutex *packet_list_mutex = NULL; + +static gboolean started = FALSE; +static gboolean going = TRUE; +static GCond *start_cond = NULL; +static GMutex *status_mutex = NULL; +static MemoryPool *cs_pool = NULL; + +static void +ctrlsocket_start_thread(void) +{ + start_cond = g_cond_new(); + status_mutex = g_mutex_new(); + packet_list_mutex = g_mutex_new(); + + ctrlsocket_thread = g_thread_create(ctrlsocket_func, NULL, TRUE, NULL); +} + +gboolean +ctrlsocket_setup(void) +{ + cs_pool = memory_pool_new(); + + if (strcmp(cfg.session_uri_base, "")) + audacious_set_session_uri(cfg.session_uri_base); + else + return ctrlsocket_setup_unix(); + + if (!g_strncasecmp(cfg.session_uri_base, "tcp://", 6)) + return ctrlsocket_setup_tcp(); + + return ctrlsocket_setup_unix(); +} + +gboolean +ctrlsocket_setup_unix(void) +{ + struct sockaddr_un saddr; + gint i; + gint fd; + + audacious_set_session_type(AUDACIOUS_TYPE_UNIX); + + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + g_critical("ctrlsocket_setup(): Failed to open socket: %s", + strerror(errno)); + return FALSE; + } + + for (i = 0;; i++) { + saddr.sun_family = AF_UNIX; + g_snprintf(saddr.sun_path, sizeof(saddr.sun_path), + "%s/%s_%s.%d", g_get_tmp_dir(), + CTRLSOCKET_NAME, g_get_user_name(), i); + + if (xmms_remote_is_running(i)) { + if (cfg.allow_multiple_instances) + continue; + break; + } + + if ((unlink(saddr.sun_path) == -1) && errno != ENOENT) { + g_critical + ("ctrlsocket_setup(): Failed to unlink %s (Error: %s)", + saddr.sun_path, strerror(errno)); + break; + } + + if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) { + g_critical + ("ctrlsocket_setup(): Failed to assign %s to a socket (Error: %s)", + saddr.sun_path, strerror(errno)); + break; + } + + listen(fd, CTRLSOCKET_BACKLOG); + + socket_name = memory_pool_strdup(cs_pool, saddr.sun_path); + ctrl_fd = fd; + session_id = i; + going = TRUE; + + ctrlsocket_start_thread(); + + return TRUE; + } + + close(fd); + + return FALSE; +} + +gboolean +ctrlsocket_setup_tcp(void) +{ + struct sockaddr_in saddr; + gint i; + gint fd; + + audacious_set_session_type(AUDACIOUS_TYPE_TCP); + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + g_critical("ctrlsocket_setup(): Failed to open socket: %s", + strerror(errno)); + return FALSE; + } + + for (i = 0;; i++) { + memset(&saddr, '\0', sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = htons(37370 + i); + + if (xmms_remote_is_running(i)) { + if (cfg.allow_multiple_instances) + continue; + break; + } + + if (bind(fd, (struct sockaddr *) &saddr, sizeof(saddr)) == -1) { + g_critical + ("ctrlsocket_setup(): Failed to bind the socket (Error: %s)", + strerror(errno)); + break; + } + + listen(fd, CTRLSOCKET_BACKLOG); + + ctrl_fd = fd; + session_id = i; + going = TRUE; + + ctrlsocket_start_thread(); + + return TRUE; + } + + close(fd); + + return FALSE; +} + +gint +ctrlsocket_get_session_id(void) +{ + return session_id; +} + +void +ctrlsocket_cleanup(void) +{ + if (ctrl_fd) + { + g_mutex_lock(status_mutex); + going = FALSE; + g_cond_signal(start_cond); + g_mutex_unlock(status_mutex); + + /* wait for ctrlsocket_thread to terminate */ + g_thread_join(ctrlsocket_thread); + + /* close and remove socket */ + close(ctrl_fd); + ctrl_fd = 0; + + if (socket_name != NULL) + { + unlink(socket_name); + memory_pool_release(cs_pool, socket_name); + } + + g_cond_free(start_cond); + g_mutex_free(status_mutex); + g_mutex_free(packet_list_mutex); + } + + memory_pool_destroy(cs_pool); +} + +void +ctrlsocket_start(void) +{ + /* tell control socket thread to go 'live' i.e. start handling + * packets */ + g_mutex_lock(status_mutex); + started = TRUE; + g_cond_signal(start_cond); + g_mutex_unlock(status_mutex); +} + +static void +ctrl_write_packet(gint fd, gpointer data, gint length) +{ + ServerPktHeader pkthdr; + + memset(&pkthdr, '\0', sizeof(ServerPktHeader)); + + pkthdr.version = XMMS_PROTOCOL_VERSION; + pkthdr.data_length = length; + if ((size_t)write(fd, &pkthdr, sizeof(ServerPktHeader)) < sizeof(pkthdr)) + return; + if (data && length > 0) + write(fd, data, length); +} + +static void +ctrl_write_gint(gint fd, gint val) +{ + ctrl_write_packet(fd, &val, sizeof(gint)); +} + +static void +ctrl_write_gfloat(gint fd, gfloat val) +{ + ctrl_write_packet(fd, &val, sizeof(gfloat)); +} + +static void +ctrl_write_gboolean(gint fd, gboolean bool) +{ + ctrl_write_packet(fd, &bool, sizeof(gboolean)); +} + +static void +ctrl_write_string(gint fd, gchar * string) +{ + ctrl_write_packet(fd, string, string ? strlen(string) + 1 : 0); +} + +static void +ctrl_ack_packet(PacketNode * pkt) +{ + ctrl_write_packet(pkt->fd, NULL, 0); + close(pkt->fd); + if (pkt->data) + memory_pool_release(cs_pool, pkt->data); + memory_pool_release(cs_pool, pkt); +} + +static gboolean +ctrlsocket_is_going(void) +{ + gboolean result; + + g_mutex_lock(status_mutex); + result = going; + g_mutex_unlock(status_mutex); + + return result; +} + +static gpointer +ctrlsocket_func(gpointer arg) +{ + fd_set set; + struct timeval tv; + struct sockaddr_un saddr; + gint fd, b, i; + gint info[3]; + gint32 v[2]; + PacketNode *pkt; + socklen_t len; + gfloat fval[11]; + + g_mutex_lock(status_mutex); + while (!started && going) + g_cond_wait(start_cond, status_mutex); + g_mutex_unlock(status_mutex); + + while (ctrlsocket_is_going()) { + FD_ZERO(&set); + FD_SET(ctrl_fd, &set); + tv.tv_sec = 0; + tv.tv_usec = CTRLSOCKET_TIMEOUT; + len = sizeof(saddr); + if (select(ctrl_fd + 1, &set, NULL, NULL, &tv) <= 0) + continue; + if ((fd = accept(ctrl_fd, (struct sockaddr *) &saddr, &len)) == -1) + continue; + + pkt = memory_pool_alloc_object(cs_pool, PacketNode); + if ((size_t)read(fd, &pkt->hdr, sizeof(ClientPktHeader)) + < sizeof(ClientPktHeader)) { + memory_pool_release(cs_pool, pkt); + continue; + } + + if (pkt->hdr.data_length) { + size_t data_length = pkt->hdr.data_length; + pkt->data = memory_pool_allocate(cs_pool, data_length); + if ((size_t)read(fd, pkt->data, data_length) < data_length) { + memory_pool_release(cs_pool, pkt->data); + memory_pool_release(cs_pool, pkt); + g_warning("ctrlsocket_func(): Incomplete data packet dropped"); + continue; + } + } + + pkt->fd = fd; + switch (pkt->hdr.command) { + case CMD_GET_VERSION: + ctrl_write_gint(pkt->fd, 0x09a3); + ctrl_ack_packet(pkt); + break; + case CMD_IS_PLAYING: + ctrl_write_gboolean(pkt->fd, playback_get_playing()); + ctrl_ack_packet(pkt); + break; + case CMD_IS_PAUSED: + ctrl_write_gboolean(pkt->fd, playback_get_paused()); + ctrl_ack_packet(pkt); + break; + case CMD_GET_PLAYLIST_POS: + ctrl_write_gint(pkt->fd, playlist_get_position(playlist_get_active())); + ctrl_ack_packet(pkt); + break; + case CMD_GET_PLAYLIST_LENGTH: + ctrl_write_gint(pkt->fd, playlist_get_length(playlist_get_active())); + ctrl_ack_packet(pkt); + break; + case CMD_GET_PLAYQUEUE_LENGTH: + ctrl_write_gint(pkt->fd, playlist_queue_get_length(playlist_get_active())); + ctrl_ack_packet(pkt); + break; + case CMD_PLAYQUEUE_IS_QUEUED: + ctrl_write_gboolean(pkt->fd, + playlist_is_position_queued(playlist_get_active(), *((guint32 *) pkt->data))); + ctrl_ack_packet(pkt); + break; + case CMD_PLAYQUEUE_GET_POS: + if (pkt->data) + ctrl_write_gint(pkt->fd, + playlist_get_queue_position_number(playlist_get_active(), + *((guint32 *) pkt->data))); + else + ctrl_write_gint(pkt->fd, 0); + + ctrl_ack_packet(pkt); + break; + case CMD_PLAYQUEUE_GET_QPOS: + if (pkt->data) + ctrl_write_gint(pkt->fd, + playlist_get_queue_position_number(playlist_get_active(), + *((guint32 *) pkt->data))); + else + ctrl_write_gint(pkt->fd, 0); + + ctrl_ack_packet(pkt); + break; + case CMD_GET_OUTPUT_TIME: + if (playback_get_playing()) + ctrl_write_gint(pkt->fd, playback_get_time()); + else + ctrl_write_gint(pkt->fd, 0); + ctrl_ack_packet(pkt); + break; + case CMD_GET_VOLUME: + input_get_volume(&v[0], &v[1]); + ctrl_write_packet(pkt->fd, v, sizeof(v)); + ctrl_ack_packet(pkt); + break; + case CMD_GET_BALANCE: + input_get_volume(&v[0], &v[1]); + if (v[0] < 0 || v[1] < 0) + b = 0; + else if (v[0] > v[1]) + b = -100 + ((v[1] * 100) / v[0]); + else if (v[1] > v[0]) + b = 100 - ((v[0] * 100) / v[1]); + else + b = 0; + ctrl_write_gint(pkt->fd, b); + ctrl_ack_packet(pkt); + break; + case CMD_GET_SKIN: + ctrl_write_string(pkt->fd, bmp_active_skin->path); + ctrl_ack_packet(pkt); + break; + case CMD_GET_PLAYLIST_FILE: + if (pkt->data) { + gchar *filename; + filename = playlist_get_filename(playlist_get_active(), *((guint32 *) pkt->data)); + ctrl_write_string(pkt->fd, filename); + g_free(filename); + } + else + ctrl_write_string(pkt->fd, NULL); + ctrl_ack_packet(pkt); + break; + case CMD_GET_PLAYLIST_TITLE: + if (pkt->data) { + gchar *title; + title = playlist_get_songtitle(playlist_get_active(), *((guint32 *) pkt->data)); + ctrl_write_string(pkt->fd, title); + g_free(title); + } + else + ctrl_write_string(pkt->fd, NULL); + ctrl_ack_packet(pkt); + break; + case CMD_GET_PLAYLIST_TIME: + if (pkt->data) + ctrl_write_gint(pkt->fd, + playlist_get_songtime(playlist_get_active(), + *((guint32 *) pkt->data))); + else + ctrl_write_gint(pkt->fd, -1); + + ctrl_ack_packet(pkt); + break; + case CMD_GET_INFO: + playback_get_sample_params(&info[0], &info[1], &info[2]); + ctrl_write_packet(pkt->fd, info, 3 * sizeof(gint)); + ctrl_ack_packet(pkt); + break; + case CMD_GET_EQ_DATA: + case CMD_SET_EQ_DATA: + /* obsolete */ + ctrl_ack_packet(pkt); + break; + case CMD_PING: + ctrl_ack_packet(pkt); + break; + case CMD_PLAYLIST_ADD: + if (pkt->data) { + guint32 *dataptr = pkt->data; + while ((len = *dataptr) > 0) { + gchar *filename; + + dataptr++; + filename = memory_pool_allocate(cs_pool, len); + memcpy(filename, dataptr, len); + + GDK_THREADS_ENTER(); + playlist_add_url(playlist_get_active(), filename); + GDK_THREADS_LEAVE(); + + memory_pool_release(cs_pool, filename); + dataptr += (len + 3) / 4; + } + } + ctrl_ack_packet(pkt); + break; + case CMD_PLAYLIST_ENQUEUE_TO_TEMP: + { + Playlist *new_pl = playlist_new(); + + GDK_THREADS_ENTER(); + playlist_select_playlist(new_pl); + playlist_add_url(new_pl, pkt->data); + GDK_THREADS_LEAVE(); + + ctrl_ack_packet(pkt); + } + break; + case CMD_PLAYLIST_ADD_URL_STRING: + GDK_THREADS_ENTER(); + playlist_add_url(playlist_get_active(), pkt->data); + GDK_THREADS_LEAVE(); + + ctrl_ack_packet(pkt); + break; + case CMD_PLAYLIST_INS_URL_STRING: + if (pkt->data) { + gint pos = *(gint *) pkt->data; + gchar *ptr = pkt->data; + ptr += sizeof(gint); + playlist_ins_url(playlist_get_active(), ptr, pos); + } + ctrl_ack_packet(pkt); + break; + case CMD_PLAYLIST_DELETE: + GDK_THREADS_ENTER(); + playlist_delete_index(playlist_get_active(), *((guint32 *) pkt->data)); + GDK_THREADS_LEAVE(); + ctrl_ack_packet(pkt); + break; + case CMD_PLAYLIST_CLEAR: + GDK_THREADS_ENTER(); + playlist_clear(playlist_get_active()); + mainwin_clear_song_info(); + mainwin_set_info_text(); + GDK_THREADS_LEAVE(); + ctrl_ack_packet(pkt); + break; + case CMD_IS_MAIN_WIN: + ctrl_write_gboolean(pkt->fd, cfg.player_visible); + ctrl_ack_packet(pkt); + break; + case CMD_IS_PL_WIN: + ctrl_write_gboolean(pkt->fd, cfg.playlist_visible); + ctrl_ack_packet(pkt); + break; + case CMD_IS_EQ_WIN: + ctrl_write_gboolean(pkt->fd, cfg.equalizer_visible); + ctrl_ack_packet(pkt); + break; + case CMD_IS_REPEAT: + ctrl_write_gboolean(pkt->fd, cfg.repeat); + ctrl_ack_packet(pkt); + break; + case CMD_IS_SHUFFLE: + ctrl_write_gboolean(pkt->fd, cfg.shuffle); + ctrl_ack_packet(pkt); + break; + case CMD_IS_ADVANCE: + ctrl_write_gboolean(pkt->fd, !cfg.no_playlist_advance); + ctrl_ack_packet(pkt); + break; + case CMD_GET_EQ: + fval[0] = equalizerwin_get_preamp(); + for (i = 0; i < 10; i++) + fval[i + 1] = equalizerwin_get_band(i); + ctrl_write_packet(pkt->fd, fval, 11 * sizeof(gfloat)); + ctrl_ack_packet(pkt); + break; + case CMD_GET_EQ_PREAMP: + ctrl_write_gfloat(pkt->fd, equalizerwin_get_preamp()); + ctrl_ack_packet(pkt); + break; + case CMD_GET_EQ_BAND: + i = *((guint32 *) pkt->data); + ctrl_write_gfloat(pkt->fd, equalizerwin_get_band(i)); + ctrl_ack_packet(pkt); + break; + default: + g_mutex_lock(packet_list_mutex); + packet_list = g_list_append(packet_list, pkt); + ctrl_write_packet(pkt->fd, NULL, 0); + close(pkt->fd); + g_mutex_unlock(packet_list_mutex); + break; + } + } + g_thread_exit(NULL); + + /* Used to suppress GCC warnings. Sometimes you'd wish C has + native threading support :p */ + return NULL; +} + +void +ctrlsocket_check(void) +{ + GList *pkt_list, *next; + PacketNode *pkt; + gpointer data; + guint32 v[2], i, num; + gboolean tbool; + gfloat *fval, f; + + g_mutex_lock(packet_list_mutex); + for (pkt_list = packet_list; pkt_list; pkt_list = next) { + pkt = pkt_list->data; + data = pkt->data; + + switch (pkt->hdr.command) { + case CMD_PLAY: + if (playback_get_paused()) + playback_pause(); + else if (playlist_get_length(playlist_get_active())) + playback_initiate(); + else + mainwin_eject_pushed(); + break; + case CMD_PAUSE: + playback_pause(); + break; + case CMD_STOP: + ip_data.stop = TRUE; + playback_stop(); + ip_data.stop = FALSE; + mainwin_clear_song_info(); + break; + case CMD_PLAY_PAUSE: + if (playback_get_playing()) + playback_pause(); + else + playback_initiate(); + break; + case CMD_PLAYQUEUE_ADD: + num = *((guint32 *) data); + if (num < (guint)playlist_get_length(playlist_get_active())) + playlist_queue_position(playlist_get_active(), num); + break; + case CMD_PLAYQUEUE_REMOVE: + num = *((guint32 *) data); + if (num < (guint)playlist_get_length(playlist_get_active())) + playlist_queue_remove(playlist_get_active(), num); + break; + case CMD_PLAYQUEUE_CLEAR: + playlist_clear_queue(playlist_get_active()); + break; + case CMD_SET_PLAYLIST_POS: + num = *((guint32 *) data); + if (num < (guint)playlist_get_length(playlist_get_active())) + playlist_set_position(playlist_get_active(), num); + break; + case CMD_JUMP_TO_TIME: + num = *((guint32 *) data); + if (playlist_get_current_length(playlist_get_active()) > 0 && + num < (guint)playlist_get_current_length(playlist_get_active())) + playback_seek(num / 1000); + break; + case CMD_SET_VOLUME: + v[0] = ((guint32 *) data)[0]; + v[1] = ((guint32 *) data)[1]; + for (i = 0; i < 2; i++) { + if (v[i] > 100) + v[i] = 100; + } + input_set_volume(v[0], v[1]); + break; + case CMD_SET_SKIN: + if (has_x11_connection == TRUE) + bmp_active_skin_load(data); + break; + case CMD_PL_WIN_TOGGLE: + if (has_x11_connection != TRUE) + break; + tbool = *((gboolean *) data); + if (tbool) + playlistwin_show(); + else + playlistwin_hide(); + break; + case CMD_EQ_WIN_TOGGLE: + if (has_x11_connection != TRUE) + break; + tbool = *((gboolean *) data); + equalizerwin_show(!!tbool); + break; + case CMD_SHOW_PREFS_BOX: + if (has_x11_connection != TRUE) + break; + show_prefs_window(); + break; + case CMD_SHOW_JTF_BOX: + if (has_x11_connection != TRUE) + break; + mainwin_jump_to_file(); + break; + case CMD_TOGGLE_AOT: + if (has_x11_connection != TRUE) + break; + tbool = *((gboolean *) data); + mainwin_set_always_on_top(tbool); + break; + case CMD_SHOW_ABOUT_BOX: + break; + case CMD_EJECT: + if (has_x11_connection != TRUE) + break; + mainwin_eject_pushed(); + break; + case CMD_PLAYLIST_PREV: + playlist_prev(playlist_get_active()); + break; + case CMD_PLAYLIST_NEXT: + playlist_next(playlist_get_active()); + break; + case CMD_TOGGLE_REPEAT: + mainwin_repeat_pushed(!cfg.repeat); + break; + case CMD_TOGGLE_SHUFFLE: + mainwin_shuffle_pushed(!cfg.shuffle); + break; + case CMD_TOGGLE_ADVANCE: + /* FIXME: to be implemented */ + break; + case CMD_MAIN_WIN_TOGGLE: + if (has_x11_connection != TRUE) + break; + tbool = *((gboolean *) data); + mainwin_show(!!tbool); + break; + case CMD_SET_EQ: + if (pkt->hdr.data_length >= 11 * sizeof(gfloat)) { + fval = (gfloat *) data; + equalizerwin_set_preamp(fval[0]); + for (i = 0; i < 10; i++) + equalizerwin_set_band(i, fval[i + 1]); + } + break; + case CMD_SET_EQ_PREAMP: + f = *((gfloat *) data); + equalizerwin_set_preamp(f); + break; + case CMD_SET_EQ_BAND: + if (pkt->hdr.data_length >= sizeof(gint) + sizeof(gfloat)) { + i = *((gint *) data); + f = *((gfloat *) ((gchar *) data + sizeof(gint))); + equalizerwin_set_band(i, f); + } + break; + case CMD_QUIT: + /* + * We unlock the packet_list_mutex to + * avoid that cleanup_ctrlsocket() can + * deadlock, mainwin_quit_cb() will + * never return anyway, so this will + * work ok. + */ + g_mutex_unlock(packet_list_mutex); + mainwin_quit_cb(); + break; + case CMD_ACTIVATE: + gtk_window_present(GTK_WINDOW(mainwin)); + break; + default: + g_message("Unknown socket command received"); + break; + } + next = g_list_next(pkt_list); + packet_list = g_list_remove_link(packet_list, pkt_list); + g_list_free_1(pkt_list); + if (pkt->data) + memory_pool_release(cs_pool, pkt->data); + memory_pool_release(cs_pool, pkt); + } + g_mutex_unlock(packet_list_mutex); +}
