From a0592c8cc08b802f86061c88dc6862352b6c3e94 Mon Sep 17 00:00:00 2001 From: Kalle Wallin Date: Fri, 19 Mar 2004 13:26:18 +0000 Subject: [PATCH 1/1] Imported ncmpc (mpc-ncures). git-svn-id: https://svn.musicpd.org/ncmpc/trunk@292 09075e82-0dd4-0310-85a5-a0d7c8717e4f --- AUTHORS | 1 + ChangeLog | 12 + Makefile.am | 23 + NEWS | 3 + README | 29 ++ TODO | 15 + bootstrap.sh | 54 +++ cleanup.sh | 14 + command.c | 222 ++++++++++ command.h | 44 ++ configure.ac | 100 +++++ doc/ncmpc.1 | 46 ++ libmpdclient.c | 1076 +++++++++++++++++++++++++++++++++++++++++++++ libmpdclient.h | 369 ++++++++++++++++ m4/CVS/Entries | 2 + m4/CVS/Repository | 1 + m4/CVS/Root | 1 + m4/glib-2.0.m4 | 212 +++++++++ main.c | 122 +++++ mpc.c | 329 ++++++++++++++ mpc.h | 45 ++ options.c | 107 +++++ options.h | 16 + out | 4 + screen.c | 520 ++++++++++++++++++++++ screen.h | 71 +++ screen_file.c | 333 ++++++++++++++ screen_file.h | 14 + screen_help.c | 165 +++++++ screen_help.h | 9 + screen_play.c | 129 ++++++ screen_play.h | 10 + screen_search.c | 37 ++ screen_search.h | 8 + screen_utils.c | 152 +++++++ screen_utils.h | 42 ++ support.c | 64 +++ support.h | 9 + 38 files changed, 4410 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100644 TODO create mode 100755 bootstrap.sh create mode 100755 cleanup.sh create mode 100644 command.c create mode 100644 command.h create mode 100644 configure.ac create mode 100644 doc/ncmpc.1 create mode 100644 libmpdclient.c create mode 100644 libmpdclient.h create mode 100644 m4/CVS/Entries create mode 100644 m4/CVS/Repository create mode 100644 m4/CVS/Root create mode 100644 m4/glib-2.0.m4 create mode 100644 main.c create mode 100644 mpc.c create mode 100644 mpc.h create mode 100644 options.c create mode 100644 options.h create mode 100644 out create mode 100644 screen.c create mode 100644 screen.h create mode 100644 screen_file.c create mode 100644 screen_file.h create mode 100644 screen_help.c create mode 100644 screen_help.h create mode 100644 screen_play.c create mode 100644 screen_play.h create mode 100644 screen_search.c create mode 100644 screen_search.h create mode 100644 screen_utils.c create mode 100644 screen_utils.h create mode 100644 support.c create mode 100644 support.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..06557c0 --- /dev/null +++ b/AUTHORS @@ -0,0 +1 @@ +Kalle Wallin diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..ca3354b --- /dev/null +++ b/ChangeLog @@ -0,0 +1,12 @@ +0.1.2 * Fixed a bug introduced in version 0.1.1. The full path to files + was printed in the browse screen (changed back to basename). + +0.1.1 * Added support for utf-8 (MPD >= 0.10.0) + +0.1.0 * First published version of mpc-ncurses + + + + + + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..82cfd58 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,23 @@ +# +# $Id: Makefile.am,v 1.5 2004/03/16 23:49:14 kalle Exp $ +# + +bin_PROGRAMS = mpc-ncurses +man_MANS = doc/ncmpc.1 +pkgdata_DATA = +docdir = $(prefix)/share/doc/$(PACKAGE) +doc_DATA = AUTHORS README ChangeLog +EXTRA_DIST = COPYING $(pkgdata_DATA) $(man_MANS) $(doc_DATA) + +mpc_ncurses_headers = libmpdclient.h mpc.h options.h command.h screen.h \ + screen_utils.h screen_play.h screen_file.h screen_search.h \ + screen_help.h support.h + +mpc_ncurses_SOURCES = libmpdclient.c main.c mpc.c options.c command.c \ + screen.c screen_utils.c screen_play.c screen_file.c \ + screen_search.c screen_help.c \ + support.c $(mpc_ncurses_headers) + + + + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..a09f8d3 --- /dev/null +++ b/NEWS @@ -0,0 +1,3 @@ + + + diff --git a/README b/README new file mode 100644 index 0000000..f2deb59 --- /dev/null +++ b/README @@ -0,0 +1,29 @@ +mpc-ncurses is a ncurses client for MPD, the Music Player Daemon. +The client connects to a MPD running on a machine on the local network. +By default, mpc-ncurses connects to localhost:2100. This can be +changed either at compile-time, or by exporting the MPD_HOST and +MPD_PORT environment variables, or by the command line options --host +and --port. + + $ mpc-ncurses --host=musicserver --port=44000 + + +Note! +If your are connecting to a MPD server with a version newer than 0.9.x +and you dont use utf-8 as your charset set the CHARSET enviroment variable. + + $ export CHARSET=ISO_8859-1 + + + +Read more about MPD on http://www.musicpd.org +Read more about mpc-ncurses on http://hem.bredband.net/kaw/apps/mpc-ncurses + + + + + + + + + diff --git a/TODO b/TODO new file mode 100644 index 0000000..d55f235 --- /dev/null +++ b/TODO @@ -0,0 +1,15 @@ + +client functions +---------------------------------------------------------------------------- + * search screen + * search function in playlist (like in less '/') + * load/save playlists + +ncurses +---------------------------------------------------------------------------- + * improve list drawing (faster) + +other +---------------------------------------------------------------------------- + + diff --git a/bootstrap.sh b/bootstrap.sh new file mode 100755 index 0000000..8d47e61 --- /dev/null +++ b/bootstrap.sh @@ -0,0 +1,54 @@ +#! /bin/sh +# Check Autoconf version +if [ -x `which autoconf` ]; then + AC_VER=`autoconf --version | head -1 | sed 's/^[^0-9]*//'` + AC_VER_MAJOR=`echo $AC_VER | cut -f1 -d'.'` + AC_VER_MINOR=`echo $AC_VER | cut -f2 -d'.' | sed 's/[^0-9]*$//'` + + if [ "$AC_VER_MAJOR" -lt "2" ]; then + echo "Autoconf 2.13 or greater needed to build configure." + exit 1 + fi + + if [ "$AC_VER_MINOR" -lt "13" ]; then + echo "Autoconf 2.13 or greater needed to build configure." + exit 1 + fi + + if [ "$AC_VER_MINOR" -lt "50" ]; then + if [ ! -e configure.in ]; then + ln -s configure.ac configure.in + fi + echo "If you see some warnings about cross-compiling, don't worry; this is normal." + else + echo "rm -f configure.in ?" + fi +else + echo Autoconf not found. AlsaPlayer CVS requires autoconf to bootstrap itself. + exit 1 +fi + +run_cmd() { + echo running $* ... + if ! $*; then + echo failed! + exit 1 + fi +} + +# Check if /usr/local/share/aclocal exists +if [ -d /usr/local/share/aclocal ]; then + ACLOCAL_INCLUDE="$ACLOCAL_INCLUDE -I /usr/local/share/aclocal" +fi + +if [ -d m4 ] ; then + run_cmd cat m4/*.m4 > acinclude.m4 +fi +run_cmd aclocal $ACLOCAL_INCLUDE +run_cmd autoheader +run_cmd libtoolize --automake +run_cmd automake --add-missing +run_cmd autoconf +echo +echo "Now run './configure'" +echo diff --git a/cleanup.sh b/cleanup.sh new file mode 100755 index 0000000..f847f2b --- /dev/null +++ b/cleanup.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +rm -f *~ *.o Makefile configure Makefile.in +rm -f *.guess *.log *.m4 config.status +rm -f stamp-h* +rm -rf *.cache +rm -f depcomp +rm -f config.h* +rm -f config.sub +rm -f libtool +rm -f ltmain.sh +rm -f missing +rm -f mkinstalldirs +rm -f install-sh diff --git a/command.c b/command.c new file mode 100644 index 0000000..cd2fd53 --- /dev/null +++ b/command.c @@ -0,0 +1,222 @@ +/* + * $Id: command.c,v 1.9 2004/03/17 14:59:32 kalle Exp $ + * + */ +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "command.h" + +#undef DEBUG_KEYS + +#ifdef DEBUG_KEYS +#define DK(x) x +#else +#define DK(x) +#endif + +#define BS KEY_BACKSPACE +#define DEL KEY_DC +#define UP KEY_UP +#define DWN KEY_DOWN +#define LEFT KEY_LEFT +#define RGHT KEY_RIGHT +#define HOME KEY_HOME +#define END KEY_END +#define PGDN KEY_NPAGE +#define PGUP KEY_PPAGE +#define TAB 0x09 +#define STAB 0x5A +#define ESC 0x1B +#define F1 KEY_F(1) +#define F2 KEY_F(2) +#define F3 KEY_F(3) +#define F4 KEY_F(4) +#define F5 KEY_F(5) +#define F6 KEY_F(6) + +static command_definition_t cmds[] = +{ + { { 13, 0, 0 }, CMD_PLAY, "Play/Enter directory" }, + { { 'p', 0, 0 }, CMD_PAUSE, "Pause" }, + { { BS, ESC, 0 }, CMD_STOP, "Stop" }, + { { 'n', 0, 0 }, CMD_TRACK_NEXT, "Next song" }, + { { 'b', 0, 0 }, CMD_TRACK_PREVIOUS, "Previous song" }, + + { { '+', RGHT, 0 }, CMD_VOLUME_UP, "Increase volume" }, + { { '-', LEFT, 0 }, CMD_VOLUME_DOWN, "Decrease volume" }, + + { { ' ', 0, 0 }, CMD_SELECT, "Select/deselect song in playlist" }, + { { DEL, 0, 0 }, CMD_DELETE, "Delete song from playlist" }, + { { 's', 0, 0 }, CMD_SHUFFLE, "Shuffle playlist" }, + { { 'c', 0, 0 }, CMD_CLEAR, "Clear playlist" }, + { { 'r', 0, 0 }, CMD_REPEAT, "Toggle repeat mode" }, + { { 'z', 0, 0 }, CMD_RANDOM, "Toggle random mode" }, + + { { UP, 0, 0 }, CMD_LIST_PREVIOUS, "Move: Up" }, + { { DWN, 0, 0 }, CMD_LIST_NEXT, "Move: Down" }, + { { HOME, 0, 0 }, CMD_LIST_FIRST, "Move: Home" }, + { { END, 0, 0 }, CMD_LIST_LAST, "Move: End" }, + { { PGUP, 0, 0 }, CMD_LIST_PREVIOUS_PAGE, "Move: Page Up" }, + { { PGDN, 0, 0 }, CMD_LIST_NEXT_PAGE, "Move: Page Down" }, + + { { TAB, 0, 0 }, CMD_SCREEN_NEXT, "Next screen" }, + { { STAB, 0, 0 }, CMD_SCREEN_PREVIOUS, "Previous screen" }, + { { F1, '1', 'h' }, CMD_SCREEN_HELP, "Help screen" }, + { { F2, '2', 0 }, CMD_SCREEN_PLAY, "Playlist screen" }, + { { F3, '3', 0 }, CMD_SCREEN_FILE, "Browse screen" }, + // { { F4, '4', 0 }, CMD_SCREEN_SEARCH, "Search screen" }, + + { { 'q', 0, 0 }, CMD_QUIT, "Quit " PACKAGE }, + + { { -1, -1, -1 }, CMD_NONE, NULL } +}; + +char * +key2str(int key) +{ + static char buf[2]; + + buf[0] = 0; + switch(key) + { + case ' ': + return "Space"; + case 13: + return "Enter"; + case BS: + return "Backspace"; + case DEL: + return "Delete"; + case UP: + return "Up"; + case DWN: + return "Down"; + case LEFT: + return "Left"; + case RGHT: + return "Right"; + case HOME: + return "Home"; + case END: + return "End"; + case PGDN: + return "PageDown"; + case PGUP: + return "PageUp"; + case TAB: + return "Tab"; + case STAB: + return "Shift+Tab"; + case ESC: + return "Esc"; + case F1: + return "F1"; + case F2: + return "F2"; + case F3: + return "F3"; + case F4: + return "F4"; + case F5: + return "F5"; + case F6: + return "F6"; + default: + snprintf(buf, 2, "%c", key); + } + return buf; +} + +void +command_dump_keys(void) +{ + int i; + + i=0; + while( cmds[i].description ) + { + if( cmds[i].command != CMD_NONE ) + { + int j; + char keystr[80]; + + strcpy(keystr, key2str(cmds[i].keys[0])); + j=1; + while( j<3 && cmds[i].keys[j]>0 ) + { + strcat(keystr, " "); + strcat(keystr, key2str(cmds[i].keys[j])); + j++; + } + printf(" %20s : %s\n", keystr, cmds[i].description); + + } + i++; + } +} + +char * +command_get_keys(command_t command) +{ + int i; + + i=0; + while( cmds[i].description ) + { + if( cmds[i].command == command ) + { + int j; + static char keystr[80]; + + strcpy(keystr, key2str(cmds[i].keys[0])); + j=1; + while( j<3 && cmds[i].keys[j]>0 ) + { + strcat(keystr, " "); + strcat(keystr, key2str(cmds[i].keys[j])); + j++; + } + return keystr; + } + i++; + } + return NULL; +} + +command_t +get_keyboard_command(void) +{ + int i; + int key; + + key = wgetch(stdscr); + + if( key==ERR ) + return CMD_NONE; + + DK(fprintf(stderr, "key = 0x%02X\t", key)); + + if( isalpha(key) ) + key=tolower(key); + + i=0; + while( cmds[i].description ) + { + if( cmds[i].keys[0] == key || + cmds[i].keys[1] == key || + cmds[i].keys[2] == key ) + { + DK(fprintf(stderr, "Match - %s\n", cmds[i].description)); + return cmds[i].command; + } + i++; + } + DK(fprintf(stderr, "NO MATCH\n")); + return CMD_NONE; +} diff --git a/command.h b/command.h new file mode 100644 index 0000000..efb3c27 --- /dev/null +++ b/command.h @@ -0,0 +1,44 @@ + +typedef enum +{ + CMD_NONE = 0, + CMD_PLAY, + CMD_SELECT, + CMD_PAUSE, + CMD_STOP, + CMD_TRACK_NEXT, + CMD_TRACK_PREVIOUS, + CMD_SHUFFLE, + CMD_RANDOM, + CMD_CLEAR, + CMD_DELETE, + CMD_REPEAT, + CMD_VOLUME_UP, + CMD_VOLUME_DOWN, + CMD_LIST_PREVIOUS, + CMD_LIST_NEXT, + CMD_LIST_FIRST, + CMD_LIST_LAST, + CMD_LIST_NEXT_PAGE, + CMD_LIST_PREVIOUS_PAGE, + CMD_SCREEN_PREVIOUS, + CMD_SCREEN_NEXT, + CMD_SCREEN_PLAY, + CMD_SCREEN_FILE, + CMD_SCREEN_SEARCH, + CMD_SCREEN_HELP, + CMD_QUIT +} command_t; + +typedef struct +{ + int keys[3]; + command_t command; + char *description; +} command_definition_t; + + +void command_dump_keys(void); +char *command_get_keys(command_t command); + +command_t get_keyboard_command(void); diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..0b39bec --- /dev/null +++ b/configure.ac @@ -0,0 +1,100 @@ +dnl +dnl $Id: configure.ac,v 1.10 2004/03/18 09:33:43 kalle Exp $ +dnl + +AC_INIT(main.c) +AM_INIT_AUTOMAKE(mpc-ncurses, 0.1.2) + +dnl Check for programs +AC_PROG_CC +AC_PROG_INSTALL +AC_PROG_LIBTOOL + +dnl ======================================================= +dnl initialize variables +dnl ======================================================= + +set -- $CFLAGS +CFLAGS="-Wall $CFLAGS" + + +dnl +dnl Check for libaries +dnl + +dnl ncurses +AC_CHECK_LIB(ncurses, initscr,, [AC_MSG_ERROR(ncurses library is required)]) + +dnl glib-2.0 +AM_PATH_GLIB_2_0(, , [AC_MSG_ERROR(glib-2.x is required)], glib) + +dnl popt +AC_CHECK_LIB(popt, + poptGetArg, + LIBS="$LIBS -lpopt", + AC_MSG_ERROR(Missing popt command line parsing library)) + +dnl +dnl Check for types +dnl +AC_CHECK_TYPE(socklen_t, + AC_DEFINE(HAVE_SOCKLEN_T, 1, socklen_t defined in sys/socket.h), +) + +dnl +dnl Check for headers +dnl +AC_CHECK_HEADER(libgen.h, + AC_DEFINE(HAVE_LIBGEN_H, 1, glibc - libgen.h), + ,) + +dnl +dnl X11 +dnl +dnl AC_PATH_XTRA + +dnl Debugging +AC_ARG_ENABLE(debug, + [ --enable-debug Enable debugging [default=no]], + , + enable_debug=no) + +if test "$enable_debug" = yes; then + CFLAGS="$CFLAGS -g -DDEBUG" +fi + +dnl Default host +AC_ARG_WITH(default-host, + [ --with-default-host=HOST Default host (localhost)], + DEFAULT_HOST="$withval", + DEFAULT_HOST="localhost") + +dnl Default port +AC_ARG_WITH(default-port, + [ --with-default-port=PORT Default port (2100)], + DEFAULT_PORT="$withval", + DEFAULT_PORT="2100") + + +CFLAGS="$CFLAGS $GLIB_CFLAGS" +LIBS="$LIBS $GLIB_LIBS -lncurses" + + +dnl Autoheader +AC_DEFINE_UNQUOTED(DEFAULT_PORT, $DEFAULT_PORT, Default MPD port) +AC_DEFINE_UNQUOTED(DEFAULT_PORT_STR, "$DEFAULT_PORT", Default MPD port) +AC_DEFINE_UNQUOTED(DEFAULT_HOST, "$DEFAULT_HOST", Default MPD host) + + +AM_CONFIG_HEADER(config.h) + +AC_OUTPUT(Makefile) + +echo " +Configuration: + Install path: ${prefix} + Enable debugging: ${enable_debug} + Default MPD host: ${DEFAULT_HOST} + Default MPD port: ${DEFAULT_PORT} +" +echo diff --git a/doc/ncmpc.1 b/doc/ncmpc.1 new file mode 100644 index 0000000..8bda42e --- /dev/null +++ b/doc/ncmpc.1 @@ -0,0 +1,46 @@ +.TH "mpc-ncurses" "1" "" "Kalle Wallin" "" +.SH "NAME" +ncmpc \- ncurses MPD client. +.SH "SYNOPSIS" +.B ncmpc +[options] +.SH "DESCRIPTION" +ncmpc is a client for MPD, the Music Player Daemon. +ncmpc connects to a MPD running on a machine on the local +network. + +By default, ncmpc connects to localhost:2100. +This can be changed either at compile\-time, or by exporting the +MPD_HOST and MPD_PORT environment variables, or by the command line +options \-\-host and \-\-port. + +$ ncmpc \-\-host=musicserver \-\-port=44000 + +Read more about MPD on http://www.musicpd.org + +.SH "OPTIONS" +.TP +.B \-?, \-\-help +Display help. +.TP +.B \-V, \-\-version +Display version information. +.TP +.B \-k, \-\-keys +Display key bindings. +.TP +.B \-h, \-\-host=HOSTNAME +Specify host. +.TP +.B \-p, \-\-port=PORT +Connect to server on PORT. +.TP +Mandatory or optional arguments to long options are also mandatory or optional for any corresponding short options. +.SH "KEYS" +Since ncmpc is under development key bindings may change. +View current ncmpc key bindings in the help screen or by running: + +$ ncmpc \-\-keys + +.SH "SEE ALSO" +mpc(1), MPD(1) diff --git a/libmpdclient.c b/libmpdclient.c new file mode 100644 index 0000000..e3f3b95 --- /dev/null +++ b/libmpdclient.c @@ -0,0 +1,1076 @@ +/* libmpdclient + * (c)2002 by Warren Dukes (shank@mercury.chem.pitt.edu) + * This project's homepage is: http://www.musicpd.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 "libmpdclient.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#ifndef HAVE_SOCKLEN_T +typedef int socklen_t; +#endif +#endif + +#ifndef MPD_NO_IPV6 +#ifdef AF_INET6 +#define MPD_HAVE_IPV6 +#endif +#endif + +#ifdef MPD_HAVE_IPV6 +int mpd_ipv6Supported() { + int s; + s = socket(AF_INET6,SOCK_STREAM,0); + if(s == -1) return 0; + close(s); + return 1; +} +#endif + + +char * mpd_sanitizeArg(const char * arg) { + size_t i; + int count=0; + char * ret; + + for(i=0;iname = strdup(name); + ret->value = strdup(value); + + return ret; +} + +void mpd_freeReturnElement(mpd_ReturnElement * re) { + free(re->name); + free(re->value); + free(re); +} + +void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout) { + connection->timeout.tv_sec = (int)timeout; + connection->timeout.tv_usec = (int)((timeout - + connection->timeout.tv_sec)/1e+6+0.5); +} + +mpd_Connection * mpd_newConnection(const char * host, int port, float timeout) { + int err; + struct hostent * he; + struct sockaddr * dest; + socklen_t destlen; + struct sockaddr_in sin; + char * rt; + char * output; + mpd_Connection * connection = malloc(sizeof(mpd_Connection)); + struct timeval tv; + fd_set fds; +#ifdef MPD_HAVE_IPV6 + struct sockaddr_in6 sin6; +#endif + strcpy(connection->buffer,""); + connection->buflen = 0; + connection->bufstart = 0; + strcpy(connection->errorStr,""); + connection->error = 0; + connection->doneProcessing = 0; + connection->commandList = 0; + connection->returnElement = NULL; + + if(!(he=gethostbyname(host))) { + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "host \"%s\" not found",host); + connection->error = MPD_ERROR_UNKHOST; + return connection; + } + + memset(&sin,0,sizeof(struct sockaddr_in)); + /*dest.sin_family = he->h_addrtype;*/ + sin.sin_family = AF_INET; + sin.sin_port = htons(port); +#ifdef MPD_HAVE_IPV6 + memset(&sin6,0,sizeof(struct sockaddr_in6)); + sin6.sin6_family = AF_INET6; + sin6.sin6_port = htons(port); +#endif + switch(he->h_addrtype) { + case AF_INET: + memcpy((char *)&sin.sin_addr.s_addr,(char *)he->h_addr, + he->h_length); + dest = (struct sockaddr *)&sin; + destlen = sizeof(struct sockaddr_in); + break; +#ifdef MPD_HAVE_IPV6 + case AF_INET6: + if(!mpd_ipv6Supported()) { + strcpy(connection->errorStr,"no IPv6 suuport but a " + "IPv6 address found\n"); + connection->error = MPD_ERROR_SYSTEM; + return connection; + } + memcpy((char *)&sin6.sin6_addr.s6_addr,(char *)he->h_addr, + he->h_length); + dest = (struct sockaddr *)&sin6; + destlen = sizeof(struct sockaddr_in6); + break; +#endif + default: + strcpy(connection->errorStr,"address type is not IPv4 or " + "IPv6\n"); + connection->error = MPD_ERROR_SYSTEM; + return connection; + break; + } + + if((connection->sock = socket(dest->sa_family,SOCK_STREAM,0))<0) { + strcpy(connection->errorStr,"problems creating socket"); + connection->error = MPD_ERROR_SYSTEM; + return connection; + } + + /* connect stuff */ + { +#ifdef SO_RCVTIMEO + struct timeval rcvoldto; + struct timeval sndoldto; + socklen_t oldlen = sizeof(struct timeval); + + mpd_setConnectionTimeout(connection,timeout); + + tv.tv_sec = connection->timeout.tv_sec; + tv.tv_usec = connection->timeout.tv_usec; + + if(getsockopt(connection->sock,SOL_SOCKET,SO_RCVTIMEO,&rcvoldto, + &oldlen)<0 || + getsockopt(connection->sock,SOL_SOCKET, + SO_SNDTIMEO,&sndoldto,&oldlen)<0) + { + strcpy(connection->errorStr,"problems getting socket " + "timeout\n"); + connection->error = MPD_ERROR_SYSTEM; + return connection; + } + if(setsockopt(connection->sock,SOL_SOCKET,SO_RCVTIMEO,&tv, + sizeof(struct timeval))<0 || + setsockopt(connection->sock,SOL_SOCKET, + SO_SNDTIMEO,&tv, + sizeof(struct timeval))<0) + { + strcpy(connection->errorStr,"problems setting socket " + "timeout\n"); + connection->error = MPD_ERROR_SYSTEM; + return connection; + } +#endif + if(connect(connection->sock,dest,destlen)<0) { + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "problems connecting to \"%s\" on port" + " %i",host,port); + connection->error = MPD_ERROR_CONNPORT; + return connection; + } +#ifdef SO_RCVTIMEO + if(setsockopt(connection->sock,SOL_SOCKET,SO_SNDTIMEO,&rcvoldto, + sizeof(struct timeval))<0 || + setsockopt(connection->sock,SOL_SOCKET, + SO_SNDTIMEO,&sndoldto, + sizeof(struct timeval))<0) + { + strcpy(connection->errorStr,"problems setting socket " + "timeout\n"); + connection->error = MPD_ERROR_SYSTEM; + return connection; + } +#endif + } + + while(!(rt = strstr(connection->buffer,"\n"))) { + tv.tv_sec = connection->timeout.tv_sec; + tv.tv_usec = connection->timeout.tv_usec; + FD_ZERO(&fds); + FD_SET(connection->sock,&fds); + if((err = select(connection->sock+1,&fds,NULL,NULL,&tv)) == 1) { + int readed; + readed = recv(connection->sock, + &(connection->buffer[connection->buflen]), + MPD_BUFFER_MAX_LENGTH-connection->buflen,0); + if(readed<=0) { + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "problems getting a response from" + " \"%s\" on port %i",host,port); + connection->error = MPD_ERROR_NORESPONSE; + return connection; + } + connection->buflen+=readed; + connection->buffer[connection->buflen] = '\0'; + tv.tv_sec = connection->timeout.tv_sec; + tv.tv_usec = connection->timeout.tv_usec; + } + else if(err<0 && errno==EINTR) continue; + else { + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "timeout in attempting to get a response from" + " \"%s\" on port %i",host,port); + connection->error = MPD_ERROR_NORESPONSE; + return connection; + } + } + + *rt = '\0'; + output = strdup(connection->buffer); + strcpy(connection->buffer,rt+1); + connection->buflen = strlen(connection->buffer); + + if(strncmp(output,MPD_WELCOME_MESSAGE,strlen(MPD_WELCOME_MESSAGE))) { + free(output); + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "mpd not running on port %i on host \"%s\"", + port,host); + connection->error = MPD_ERROR_NOTMPD; + return connection; + } + + { + char * test; + char * version[3]; + char * tmp = &output[strlen(MPD_WELCOME_MESSAGE)]; + char * search = "."; + int i; + + for(i=0;i<3;i++) { + char * tok; + if(i==3) search = " "; + version[i] = strtok_r(tmp,search,&tok); + if(!version[i]) { + free(output); + snprintf(connection->errorStr, + MPD_BUFFER_MAX_LENGTH, + "error parsing version number at " + "\"%s\"", + &output[strlen(MPD_WELCOME_MESSAGE)]); + connection->error = MPD_ERROR_NOTMPD; + return connection; + } + connection->version[i] = strtol(version[i],&test,10); + if(version[i]==test || *test!='\0') { + free(output); + snprintf(connection->errorStr, + MPD_BUFFER_MAX_LENGTH, + "error parsing version number at " + "\"%s\"", + &output[strlen(MPD_WELCOME_MESSAGE)]); + connection->error = MPD_ERROR_NOTMPD; + return connection; + } + tmp = NULL; + } + } + + free(output); + + connection->doneProcessing = 1; + + return connection; +} + +void mpd_clearError(mpd_Connection * connection) { + connection->error = 0; + connection->errorStr[0] = '\0'; +} + +void mpd_closeConnection(mpd_Connection * connection) { + close(connection->sock); + if(connection->returnElement) free(connection->returnElement); + free(connection); +} + +void mpd_executeCommand(mpd_Connection * connection, char * command) { + int ret; + struct timeval tv; + fd_set fds; + char * commandPtr = command; + int commandLen = strlen(command); + + if(!connection->doneProcessing && !connection->commandList) { + strcpy(connection->errorStr,"not done processing current command"); + connection->error = 1; + return; + } + + mpd_clearError(connection); + + FD_ZERO(&fds); + FD_SET(connection->sock,&fds); + tv.tv_sec = connection->timeout.tv_sec; + tv.tv_usec = connection->timeout.tv_usec; + + while((ret = select(connection->sock+1,NULL,&fds,NULL,&tv)==1) || + (ret==-1 && errno==EINTR)) { + ret = send(connection->sock,commandPtr,commandLen, + MSG_DONTWAIT); + if(ret<=0) + { + if(ret==EAGAIN || ret==EINTR) continue; + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "problems giving command \"%s\"",command); + connection->error = MPD_ERROR_SENDING; + return; + } + else { + commandPtr+=ret; + commandLen-=ret; + } + + if(commandLen<=0) break; + } + + if(commandLen>0) { + perror(""); + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "timeout sending command \"%s\"",command); + connection->error = MPD_ERROR_TIMEOUT; + return; + } + + if(!connection->commandList) connection->doneProcessing = 0; +} + +void mpd_getNextReturnElement(mpd_Connection * connection) { + char * output = NULL; + char * rt = NULL; + char * name; + char * value; + fd_set fds; + struct timeval tv; + char * tok; + int readed; + char * bufferCheck; + int err; + + if(connection->returnElement) mpd_freeReturnElement(connection->returnElement); + connection->returnElement = NULL; + + if(connection->doneProcessing) { + strcpy(connection->errorStr,"already done processing current command"); + connection->error = 1; + return; + } + + bufferCheck = connection->buffer+connection->bufstart; + while(connection->bufstart>=connection->buflen || + !(rt = strstr(bufferCheck,"\n"))) { + if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) { + memmove(connection->buffer, + connection->buffer+ + connection->bufstart, + connection->buflen- + connection->bufstart+1); + bufferCheck-=connection->bufstart; + connection->buflen-=connection->bufstart; + connection->bufstart = 0; + } + if(connection->buflen>=MPD_BUFFER_MAX_LENGTH) { + strcpy(connection->errorStr,"buffer overrun"); + connection->error = MPD_ERROR_BUFFEROVERRUN; + connection->doneProcessing = 1; + return; + } + bufferCheck+=connection->buflen-connection->bufstart; + tv.tv_sec = connection->timeout.tv_sec; + tv.tv_usec = connection->timeout.tv_usec; + FD_ZERO(&fds); + FD_SET(connection->sock,&fds); + if((err = select(connection->sock+1,&fds,NULL,NULL,&tv) == 1)) { + readed = recv(connection->sock, + connection->buffer+connection->buflen, + MPD_BUFFER_MAX_LENGTH-connection->buflen, + MSG_DONTWAIT); + if(readed<0 && (errno==EAGAIN || errno==EINTR)) { + continue; + } + if(readed<=0) { + strcpy(connection->errorStr,"connection" + " closed"); + connection->error = MPD_ERROR_CONNCLOSED; + connection->doneProcessing = 1; + return; + } + connection->buflen+=readed; + connection->buffer[connection->buflen] = '\0'; + } + else if(err<0 && errno==EINTR) continue; + else { + strcpy(connection->errorStr,"connection timeout"); + connection->error = MPD_ERROR_TIMEOUT; + connection->doneProcessing = 1; + return; + } + } + + *rt = '\0'; + output = connection->buffer+connection->bufstart; + connection->bufstart = rt - connection->buffer + 1; + + if(strcmp(output,"OK")==0) { + connection->doneProcessing = 1; + return; + } + if(strncmp(output,"ACK",strlen("ACK"))==0) { + strcpy(connection->errorStr,output); + connection->error = MPD_ERROR_ACK; + connection->doneProcessing = 1; + return; + } + + name = strtok_r(output,":",&tok); + if(name && (value = strtok_r(NULL,"",&tok)) && value[0]==' ') { + connection->returnElement = mpd_newReturnElement(name,&(value[1])); + } + else { + if(!name || !value) { + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "error parsing: %s",output); + } + else { + snprintf(connection->errorStr,MPD_BUFFER_MAX_LENGTH, + "error parsing: %s:%s",name,value); + } + connection->errorStr[MPD_BUFFER_MAX_LENGTH] = '\0'; + connection->error = 1; + } +} + +void mpd_finishCommand(mpd_Connection * connection) { + while(!connection->doneProcessing) mpd_getNextReturnElement(connection); +} + + +mpd_Status * mpd_getStatus(mpd_Connection * connection) { + mpd_Status * status; + + mpd_executeCommand(connection,"status\n"); + + if(connection->error) return NULL; + + status = malloc(sizeof(mpd_Status)); + status->volume = -1; + status->repeat = 0; + status->random = 0; + status->playlist = -1; + status->playlistLength = -1; + status->state = -1; + status->song = 0; + status->elapsedTime = 0; + status->totalTime = 0; + status->bitRate = 0; + status->error = NULL; + + mpd_getNextReturnElement(connection); + if(connection->error) { + free(status); + return NULL; + } + while(connection->returnElement) { + mpd_ReturnElement * re = connection->returnElement; + if(strcmp(re->name,"volume")==0) { + status->volume = atoi(re->value); + } + else if(strcmp(re->name,"repeat")==0) { + status->repeat = atoi(re->value); + } + else if(strcmp(re->name,"random")==0) { + status->random = atoi(re->value); + } + else if(strcmp(re->name,"playlist")==0) { + status->playlist = strtol(re->value,NULL,10); + } + else if(strcmp(re->name,"playlistlength")==0) { + status->playlistLength = atoi(re->value); + } + else if(strcmp(re->name,"bitrate")==0) { + status->bitRate = atoi(re->value); + } + else if(strcmp(re->name,"state")==0) { + if(strcmp(re->value,"play")==0) { + status->state = MPD_STATUS_STATE_PLAY; + } + else if(strcmp(re->value,"stop")==0) { + status->state = MPD_STATUS_STATE_STOP; + } + else if(strcmp(re->value,"pause")==0) { + status->state = MPD_STATUS_STATE_PAUSE; + } + else { + status->state = MPD_STATUS_STATE_UNKNOWN; + } + } + else if(strcmp(re->name,"song")==0) { + status->song = atoi(re->value); + } + else if(strcmp(re->name,"time")==0) { + char * tok; + char * copy; + copy = strdup(re->value); + status->elapsedTime = atoi(strtok_r(copy,":",&tok)); + status->totalTime = atoi(strtok_r(NULL,"",&tok)); + free(copy); + } + else if(strcmp(re->name,"error")==0) { + status->error = strdup(re->value); + } + + mpd_getNextReturnElement(connection); + if(connection->error) { + free(status); + return NULL; + } + } + + if(connection->error) { + free(status); + return NULL; + } + else if(status->state<0) { + strcpy(connection->errorStr,"state not found"); + connection->error = 1; + free(status); + return NULL; + } + + return status; +} + +void mpd_freeStatus(mpd_Status * status) { + if(status->error) free(status->error); + free(status); +} + +void mpd_initSong(mpd_Song * song) { + song->file = NULL; + song->artist = NULL; + song->album = NULL; + song->track = NULL; + song->title = NULL; + song->time = MPD_SONG_NO_TIME; +} + +void mpd_finishSong(mpd_Song * song) { + if(song->file) free(song->file); + if(song->artist) free(song->artist); + if(song->album) free(song->album); + if(song->title) free(song->title); + if(song->track) free(song->track); +} + +mpd_Song * mpd_newSong() { + mpd_Song * ret = malloc(sizeof(mpd_Song)); + + mpd_initSong(ret); + + return ret; +} + +void mpd_freeSong(mpd_Song * song) { + mpd_finishSong(song); + free(song); +} + +mpd_Song * mpd_songDup(mpd_Song * song) { + mpd_Song * ret = mpd_newSong(); + + if(song->file) ret->file = strdup(song->file); + if(song->artist) ret->artist = strdup(song->artist); + if(song->album) ret->album = strdup(song->album); + if(song->title) ret->title = strdup(song->title); + if(song->track) ret->track = strdup(song->track); + ret->time = song->time; + + return ret; +} + +void mpd_initDirectory(mpd_Directory * directory) { + directory->path = NULL; +} + +void mpd_finishDirectory(mpd_Directory * directory) { + if(directory->path) free(directory->path); +} + +mpd_Directory * mpd_newDirectory () { + mpd_Directory * directory = malloc(sizeof(mpd_Directory));; + + mpd_initDirectory(directory); + + return directory; +} + +void mpd_freeDirectory(mpd_Directory * directory) { + mpd_finishDirectory(directory); + + free(directory); +} + +mpd_Directory * mpd_directoryDup(mpd_Directory * directory) { + mpd_Directory * ret = mpd_newDirectory(); + + if(directory->path) ret->path = strdup(directory->path); + + return ret; +} + +void mpd_initPlaylistFile(mpd_PlaylistFile * playlist) { + playlist->path = NULL; +} + +void mpd_finishPlaylistFile(mpd_PlaylistFile * playlist) { + if(playlist->path) free(playlist->path); +} + +mpd_PlaylistFile * mpd_newPlaylistFile() { + mpd_PlaylistFile * playlist = malloc(sizeof(mpd_PlaylistFile)); + + mpd_initPlaylistFile(playlist); + + return playlist; +} + +void mpd_freePlaylistFile(mpd_PlaylistFile * playlist) { + mpd_finishPlaylistFile(playlist); + free(playlist); +} + +mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist) { + mpd_PlaylistFile * ret = mpd_newPlaylistFile(); + + if(playlist->path) ret->path = strdup(playlist->path); + + return ret; +} + +void mpd_initInfoEntity(mpd_InfoEntity * entity) { + entity->info.directory = NULL; +} + +void mpd_finishInfoEntity(mpd_InfoEntity * entity) { + if(entity->info.directory) { + if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) { + mpd_freeDirectory(entity->info.directory); + } + else if(entity->type == MPD_INFO_ENTITY_TYPE_SONG) { + mpd_freeSong(entity->info.song); + } + else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) { + mpd_freePlaylistFile(entity->info.playlistFile); + } + } +} + +mpd_InfoEntity * mpd_newInfoEntity() { + mpd_InfoEntity * entity = malloc(sizeof(mpd_InfoEntity)); + + mpd_initInfoEntity(entity); + + return entity; +} + +void mpd_freeInfoEntity(mpd_InfoEntity * entity) { + mpd_finishInfoEntity(entity); + free(entity); +} + +void mpd_sendInfoCommand(mpd_Connection * connection, char * command) { + mpd_executeCommand(connection,command); +} + +mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection) { + mpd_InfoEntity * entity = NULL; + + if(connection->doneProcessing) return NULL; + + if(!connection->returnElement) mpd_getNextReturnElement(connection); + + if(connection->returnElement) { + if(strcmp(connection->returnElement->name,"file")==0) { + entity = mpd_newInfoEntity(); + entity->type = MPD_INFO_ENTITY_TYPE_SONG; + entity->info.song = mpd_newSong(); + entity->info.song->file = + strdup(connection->returnElement->value); + } + else if(strcmp(connection->returnElement->name, + "directory")==0) { + entity = mpd_newInfoEntity(); + entity->type = MPD_INFO_ENTITY_TYPE_DIRECTORY; + entity->info.directory = mpd_newDirectory(); + entity->info.directory->path = + strdup(connection->returnElement->value); + } + else if(strcmp(connection->returnElement->name,"playlist")==0) { + entity = mpd_newInfoEntity(); + entity->type = MPD_INFO_ENTITY_TYPE_PLAYLISTFILE; + entity->info.playlistFile = mpd_newPlaylistFile(); + entity->info.playlistFile->path = + strdup(connection->returnElement->value); + } + else { + connection->error = 1; + strcpy(connection->errorStr,"problem parsing song info"); + return NULL; + } + } + else return NULL; + + mpd_getNextReturnElement(connection); + while(connection->returnElement) { + mpd_ReturnElement * re = connection->returnElement; + + if(strcmp(re->name,"file")==0) return entity; + else if(strcmp(re->name,"directory")==0) return entity; + else if(strcmp(re->name,"playlist")==0) return entity; + + if(entity->type == MPD_INFO_ENTITY_TYPE_SONG && + strlen(re->value)) { + if(!entity->info.song->artist && + strcmp(re->name,"Artist")==0) { + entity->info.song->artist = strdup(re->value); + } + else if(!entity->info.song->album && + strcmp(re->name,"Album")==0) { + entity->info.song->album = strdup(re->value); + } + else if(!entity->info.song->title && + strcmp(re->name,"Title")==0) { + entity->info.song->title = strdup(re->value); + } + else if(!entity->info.song->track && + strcmp(re->name,"Track")==0) { + entity->info.song->track = strdup(re->value); + } + else if(entity->info.song->time==MPD_SONG_NO_TIME && + strcmp(re->name,"Time")==0) { + entity->info.song->time = atoi(re->value); + } + } + else if(entity->type == MPD_INFO_ENTITY_TYPE_DIRECTORY) { + } + else if(entity->type == MPD_INFO_ENTITY_TYPE_PLAYLISTFILE) { + } + + mpd_getNextReturnElement(connection); + } + + return entity; +} + +char * mpd_getNextReturnElementNamed(mpd_Connection * connection, + const char * name) +{ + if(connection->doneProcessing) return NULL; + + mpd_getNextReturnElement(connection); + while(connection->returnElement) { + mpd_ReturnElement * re = connection->returnElement; + + if(strcmp(re->name,name)==0) return strdup(re->value); + mpd_getNextReturnElement(connection); + } + + return NULL; +} + +char * mpd_getNextArtist(mpd_Connection * connection) { + return mpd_getNextReturnElementNamed(connection,"Artist"); +} + +char * mpd_getNextAlbum(mpd_Connection * connection) { + return mpd_getNextReturnElementNamed(connection,"Album"); +} + +void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songNum) { + char * string = malloc(strlen("playlistinfo")+25); + sprintf(string,"playlistinfo \"%i\"\n",songNum); + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendListallCommand(mpd_Connection * connection, const char * dir) { + char * sDir = mpd_sanitizeArg(dir); + char * string = malloc(strlen("listall")+strlen(sDir)+5); + sprintf(string,"listall \"%s\"\n",sDir); + mpd_sendInfoCommand(connection,string); + free(string); + free(sDir); +} + +void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir) { + char * sDir = mpd_sanitizeArg(dir); + char * string = malloc(strlen("listallinfo")+strlen(sDir)+5); + sprintf(string,"listallinfo \"%s\"\n",sDir); + mpd_sendInfoCommand(connection,string); + free(string); + free(sDir); +} + +void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir) { + char * sDir = mpd_sanitizeArg(dir); + char * string = malloc(strlen("lsinfo")+strlen(sDir)+5); + sprintf(string,"lsinfo \"%s\"\n",sDir); + mpd_sendInfoCommand(connection,string); + free(string); + free(sDir); +} + +void mpd_sendSearchCommand(mpd_Connection * connection, int table, + const char * str) +{ + char st[10]; + char * string; + char * sanitStr = mpd_sanitizeArg(str); + if(table == MPD_TABLE_ARTIST) strcpy(st,"artist"); + else if(table == MPD_TABLE_ALBUM) strcpy(st,"album"); + else if(table == MPD_TABLE_TITLE) strcpy(st,"title"); + else if(table == MPD_TABLE_FILENAME) strcpy(st,"filename"); + else { + connection->error = 1; + strcpy(connection->errorStr,"unknown table for search"); + return; + } + string = malloc(strlen("search")+strlen(sanitStr)+strlen(st)+6); + sprintf(string,"search %s \"%s\"\n",st,sanitStr); + mpd_sendInfoCommand(connection,string); + free(string); + free(sanitStr); +} + +void mpd_sendFindCommand(mpd_Connection * connection, int table, + const char * str) +{ + char st[10]; + char * string; + char * sanitStr = mpd_sanitizeArg(str); + if(table == MPD_TABLE_ARTIST) strcpy(st,"artist"); + else if(table == MPD_TABLE_ALBUM) strcpy(st,"album"); + else if(table == MPD_TABLE_TITLE) strcpy(st,"title"); + else { + connection->error = 1; + strcpy(connection->errorStr,"unknown table for find"); + return; + } + string = malloc(strlen("find")+strlen(sanitStr)+strlen(st)+6); + sprintf(string,"find %s \"%s\"\n",st,sanitStr); + mpd_sendInfoCommand(connection,string); + free(string); + free(sanitStr); +} + +void mpd_sendListCommand(mpd_Connection * connection, int table, + const char * arg1) +{ + char st[10]; + char * string; + if(table == MPD_TABLE_ARTIST) strcpy(st,"artist"); + else if(table == MPD_TABLE_ALBUM) strcpy(st,"album"); + else { + connection->error = 1; + strcpy(connection->errorStr,"unknown table for list"); + return; + } + if(arg1) { + char * sanitArg1 = mpd_sanitizeArg(arg1); + string = malloc(strlen("list")+strlen(sanitArg1)+strlen(st)+6); + sprintf(string,"list %s \"%s\"\n",st,sanitArg1); + free(sanitArg1); + } + else { + string = malloc(strlen("list")+strlen(st)+3); + sprintf(string,"list %s\n",st); + } + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendAddCommand(mpd_Connection * connection, const char * file) { + char * sFile = mpd_sanitizeArg(file); + char * string = malloc(strlen("add")+strlen(sFile)+5); + sprintf(string,"add \"%s\"\n",sFile); + mpd_executeCommand(connection,string); + free(string); + free(sFile); +} + +void mpd_sendDeleteCommand(mpd_Connection * connection, int songNum) { + char * string = malloc(strlen("delete")+25); + sprintf(string,"delete \"%i\"\n",songNum); + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendSaveCommand(mpd_Connection * connection, const char * name) { + char * sName = mpd_sanitizeArg(name); + char * string = malloc(strlen("save")+strlen(sName)+5); + sprintf(string,"save \"%s\"\n",sName); + mpd_executeCommand(connection,string); + free(string); + free(sName); +} + +void mpd_sendLoadCommand(mpd_Connection * connection, const char * name) { + char * sName = mpd_sanitizeArg(name); + char * string = malloc(strlen("load")+strlen(sName)+5); + sprintf(string,"load \"%s\"\n",sName); + mpd_executeCommand(connection,string); + free(string); + free(sName); +} + +void mpd_sendRmCommand(mpd_Connection * connection, const char * name) { + char * sName = mpd_sanitizeArg(name); + char * string = malloc(strlen("rm")+strlen(sName)+5); + sprintf(string,"rm \"%s\"\n",sName); + mpd_executeCommand(connection,string); + free(string); + free(sName); +} + +void mpd_sendShuffleCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"shuffle\n"); +} + +void mpd_sendClearCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"clear\n"); +} + +void mpd_sendPlayCommand(mpd_Connection * connection, int songNum) { + char * string = malloc(strlen("play")+25); + sprintf(string,"play \"%i\"\n",songNum); + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendStopCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"stop\n"); +} + +void mpd_sendPauseCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"pause\n"); +} + +void mpd_sendNextCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"next\n"); +} + +void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to) { + char * string = malloc(strlen("move")+25); + sprintf(string,"move \"%i\" \"%i\"\n",from,to); + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2) { + char * string = malloc(strlen("swap")+25); + sprintf(string,"swap \"%i\" \"%i\"\n",song1,song2); + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time) { + char * string = malloc(strlen("seek")+25); + sprintf(string,"seek \"%i\" \"%i\"\n",song,time); + mpd_sendInfoCommand(connection,string); + free(string); +} + +void mpd_sendUpdateCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"update\n"); +} + +void mpd_sendPrevCommand(mpd_Connection * connection) { + mpd_executeCommand(connection,"previous\n"); +} + +void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode) { + char * string = malloc(strlen("repeat")+25); + sprintf(string,"repeat \"%i\"\n",repeatMode); + mpd_executeCommand(connection,string); + free(string); +} + +void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode) { + char * string = malloc(strlen("random")+25); + sprintf(string,"random \"%i\"\n",randomMode); + mpd_executeCommand(connection,string); + free(string); +} + +void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange) { + char * string = malloc(strlen("volume")+25); + sprintf(string,"volume \"%i\"\n",volumeChange); + mpd_executeCommand(connection,string); + free(string); +} + +void mpd_sendCommandListBegin(mpd_Connection * connection) { + if(connection->commandList) { + strcpy(connection->errorStr,"already in command list mode"); + connection->error = 1; + return; + } + connection->commandList = 1; + mpd_executeCommand(connection,"command_list_begin\n"); +} + +void mpd_sendCommandListEnd(mpd_Connection * connection) { + if(!connection->commandList) { + strcpy(connection->errorStr,"not in command list mode"); + connection->error = 1; + return; + } + connection->commandList = 0; + mpd_executeCommand(connection,"command_list_end\n"); +} diff --git a/libmpdclient.h b/libmpdclient.h new file mode 100644 index 0000000..0ea84b1 --- /dev/null +++ b/libmpdclient.h @@ -0,0 +1,369 @@ +/* libmpdclient + * (c)2002 by Warren Dukes (shank@mercury.chem.pitt.edu) + * This project's homepage is: http://www.musicpd.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser 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 + */ + +#ifndef LIBMPDCLIENT_H +#define LIBMPDCLIENT_H + +#include +#include + +#define MPD_BUFFER_MAX_LENGTH 50000 +#define MPD_WELCOME_MESSAGE "OK MPD " + +#define MPD_ERROR_TIMEOUT 10 /* timeout trying to talk to mpd */ +#define MPD_ERROR_SYSTEM 11 /* system error */ +#define MPD_ERROR_UNKHOST 12 /* unknown host */ +#define MPD_ERROR_CONNPORT 13 /* problems connecting to port on host */ +#define MPD_ERROR_NOTMPD 14 /* mpd not running on port at host */ +#define MPD_ERROR_NORESPONSE 15 /* no response on attempting to connect */ +#define MPD_ERROR_SENDING 16 /* error sending command */ +#define MPD_ERROR_CONNCLOSED 17 /* connection closed by mpd */ +#define MPD_ERROR_ACK 18 /* ACK returned! */ +#define MPD_ERROR_BUFFEROVERRUN 19 /* Buffer was overrun! */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* internal stuff don't touch this struct */ +typedef struct _mpd_ReturnElement { + char * name; + char * value; +} mpd_ReturnElement; + +/* mpd_Connection + * holds info about connection to mpd + * use error, and errorStr to detect errors + */ +typedef struct _mpd_Connection { + /* use this to check the version of mpd */ + int version[3]; + /* IMPORTANT, you want to get the error messages from here */ + char errorStr[MPD_BUFFER_MAX_LENGTH+1]; + /* this will be set to 1 if there is an error, 0 if not */ + int error; + /* DON'T TOUCH any of the rest of this stuff */ + int sock; + char buffer[MPD_BUFFER_MAX_LENGTH+1]; + int buflen; + int bufstart; + int doneProcessing; + int commandList; + mpd_ReturnElement * returnElement; + struct timeval timeout; +} mpd_Connection; + +/* mpd_newConnection + * use this to open a new connection + * you should use mpd_closeConnection, when your done with the connection, + * even if an error has occurred + * _timeout_ is the connection timeout period in seconds + */ +mpd_Connection * mpd_newConnection(const char * host, int port, float timeout); + +void mpd_setConnectionTimeout(mpd_Connection * connection, float timeout); + +/* mpd_closeConnection + * use this to close a connection and free'ing subsequent memory + */ +void mpd_closeConnection(mpd_Connection * connection); + +/* mpd_clearError + * clears error + */ +void mpd_clearError(mpd_Connection * connection); + +/* STATUS STUFF */ + +/* use these with status.state to determine what state the player is in */ +#define MPD_STATUS_STATE_UNKNOWN 0 +#define MPD_STATUS_STATE_STOP 1 +#define MPD_STATUS_STATE_PLAY 2 +#define MPD_STATUS_STATE_PAUSE 3 + +/* us this with status.volume to determine if mpd has volume support */ +#define MPD_STATUS_NO_VOLUME -1 + +/* mpd_Status + * holds info return from status command + */ +typedef struct mpd_Status { + /* 0-100, or MPD_STATUS_NO_VOLUME when there is no volume support */ + int volume; + /* 1 if repeat is on, 0 otherwise */ + int repeat; + /* 1 if random is on, 0 otherwise */ + int random; + /* playlist length */ + int playlistLength; + /* playlist, use this to determine when the playlist has changed */ + long long playlist; + /* use with MPD_STATUS_STATE_* to determine state of player */ + int state; + /* if in PLAY or PAUSE state, this is the number of the currently + * playing song in the playlist, beginning with 0 + */ + int song; + /* time in seconds that have elapsed in the currently playing/paused + * song + */ + int elapsedTime; + /* length in seconds of the currently playing/paused song */ + int totalTime; + /* current bit rate in kbs */ + int bitRate; + /* error */ + char * error; +} mpd_Status; + +/* mpd_getStatus + * returns status info, be sure to free it with mpd_freeStatus() + */ +mpd_Status * mpd_getStatus(mpd_Connection * connection); + +/* mpd_freeStatus + * free's status info malloc'd and returned by mpd_getStatus + */ +void mpd_freeStatus(mpd_Status * status); + +/* SONG STUFF */ + +#define MPD_SONG_NO_TIME -1 + +/* mpd_Song + * for storing song info returned by mpd + */ +typedef struct _mpd_Song { + /* filename of song */ + char * file; + /* artist, maybe NULL if there is no tag */ + char * artist; + /* title, maybe NULL if there is no tag */ + char * title; + /* album, maybe NULL if there is no tag */ + char * album; + /* track, maybe NULL if there is no tag */ + char * track; + /* length of song in seconds, check that it is not MPD_SONG_NO_TIME */ + int time; +} mpd_Song; + +/* mpd_newSong + * use to allocate memory for a new mpd_Song + * file, artist, etc all initialized to NULL + * if your going to assign values to file, artist, etc + * be sure to malloc or strdup the memory + * use mpd_freeSong to free the memory for the mpd_Song, it will also + * free memory for file, artist, etc, so don't do it yourself + */ +mpd_Song * mpd_newSong(); + +/* mpd_freeSong + * use to free memory allocated by mpd_newSong + * also it will free memory pointed to by file, artist, etc, so be careful + */ +void mpd_freeSong(mpd_Song * song); + +/* mpd_songDup + * works like strDup, but for a mpd_Song + */ +mpd_Song * mpd_songDup(mpd_Song * song); + +/* DIRECTORY STUFF */ + +/* mpd_Directory + * used to store info fro directory (right now that just the path) + */ +typedef struct _mpd_Directory { + char * path; +} mpd_Directory; + +/* mpd_newDirectory + * allocates memory for a new directory + * use mpd_freeDirectory to free this memory + */ +mpd_Directory * mpd_newDirectory (); + +/* mpd_freeDirectory + * used to free memory allocated with mpd_newDirectory, and it frees + * path of mpd_Directory, so be careful + */ +void mpd_freeDirectory(mpd_Directory * directory); + +/* mpd_directoryDup + * works like strdup, but for mpd_Directory + */ +mpd_Directory * mpd_directoryDup(mpd_Directory * directory); + +/* PLAYLISTFILE STUFF */ + +/* mpd_PlaylistFile + * stores info about playlist file returned by lsinfo + */ +typedef struct _mpd_PlaylistFile { + char * path; +} mpd_PlaylistFile; + +/* mpd_newPlaylistFile + * allocates memory for new mpd_PlaylistFile, path is set to NULL + * free this memory with mpd_freePlaylistFile + */ +mpd_PlaylistFile * mpd_newPlaylistFile(); + +/* mpd_freePlaylist + * free memory allocated for freePlaylistFile, will also free + * path, so be careful + */ +void mpd_freePlaylistFile(mpd_PlaylistFile * playlist); + +/* mpd_playlistFileDup + * works like strdup, but for mpd_PlaylistFile + */ +mpd_PlaylistFile * mpd_playlistFileDup(mpd_PlaylistFile * playlist); + +/* INFO ENTITY STUFF */ + +/* the type of entity returned from one of the commands that generates info + * use in conjunction with mpd_InfoEntity.type + */ +#define MPD_INFO_ENTITY_TYPE_DIRECTORY 0 +#define MPD_INFO_ENTITY_TYPE_SONG 1 +#define MPD_INFO_ENTITY_TYPE_PLAYLISTFILE 2 + +/* mpd_InfoEntity + * stores info on stuff returned info commands + */ +typedef struct mpd_InfoEntity { + /* the type of entity, use with MPD_INFO_ENTITY_TYPE_* to determine + * what this entity is (song, directory, etc...) + */ + int type; + /* the actual data you want, mpd_Song, mpd_Directory, etc */ + union { + mpd_Directory * directory; + mpd_Song * song; + mpd_PlaylistFile * playlistFile; + } info; +} mpd_InfoEntity; + +mpd_InfoEntity * mpd_newInfoEntity(); + +void mpd_freeInfoEntity(mpd_InfoEntity * entity); + +/* INFO COMMANDS AND STUFF */ + +/* use this function to loop over after calling Info/Listall functions */ +mpd_InfoEntity * mpd_getNextInfoEntity(mpd_Connection * connection); + +/* songNum of -1, means to display the whole list */ +void mpd_sendPlaylistInfoCommand(mpd_Connection * connection, int songNum); + +void mpd_sendListallCommand(mpd_Connection * connection, const char * dir); + +void mpd_sendListallInfoCommand(mpd_Connection * connection, const char * dir); + +void mpd_sendLsInfoCommand(mpd_Connection * connection, const char * dir); + +#define MPD_TABLE_ARTIST 0 +#define MPD_TABLE_ALBUM 1 +#define MPD_TABLE_TITLE 2 +#define MPD_TABLE_FILENAME 3 + +void mpd_sendSearchCommand(mpd_Connection * connection, int table, + const char * str); + +void mpd_sendFindCommand(mpd_Connection * connection, int table, + const char * str); + +/* LIST TAG COMMANDS */ + +/* use this function fetch next artist entry, be sure to free the returned + * string. NULL means there are no more. Best used with sendListArtists + */ +char * mpd_getNextArtist(mpd_Connection * connection); + +char * mpd_getNextAlbum(mpd_Connection * connection); + +/* list artist or albums by artist, arg1 should be set to the artist if + * listing albums by a artist, otherwise NULL for listing all artists or albums + */ +void mpd_sendListCommand(mpd_Connection * connection, int table, + const char * arg1); + +void mpd_sendListAlbumsCommand(mpd_Connection * connection, + const char * artist); + +/* SIMPLE COMMANDS */ + +void mpd_sendAddCommand(mpd_Connection * connection, const char * file); + +void mpd_sendDeleteCommand(mpd_Connection * connection, int songNum); + +void mpd_sendSaveCommand(mpd_Connection * connection, const char * name); + +void mpd_sendLoadCommand(mpd_Connection * connection, const char * name); + +void mpd_sendRmCommand(mpd_Connection * connection, const char * name); + +void mpd_sendShuffleCommand(mpd_Connection * connection); + +void mpd_sendClearCommand(mpd_Connection * connection); + +/* use this to start playing at the beginning, useful when in random mode */ +#define MPD_PLAY_AT_BEGINNING -1 + +void mpd_sendPlayCommand(mpd_Connection * connection, int songNum); + +void mpd_sendStopCommand(mpd_Connection * connection); + +void mpd_sendPauseCommand(mpd_Connection * connection); + +void mpd_sendNextCommand(mpd_Connection * connection); + +void mpd_sendPrevCommand(mpd_Connection * connection); + +void mpd_sendMoveCommand(mpd_Connection * connection, int from, int to); + +void mpd_sendSwapCommand(mpd_Connection * connection, int song1, int song2); + +void mpd_sendSeekCommand(mpd_Connection * connection, int song, int time); + +void mpd_sendRepeatCommand(mpd_Connection * connection, int repeatMode); + +void mpd_sendRandomCommand(mpd_Connection * connection, int randomMode); + +void mpd_sendVolumeCommand(mpd_Connection * connection, int volumeChange); + +void mpd_sendUpdateCommand(mpd_Connection * connection); + +/* after executing a command, when your done with it to get its status + * (you want to check connection->error for an error) + */ +void mpd_finishCommand(mpd_Connection * connection); + +/* command list stuff, use this to do things like add files very quickly */ +void mpd_sendCommandListBegin(mpd_Connection * connection); + +void mpd_sendCommandListEnd(mpd_Connection * connection); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/m4/CVS/Entries b/m4/CVS/Entries new file mode 100644 index 0000000..629d8da --- /dev/null +++ b/m4/CVS/Entries @@ -0,0 +1,2 @@ +/glib-2.0.m4/1.1/Tue Sep 9 20:11:05 2003// +D diff --git a/m4/CVS/Repository b/m4/CVS/Repository new file mode 100644 index 0000000..b320242 --- /dev/null +++ b/m4/CVS/Repository @@ -0,0 +1 @@ +apps/mpc-curses/m4 diff --git a/m4/CVS/Root b/m4/CVS/Root new file mode 100644 index 0000000..c7f5064 --- /dev/null +++ b/m4/CVS/Root @@ -0,0 +1 @@ +/usr/global/var/cvs/dev diff --git a/m4/glib-2.0.m4 b/m4/glib-2.0.m4 new file mode 100644 index 0000000..28ccef4 --- /dev/null +++ b/m4/glib-2.0.m4 @@ -0,0 +1,212 @@ +# Configure paths for GLIB +# Owen Taylor 1997-2001 + +dnl AM_PATH_GLIB_2_0([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, MODULES]]]]) +dnl Test for GLIB, and define GLIB_CFLAGS and GLIB_LIBS, if gmodule, gobject or +dnl gthread is specified in MODULES, pass to pkg-config +dnl +AC_DEFUN(AM_PATH_GLIB_2_0, +[dnl +dnl Get the cflags and libraries from pkg-config +dnl +AC_ARG_ENABLE(glibtest, [ --disable-glibtest do not try to compile and run a test GLIB program], + , enable_glibtest=yes) + + pkg_config_args=glib-2.0 + for module in . $4 + do + case "$module" in + gmodule) + pkg_config_args="$pkg_config_args gmodule-2.0" + ;; + gobject) + pkg_config_args="$pkg_config_args gobject-2.0" + ;; + gthread) + pkg_config_args="$pkg_config_args gthread-2.0" + ;; + esac + done + + AC_PATH_PROG(PKG_CONFIG, pkg-config, no) + + no_glib="" + + if test x$PKG_CONFIG != xno ; then + if $PKG_CONFIG --atleast-pkgconfig-version 0.7 ; then + : + else + echo *** pkg-config too old; version 0.7 or better required. + no_glib=yes + PKG_CONFIG=no + fi + else + no_glib=yes + fi + + min_glib_version=ifelse([$1], ,2.0.0,$1) + AC_MSG_CHECKING(for GLIB - version >= $min_glib_version) + + if test x$PKG_CONFIG != xno ; then + ## don't try to run the test against uninstalled libtool libs + if $PKG_CONFIG --uninstalled $pkg_config_args; then + echo "Will use uninstalled version of GLib found in PKG_CONFIG_PATH" + enable_glibtest=no + fi + + if $PKG_CONFIG --atleast-version $min_glib_version $pkg_config_args; then + : + else + no_glib=yes + fi + fi + + if test x"$no_glib" = x ; then + GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0` + GOBJECT_QUERY=`$PKG_CONFIG --variable=gobject_query glib-2.0` + GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0` + + GLIB_CFLAGS=`$PKG_CONFIG --cflags $pkg_config_args` + GLIB_LIBS=`$PKG_CONFIG --libs $pkg_config_args` + glib_config_major_version=`$PKG_CONFIG --modversion glib-2.0 | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + glib_config_minor_version=`$PKG_CONFIG --modversion glib-2.0 | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + glib_config_micro_version=`$PKG_CONFIG --modversion glib-2.0 | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x$enable_glibtest" = "xyes" ; then + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GLIB_CFLAGS" + LIBS="$GLIB_LIBS $LIBS" +dnl +dnl Now check if the installed GLIB is sufficiently new. (Also sanity +dnl checks the results of pkg-config to some extent) +dnl + rm -f conf.glibtest + AC_TRY_RUN([ +#include +#include +#include + +int +main () +{ + int major, minor, micro; + char *tmp_version; + + system ("touch conf.glibtest"); + + /* HP/UX 9 (%@#!) writes to sscanf strings */ + tmp_version = g_strdup("$min_glib_version"); + if (sscanf(tmp_version, "%d.%d.%d", &major, &minor, µ) != 3) { + printf("%s, bad version string\n", "$min_glib_version"); + exit(1); + } + + if ((glib_major_version != $glib_config_major_version) || + (glib_minor_version != $glib_config_minor_version) || + (glib_micro_version != $glib_config_micro_version)) + { + printf("\n*** 'pkg-config --modversion glib-2.0' returned %d.%d.%d, but GLIB (%d.%d.%d)\n", + $glib_config_major_version, $glib_config_minor_version, $glib_config_micro_version, + glib_major_version, glib_minor_version, glib_micro_version); + printf ("*** was found! If pkg-config was correct, then it is best\n"); + printf ("*** to remove the old version of GLib. You may also be able to fix the error\n"); + printf("*** by modifying your LD_LIBRARY_PATH enviroment variable, or by editing\n"); + printf("*** /etc/ld.so.conf. Make sure you have run ldconfig if that is\n"); + printf("*** required on your system.\n"); + printf("*** If pkg-config was wrong, set the environment variable PKG_CONFIG_PATH\n"); + printf("*** to point to the correct configuration files\n"); + } + else if ((glib_major_version != GLIB_MAJOR_VERSION) || + (glib_minor_version != GLIB_MINOR_VERSION) || + (glib_micro_version != GLIB_MICRO_VERSION)) + { + printf("*** GLIB header files (version %d.%d.%d) do not match\n", + GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION); + printf("*** library (version %d.%d.%d)\n", + glib_major_version, glib_minor_version, glib_micro_version); + } + else + { + if ((glib_major_version > major) || + ((glib_major_version == major) && (glib_minor_version > minor)) || + ((glib_major_version == major) && (glib_minor_version == minor) && (glib_micro_version >= micro))) + { + return 0; + } + else + { + printf("\n*** An old version of GLIB (%d.%d.%d) was found.\n", + glib_major_version, glib_minor_version, glib_micro_version); + printf("*** You need a version of GLIB newer than %d.%d.%d. The latest version of\n", + major, minor, micro); + printf("*** GLIB is always available from ftp://ftp.gtk.org.\n"); + printf("***\n"); + printf("*** If you have already installed a sufficiently new version, this error\n"); + printf("*** probably means that the wrong copy of the pkg-config shell script is\n"); + printf("*** being found. The easiest way to fix this is to remove the old version\n"); + printf("*** of GLIB, but you can also set the PKG_CONFIG environment to point to the\n"); + printf("*** correct copy of pkg-config. (In this case, you will have to\n"); + printf("*** modify your LD_LIBRARY_PATH enviroment variable, or edit /etc/ld.so.conf\n"); + printf("*** so that the correct libraries are found at run-time))\n"); + } + } + return 1; +} +],, no_glib=yes,[echo $ac_n "cross compiling; assumed OK... $ac_c"]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + if test "x$no_glib" = x ; then + AC_MSG_RESULT(yes (version $glib_config_major_version.$glib_config_minor_version.$glib_config_micro_version)) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + if test "$PKG_CONFIG" = "no" ; then + echo "*** A new enough version of pkg-config was not found." + echo "*** See http://www.freedesktop.org/software/pkgconfig/" + else + if test -f conf.glibtest ; then + : + else + echo "*** Could not run GLIB test program, checking why..." + ac_save_CFLAGS="$CFLAGS" + ac_save_LIBS="$LIBS" + CFLAGS="$CFLAGS $GLIB_CFLAGS" + LIBS="$LIBS $GLIB_LIBS" + AC_TRY_LINK([ +#include +#include +], [ return ((glib_major_version) || (glib_minor_version) || (glib_micro_version)); ], + [ echo "*** The test program compiled, but did not run. This usually means" + echo "*** that the run-time linker is not finding GLIB or finding the wrong" + echo "*** version of GLIB. If it is not finding GLIB, you'll need to set your" + echo "*** LD_LIBRARY_PATH environment variable, or edit /etc/ld.so.conf to point" + echo "*** to the installed location Also, make sure you have run ldconfig if that" + echo "*** is required on your system" + echo "***" + echo "*** If you have an old version installed, it is best to remove it, although" + echo "*** you may also be able to get things to work by modifying LD_LIBRARY_PATH" ], + [ echo "*** The test program failed to compile or link. See the file config.log for the" + echo "*** exact error that occured. This usually means GLIB is incorrectly installed."]) + CFLAGS="$ac_save_CFLAGS" + LIBS="$ac_save_LIBS" + fi + fi + GLIB_CFLAGS="" + GLIB_LIBS="" + GLIB_GENMARSHAL="" + GOBJECT_QUERY="" + GLIB_MKENUMS="" + ifelse([$3], , :, [$3]) + fi + AC_SUBST(GLIB_CFLAGS) + AC_SUBST(GLIB_LIBS) + AC_SUBST(GLIB_GENMARSHAL) + AC_SUBST(GOBJECT_QUERY) + AC_SUBST(GLIB_MKENUMS) + rm -f conf.glibtest +]) diff --git a/main.c b/main.c new file mode 100644 index 0000000..e915322 --- /dev/null +++ b/main.c @@ -0,0 +1,122 @@ +/* + * $Id: main.c,v 1.5 2004/03/16 14:34:49 kalle Exp $ + * + */ + +#include +#include +#include +#include + +#include "config.h" +#include "libmpdclient.h" +#include "mpc.h" +#include "options.h" +#include "command.h" +#include "screen.h" + + +static mpd_client_t *mpc = NULL; + +void +exit_and_cleanup(void) +{ + screen_exit(); + if( mpc ) + { + if( mpc_error(mpc) ) + fprintf(stderr,"Error: %s\n", mpc_error_str(mpc)); + mpc_close(mpc); + } +} + +void +catch_sigint( int sig ) +{ + printf( "\nExiting...\n"); + exit(EXIT_SUCCESS); +} + +int +main(int argc, char *argv[]) +{ + options_t *options; + struct sigaction act; + int counter; + + /* parse command line options */ + options_init(); + options = options_parse(argc, argv); + + /* setup signal behavior - SIGINT */ + sigemptyset( &act.sa_mask ); + act.sa_flags = 0; + act.sa_handler = catch_sigint; + if( sigaction( SIGINT, &act, NULL )<0 ) + { + perror("signal"); + exit(EXIT_FAILURE); + } + /* setup signal behavior - SIGWINCH */ + sigemptyset( &act.sa_mask ); + act.sa_flags = 0; + act.sa_handler = screen_resized; + if( sigaction( SIGWINCH, &act, NULL )<0 ) + { + perror("sigaction()"); + exit(EXIT_FAILURE); + } + /* setup signal behavior - SIGTERM */ + sigemptyset( &act.sa_mask ); + act.sa_flags = 0; + act.sa_handler = catch_sigint; + if( sigaction( SIGTERM, &act, NULL )<0 ) + { + perror("sigaction()"); + exit(EXIT_FAILURE); + } + + /* set xterm title */ + printf("%c]0;%s%c", '\033', PACKAGE " v" VERSION, '\007'); + + /* install exit function */ + atexit(exit_and_cleanup); + + /* connect to our music player daemon */ + mpc = mpc_connect(options->host, options->port); + if( mpc_error(mpc) ) + exit(EXIT_FAILURE); + + screen_init(); +#if 0 + mpc_update(mpc); + mpc_update_filelist(mpc); + screen_paint(mpc); + // sleep(1); +#endif + + counter=0; + while( !mpc_error(mpc) ) + { + command_t cmd; + + if( !counter ) + { + mpc_update(mpc); + mpd_finishCommand(mpc->connection); + counter=10; + } + else + counter--; + + screen_update(mpc); + + if( (cmd=get_keyboard_command()) != CMD_NONE ) + { + screen_cmd(mpc, cmd); + counter=0; + } + } + + exit(EXIT_FAILURE); +} diff --git a/mpc.c b/mpc.c new file mode 100644 index 0000000..ccb0835 --- /dev/null +++ b/mpc.c @@ -0,0 +1,329 @@ +/* + * $Id: mpc.c,v 1.5 2004/03/17 23:19:21 kalle Exp $ + * + */ + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "support.h" +#include "libmpdclient.h" +#include "mpc.h" +#include "options.h" + +void +mpc_update_song(mpd_client_t *c) +{ + mpd_InfoEntity *entity; + + if( c->song ) + { + mpd_freeSong(c->song); + c->song = NULL; + } + + mpd_sendPlaylistInfoCommand(c->connection, c->status->song); + while( (entity=mpd_getNextInfoEntity(c->connection)) ) + { + mpd_Song *song = entity->info.song; + + if(c->connection->error) + { + fprintf(stderr,"error: %s\n",c->connection->errorStr); + exit(EXIT_FAILURE); + } + if(entity->type!=MPD_INFO_ENTITY_TYPE_SONG) { + mpd_freeInfoEntity(entity); + fprintf(stderr, + "error: type != MPD_INFO_ENTITY_TYPE_SONG [%d]\n", + entity->type); + exit(EXIT_FAILURE); + } + c->song = mpd_songDup(song); + mpd_freeInfoEntity(entity); + } +} + +int +mpc_close(mpd_client_t *c) +{ + if( c->connection ) + mpd_closeConnection(c->connection); + if( c->cwd ) + free( c->cwd ); + + return 0; +} + +mpd_client_t * +mpc_connect(char *host, int port) +{ + mpd_Connection *connection; + mpd_client_t *c; + + connection = mpd_newConnection(host, port, 10); + if( connection==NULL ) + { + fprintf(stderr, "mpd_newConnection to %s:%d failed!\n", host, port); + exit(EXIT_FAILURE); + } + + c = malloc(sizeof(mpd_client_t)); + memset(c, 0, sizeof(mpd_client_t)); + c->connection = connection; + c->cwd = strdup(""); + + return c; +} + +int +mpc_error(mpd_client_t *c) +{ + if( c == NULL || c->connection == NULL ) + return 1; + if( c->connection->error ) + return 1; + + return 0; +} + +char * +mpc_error_str(mpd_client_t *c) +{ + if( c == NULL || c->connection == NULL ) + return "Not connected"; + + if( c->connection && c->connection->errorStr ) + return c->connection->errorStr; + + return NULL; +} + + + +int +mpc_free_playlist(mpd_client_t *c) +{ + GList *list; + + if( c==NULL || c->playlist==NULL ) + return -1; + + list=g_list_first(c->playlist); + + while( list!=NULL ) + { + mpd_Song *song = (mpd_Song *) list->data; + + mpd_freeSong(song); + list=list->next; + } + g_list_free(c->playlist); + c->playlist=NULL; + c->playlist_length=0; + + return 0; +} + +int +mpc_update_playlist(mpd_client_t *c) +{ + mpd_InfoEntity *entity; + + // fprintf(stderr, "mpc_update_playlist(): status->playlist = %d\n", c->status->playlist); + + if( c->playlist ) + mpc_free_playlist(c); + + c->playlist_length=0; + mpd_sendPlaylistInfoCommand(c->connection,-1); + while( (entity=mpd_getNextInfoEntity(c->connection)) ) + { + if(entity->type==MPD_INFO_ENTITY_TYPE_SONG) + { + mpd_Song *song = mpd_songDup(entity->info.song); + + c->playlist = g_list_append(c->playlist, (gpointer) song); + c->playlist_length++; + } + mpd_freeInfoEntity(entity); + } + c->playlist_id = c->status->playlist; + c->playlist_updated = 1; + c->song_id = -1; + + return 0; +} + +int +mpc_playlist_get_song_index(mpd_client_t *c, char *filename) +{ + GList *list = c->playlist; + int i=0; + + while( list ) + { + mpd_Song *song = (mpd_Song *) list->data; + if( strcmp(song->file, filename ) == 0 ) + return i; + list=list->next; + i++; + } + return -1; +} + +mpd_Song * +mpc_playlist_get_song(mpd_client_t *c, int n) +{ + return (mpd_Song *) g_list_nth_data(c->playlist, n); +} + +char * +mpc_get_song_name(mpd_Song *song) +{ + if( song->title ) + { + if( song->artist ) + { + static char buf[512]; + + snprintf(buf, 512, "%s - %s", song->artist, song->title); + return utf8(buf); + } + else + { + return utf8(song->title); + } + } + return utf8(basename(song->file)); +} + +int +mpc_update(mpd_client_t *c) +{ + if( c->status ) + { + mpd_freeStatus(c->status); + } + + c->status = mpd_getStatus(c->connection); + + // if( c->playlist == NULL || c->playlist_id!=c->status->playlist ) + if( c->playlist_id!=c->status->playlist ) + mpc_update_playlist(c); + + // if( c->song == NULL || c->status->song != c->song_id ) + if( c->status->song != c->song_id ) + { + c->song = mpc_playlist_get_song(c, c->status->song); + c->song_id = c->status->song; + c->song_updated = 1; + } + + return 0; +} + + + + + + +int +mpc_free_filelist(mpd_client_t *c) +{ + GList *list; + + if( c==NULL || c->filelist==NULL ) + return -1; + + list=g_list_first(c->filelist); + + while( list!=NULL ) + { + filelist_entry_t *entry = list->data; + + if( entry->entity ) + mpd_freeInfoEntity(entry->entity); + free(entry); + list=list->next; + } + g_list_free(c->filelist); + c->filelist=NULL; + c->filelist_length=0; + + return 0; +} + + + +int +mpc_update_filelist(mpd_client_t *c) +{ + mpd_InfoEntity *entity; + + if( c->filelist ) + mpc_free_filelist(c); + + c->filelist_length=0; + + // mpd_sendListallCommand(conn,""); + mpd_sendLsInfoCommand(c->connection, c->cwd); + + if( c->cwd && c->cwd[0] ) + { + /* add a dummy entry for ./.. */ + filelist_entry_t *entry = malloc(sizeof(filelist_entry_t)); + memset(entry, 0, sizeof(filelist_entry_t)); + entry->entity = NULL; + c->filelist = g_list_append(c->filelist, (gpointer) entry); + c->filelist_length++; + } + + while( (entity=mpd_getNextInfoEntity(c->connection)) ) + { + filelist_entry_t *entry = malloc(sizeof(filelist_entry_t)); + + memset(entry, 0, sizeof(filelist_entry_t)); + entry->entity = entity; + c->filelist = g_list_append(c->filelist, (gpointer) entry); + c->filelist_length++; + } + + c->filelist_updated = 1; + + mpd_finishCommand(c->connection); + + mpc_filelist_set_selected(c); + + return 0; +} + +int +mpc_filelist_set_selected(mpd_client_t *c) +{ + GList *list = c->filelist; + + while( list ) + { + filelist_entry_t *entry = list->data; + mpd_InfoEntity *entity = entry->entity ; + + if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) + { + mpd_Song *song = entity->info.song; + + if( mpc_playlist_get_song_index(c, song->file) >= 0 ) + entry->selected = 1; + else + entry->selected = 0; + } + + list=list->next; + } + return 0; +} diff --git a/mpc.h b/mpc.h new file mode 100644 index 0000000..fb4e678 --- /dev/null +++ b/mpc.h @@ -0,0 +1,45 @@ +typedef struct +{ + char selected; + mpd_InfoEntity *entity; +} filelist_entry_t; + +typedef struct +{ + mpd_Connection *connection; + mpd_Status *status; + + mpd_Song *song; + int song_id; + int song_updated; + + GList *playlist; + int playlist_length; + long long playlist_id; + int playlist_updated; + + char *cwd; + GList *filelist; + int filelist_length; + int filelist_updated; + +} mpd_client_t; + + +int mpc_close(mpd_client_t *c); + +mpd_client_t *mpc_connect(char *host, int port); + +int mpc_update(mpd_client_t *c); +int mpc_update_playlist(mpd_client_t *c); + +int mpc_update_filelist(mpd_client_t *c); +int mpc_filelist_set_selected(mpd_client_t *c); +int mpc_set_cwd(mpd_client_t *c, char *dir); + +mpd_Song *mpc_playlist_get_song(mpd_client_t *c, int n); +char *mpc_get_song_name(mpd_Song *song); +int mpc_playlist_get_song_index(mpd_client_t *c, char *filename); + +int mpc_error(mpd_client_t *c); +char *mpc_error_str(mpd_client_t *c); diff --git a/options.c b/options.c new file mode 100644 index 0000000..33de4c0 --- /dev/null +++ b/options.c @@ -0,0 +1,107 @@ +/* + * $Id: options.c,v 1.7 2004/03/17 11:34:36 kalle Exp $ + * + */ + +#include +#include +#include +#include +#include + +#include "config.h" +#include "options.h" +#include "command.h" + +static options_t options; + +static struct poptOption optionsTable[] = { +#ifdef DEBUG + { "debug", 'D', 0, 0, 'D', "Enable debug output." }, +#endif + { "version", 'V', 0, 0, 'V', "Display version information." }, + { "keys", 'k', 0, 0, 'k', "Display key bindings." }, + { "port", 'p', POPT_ARG_INT, &options.port, 0, + "Connect to server on port [" DEFAULT_PORT_STR "].", "PORT" }, + { "host", 'h', POPT_ARG_STRING, &options.host, 0, + "Connect to server [" DEFAULT_HOST "].", "HOSTNAME" }, + POPT_AUTOHELP + { NULL, 0, 0, NULL, 0 } +}; + +static void +usage(poptContext optCon, int exitcode, char *error, char *addl) +{ + poptPrintUsage(optCon, stderr, 0); + if (error) + fprintf(stderr, "%s: %s0", error, addl); + exit(exitcode); +} + +options_t * +options_parse( int argc, char **argv ) +{ + int c; + poptContext optCon; /* context for parsing command-line options */ + + optCon = poptGetContext(NULL, argc, argv, optionsTable, 0); + while ((c = poptGetNextOpt(optCon)) >= 0) + { + switch (c) + { +#ifdef DEBUG + case 'D': + options.debug = 1; + break; +#endif + case 'V': + printf("Version " VERSION "\n"); + exit(EXIT_SUCCESS); + case 'k': + command_dump_keys(); + exit(EXIT_SUCCESS); + default: + fprintf(stderr, "%s: %s\n", + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + poptFreeContext(optCon); + exit(EXIT_FAILURE); + break; + } + } + if (c < -1) + { + /* an error occurred during option processing */ + fprintf(stderr, "%s: %s\n", + poptBadOption(optCon, POPT_BADOPTION_NOALIAS), + poptStrerror(c)); + poptFreeContext(optCon); + exit(EXIT_FAILURE); + } + + poptFreeContext(optCon); + return &options; +} + +void +options_init( void ) +{ + char *value; + + memset(&options, 0, sizeof(options_t)); + if( (value=getenv(MPD_HOST_ENV)) ) + options.host = strdup(value); + else + options.host = strdup(DEFAULT_HOST); + if( (value=getenv(MPD_PORT_ENV)) ) + options.port = atoi(value); + else + options.port = DEFAULT_PORT; +} + + +options_t * +options_get(void) +{ + return &options; +} diff --git a/options.h b/options.h new file mode 100644 index 0000000..7bf43de --- /dev/null +++ b/options.h @@ -0,0 +1,16 @@ + +#define MPD_HOST_ENV "MPD_HOST" +#define MPD_PORT_ENV "MPD_PORT" + + +typedef struct +{ + char *host; + int port; + +} options_t; + +void options_init(void); +options_t *options_parse(int argc, char **argv); +options_t *get_options(void); + diff --git a/out b/out new file mode 100644 index 0000000..a5b9bbf --- /dev/null +++ b/out @@ -0,0 +1,4 @@ +charset: ANSI_X3.4-1968 +[?25l[?1cMusic Player Client - PlaylistVolume 80% ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄMusic Player Client - PlaylistVolume 80% ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄStopped! Music Player Client - PlaylistVolume 80% ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄStopped!  +Exiting... +[?25h[?0c \ No newline at end of file diff --git a/screen.c b/screen.c new file mode 100644 index 0000000..74244be --- /dev/null +++ b/screen.c @@ -0,0 +1,520 @@ +/* + * $Id: screen.c,v 1.10 2004/03/17 14:50:12 kalle Exp $ + * + */ + +#include +#include +#include +#include +#include +#include + +#include "libmpdclient.h" +#include "mpc.h" +#include "command.h" +#include "screen.h" +#include "screen_play.h" +#include "screen_file.h" +#include "screen_help.h" +#include "screen_search.h" + +#define STATUS_MESSAGE_TIMEOUT 3 + +static screen_t *screen = NULL; + +static void +switch_screen_mode(screen_mode_t new_mode, mpd_client_t *c) +{ + if( new_mode == screen->mode ) + return; + + switch(screen->mode) + { + case SCREEN_PLAY_WINDOW: + play_close(screen, c); + break; + case SCREEN_FILE_WINDOW: + file_close(screen, c); + break; + case SCREEN_SEARCH_WINDOW: + search_close(screen, c); + break; + case SCREEN_HELP_WINDOW: + help_close(screen, c); + break; + } + + screen->mode = new_mode; + screen->painted = 0; + + switch(screen->mode) + { + case SCREEN_PLAY_WINDOW: + play_open(screen, c); + break; + case SCREEN_FILE_WINDOW: + file_open(screen, c); + break; + case SCREEN_SEARCH_WINDOW: + search_open(screen, c); + break; + case SCREEN_HELP_WINDOW: + help_open(screen, c); + break; + } +} + +static void +paint_top_window(char *header, int volume, int clear) +{ + static int prev_volume = -1; + WINDOW *w = screen->top_window.w; + + if(clear) + { + wclear(w); + } + + if(prev_volume!=volume || clear) + { + char buf[12]; + + wattron(w, A_BOLD); + mvwaddstr(w, 0, 0, header ); + wattroff(w, A_BOLD); + if( volume==MPD_STATUS_NO_VOLUME ) + { + snprintf(buf, 12, "Volume n/a "); + } + else + { + snprintf(buf, 12, "Volume %3d%%", volume); + } + mvwaddstr(w, 0, screen->top_window.cols-12, buf); + + mvwhline(w, 1, 0, ACS_HLINE, screen->top_window.cols); + + wrefresh(w); + } +} + +static void +paint_progress_window(mpd_client_t *c) +{ + double p; + int width; + + if( c->status==NULL || !IS_PLAYING(c->status->state) ) + { + mvwhline(screen->progress_window.w, 0, 0, ACS_HLINE, + screen->progress_window.cols); + wrefresh(screen->progress_window.w); + return; + } + + p = ((double) c->status->elapsedTime) / ((double) c->status->totalTime); + + width = (int) (p * (double) screen->progress_window.cols); + + mvwhline(screen->progress_window.w, + 0, 0, + ACS_HLINE, + screen->progress_window.cols); + + whline(screen->progress_window.w, '=', width-1); + + mvwaddch(screen->progress_window.w, 0, width-1, 'O'); + wrefresh(screen->progress_window.w); +} + +static void +paint_status_window(mpd_client_t *c) +{ + WINDOW *w = screen->status_window.w; + mpd_Status *status = c->status; + mpd_Song *song = c->song; + int x = 0; + + if( time(NULL) - screen->status_timestamp <= STATUS_MESSAGE_TIMEOUT ) + return; + + wmove(w, 0, 0); + wclrtoeol(w); + + switch(status->state) + { + case MPD_STATUS_STATE_STOP: + wattron(w, A_BOLD); + waddstr(w, "Stopped! "); + wattroff(w, A_BOLD); + break; + case MPD_STATUS_STATE_PLAY: + waddstr(w, "Playing:"); + break; + case MPD_STATUS_STATE_PAUSE: + wattron(w, A_BOLD); + waddstr(w, "Paused:"); + wattroff(w, A_BOLD); + break; + default: + waddstr(w, "Warning: Music Player Daemon in unknown state!"); + break; + } + x += 10; + + if( IS_PLAYING(status->state) && song ) + { + mvwaddstr(w, 0, x, mpc_get_song_name(song)); + } + + + /* time */ + if( IS_PLAYING(status->state) ) + { + x = screen->status_window.cols - strlen(screen->buf); + + if( c->status->repeat ) + mvwaddstr(w, 0, x-3, ""); + else + mvwaddstr(w, 0, x-3, " "); + + snprintf(screen->buf, screen->buf_size, + " [%i:%02i/%i:%02i] ", + status->elapsedTime/60, status->elapsedTime%60, + status->totalTime/60, status->totalTime%60 ); + mvwaddstr(w, 0, x, screen->buf); + + } + + + wrefresh(w); +} + + + +int +screen_exit(void) +{ + endwin(); + if( screen ) + { + screen->playlist = list_window_free(screen->playlist); + screen->filelist = list_window_free(screen->filelist); + screen->helplist = list_window_free(screen->helplist); + free(screen->buf); + free(screen); + screen = NULL; + } + return 0; +} + +void +screen_resized(int sig) +{ + screen_exit(); + if( COLSstatus_window.w; + + wmove(w, 0, 0); + wclrtoeol(w); + wattron(w, A_BOLD); + waddstr(w, msg); + wattroff(w, A_BOLD); + wrefresh(w); + screen->status_timestamp = time(NULL); +} + +int +screen_init(void) +{ + /* initialize the curses library */ + initscr(); + start_color(); + use_default_colors(); + /* tell curses not to do NL->CR/NL on output */ + nonl(); + /* take input chars one at a time, no wait for \n */ + cbreak(); + /* don't echo input */ + noecho(); + /* set cursor invisible */ + curs_set(0); + /* return from getch() without blocking */ + // nodelay(stdscr, TRUE); + keypad(stdscr, TRUE); + timeout(100); /*void wtimeout(WINDOW *win, int delay);*/ + + + if( COLSmode = SCREEN_PLAY_WINDOW; + screen->cols = COLS; + screen->rows = LINES; + screen->buf = malloc(screen->cols); + screen->buf_size = screen->cols; + screen->painted = 0; + + /* create top window */ + screen->top_window.rows = 2; + screen->top_window.cols = screen->cols; + screen->top_window.w = newwin(screen->top_window.rows, + screen->top_window.cols, + 0, 0); + leaveok(screen->top_window.w, TRUE); + keypad(screen->top_window.w, TRUE); + + /* create main window */ + screen->main_window.rows = screen->rows-4; + screen->main_window.cols = screen->cols; + screen->main_window.w = newwin(screen->main_window.rows, + screen->main_window.cols, + 2, + 0); + screen->playlist = list_window_init( screen->main_window.w, + screen->main_window.cols, + screen->main_window.rows ); + screen->filelist = list_window_init( screen->main_window.w, + screen->main_window.cols, + screen->main_window.rows ); + screen->helplist = list_window_init( screen->main_window.w, + screen->main_window.cols, + screen->main_window.rows ); + leaveok(screen->main_window.w, TRUE); + keypad(screen->main_window.w, TRUE); + + /* create progress window */ + screen->progress_window.rows = 1; + screen->progress_window.cols = screen->cols; + screen->progress_window.w = newwin(screen->progress_window.rows, + screen->progress_window.cols, + screen->rows-2, + 0); + leaveok(screen->progress_window.w, TRUE); + + /* create status window */ + screen->status_window.rows = 1; + screen->status_window.cols = screen->cols; + screen->status_window.w = newwin(screen->status_window.rows, + screen->status_window.cols, + screen->rows-1, + 0); + leaveok(screen->status_window.w, FALSE); + keypad(screen->status_window.w, TRUE); + + return 0; +} + +void +screen_paint(mpd_client_t *c) +{ + switch(screen->mode) + { + case SCREEN_PLAY_WINDOW: + paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1); + play_paint(screen, c); + break; + case SCREEN_FILE_WINDOW: + paint_top_window(file_get_header(c), c->status->volume, 1); + file_paint(screen, c); + break; + case SCREEN_SEARCH_WINDOW: + paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 1); + search_paint(screen, c); + break; + case SCREEN_HELP_WINDOW: + paint_top_window(TOP_HEADER_PLAY, c->status->volume, 1); + help_paint(screen, c); + break; + } + + paint_progress_window(c); + paint_status_window(c); + screen->painted = 1; +} + +void +screen_update(mpd_client_t *c) +{ + if( !screen->painted ) + return screen_paint(c); + + switch(screen->mode) + { + case SCREEN_PLAY_WINDOW: + paint_top_window(TOP_HEADER_PLAY, c->status->volume, 0); + play_update(screen, c); + break; + case SCREEN_FILE_WINDOW: + paint_top_window(file_get_header(c), c->status->volume, 0); + file_update(screen, c); + break; + case SCREEN_SEARCH_WINDOW: + paint_top_window(TOP_HEADER_SEARCH, c->status->volume, 0); + search_update(screen, c); + break; + case SCREEN_HELP_WINDOW: + paint_top_window(TOP_HEADER_HELP, c->status->volume, 0); + help_update(screen, c); + break; + } + paint_progress_window(c); + paint_status_window(c); +} + +void +screen_cmd(mpd_client_t *c, command_t cmd) +{ + int n; + char buf[256]; + screen_mode_t new_mode = screen->mode; + + switch(screen->mode) + { + case SCREEN_PLAY_WINDOW: + if( play_cmd(screen, c, cmd) ) + return; + break; + case SCREEN_FILE_WINDOW: + if( file_cmd(screen, c, cmd) ) + return; + break; + case SCREEN_SEARCH_WINDOW: + if( search_cmd(screen, c, cmd) ) + return; + break; + case SCREEN_HELP_WINDOW: + if( help_cmd(screen, c, cmd) ) + return; + break; + } + + switch(cmd) + { + case CMD_PLAY: + mpd_sendPlayCommand(c->connection, screen->playlist->selected); + mpd_finishCommand(c->connection); + break; + case CMD_PAUSE: + mpd_sendPauseCommand(c->connection); + mpd_finishCommand(c->connection); + break; + case CMD_STOP: + mpd_sendStopCommand(c->connection); + mpd_finishCommand(c->connection); + break; + case CMD_TRACK_NEXT: + if( IS_PLAYING(c->status->state) ) + { + mpd_sendNextCommand(c->connection); + mpd_finishCommand(c->connection); + } + break; + case CMD_TRACK_PREVIOUS: + if( IS_PLAYING(c->status->state) ) + { + mpd_sendPrevCommand(c->connection); + mpd_finishCommand(c->connection); + } + break; + case CMD_SHUFFLE: + mpd_sendShuffleCommand(c->connection); + mpd_finishCommand(c->connection); + screen_status_message(c, "Shuffled playlist!"); + break; + case CMD_CLEAR: + mpd_sendClearCommand(c->connection); + mpd_finishCommand(c->connection); + file_clear_highlights(c); + screen_status_message(c, "Cleared playlist!"); + break; + case CMD_REPEAT: + n = !c->status->repeat; + mpd_sendRepeatCommand(c->connection, n); + mpd_finishCommand(c->connection); + snprintf(buf, 256, "Repeat is %s", n ? "On" : "Off"); + screen_status_message(c, buf); + break; + case CMD_RANDOM: + n = !c->status->random; + mpd_sendRandomCommand(c->connection, n); + mpd_finishCommand(c->connection); + snprintf(buf, 256, "Random is %s", n ? "On" : "Off"); + screen_status_message(c, buf); + break; + case CMD_VOLUME_UP: + mpd_sendVolumeCommand(c->connection, 1); + mpd_finishCommand(c->connection); + if( c->status->volume!=MPD_STATUS_NO_VOLUME ) + { + snprintf(buf, 256, "Volume %d%%", c->status->volume+1); + screen_status_message(c, buf); + } + break; + case CMD_VOLUME_DOWN: + mpd_sendVolumeCommand(c->connection, -1); + mpd_finishCommand(c->connection); + if( c->status->volume!=MPD_STATUS_NO_VOLUME ) + { + snprintf(buf, 256, "Volume %d%%", c->status->volume-1); + screen_status_message(c, buf); + } + break; + case CMD_SCREEN_PREVIOUS: + if( screen->mode > SCREEN_PLAY_WINDOW ) + new_mode = screen->mode - 1; + else + new_mode = SCREEN_HELP_WINDOW-1; + switch_screen_mode(new_mode, c); + break; + case CMD_SCREEN_NEXT: + new_mode = screen->mode + 1; + if( new_mode >= SCREEN_HELP_WINDOW ) + new_mode = SCREEN_PLAY_WINDOW; + switch_screen_mode(new_mode, c); + break; + case CMD_SCREEN_PLAY: + switch_screen_mode(SCREEN_PLAY_WINDOW, c); + break; + case CMD_SCREEN_FILE: + switch_screen_mode(SCREEN_FILE_WINDOW, c); + break; + case CMD_SCREEN_SEARCH: + switch_screen_mode(SCREEN_SEARCH_WINDOW, c); + break; + case CMD_SCREEN_HELP: + switch_screen_mode(SCREEN_HELP_WINDOW, c); + break; + case CMD_QUIT: + exit(EXIT_SUCCESS); + case CMD_NONE: + case CMD_DELETE: + case CMD_SELECT: + case CMD_LIST_PREVIOUS: + case CMD_LIST_NEXT: + case CMD_LIST_FIRST: + case CMD_LIST_LAST: + case CMD_LIST_NEXT_PAGE: + case CMD_LIST_PREVIOUS_PAGE: + break; + } + +} + + diff --git a/screen.h b/screen.h new file mode 100644 index 0000000..3512424 --- /dev/null +++ b/screen.h @@ -0,0 +1,71 @@ +#ifndef SCREEN_H +#define SCREEN_H +#include +#include "screen_utils.h" + +#define TOP_HEADER_PREFIX "Music Player Client - " +#define TOP_HEADER_PLAY TOP_HEADER_PREFIX "Playlist" +#define TOP_HEADER_FILE TOP_HEADER_PREFIX "Browse" +#define TOP_HEADER_HELP TOP_HEADER_PREFIX "Help" +#define TOP_HEADER_SEARCH TOP_HEADER_PREFIX "Search" + +#define SCREEN_MIN_COLS 14 +#define SCREEN_MIN_ROWS 5 + +#define IS_PLAYING(s) (s==MPD_STATUS_STATE_PLAY) + +typedef enum +{ + SCREEN_PLAY_WINDOW = 0, + SCREEN_FILE_WINDOW, + SCREEN_HELP_WINDOW, + SCREEN_SEARCH_WINDOW + +} screen_mode_t; + + + +typedef struct +{ + WINDOW *w; + int rows, cols; + +} window_t; + + + +typedef struct +{ + window_t top_window; + window_t main_window; + window_t progress_window; + window_t status_window; + time_t status_timestamp; + + list_window_t *playlist; + list_window_t *filelist; + list_window_t *helplist; + + int cols, rows; + + screen_mode_t mode; + + char *buf; + size_t buf_size; + + int painted; + +} screen_t; + + + +int screen_init(void); +int screen_exit(void); +void screen_resized(int sig); +void screen_status_message(mpd_client_t *c, char *msg); +char *screen_error(void); +void screen_paint(mpd_client_t *c); +void screen_update(mpd_client_t *c); +void screen_cmd(mpd_client_t *c, command_t cmd); + +#endif diff --git a/screen_file.c b/screen_file.c new file mode 100644 index 0000000..bd60872 --- /dev/null +++ b/screen_file.c @@ -0,0 +1,333 @@ +/* + * $Id: screen_file.c,v 1.9 2004/03/18 09:33:07 kalle Exp $ + * + */ + +#include +#include +#include +#include + +#include "config.h" +#include "support.h" +#include "libmpdclient.h" +#include "mpc.h" +#include "command.h" +#include "screen.h" +#include "screen_file.h" + + +static char * +list_callback(int index, int *highlight, void *data) +{ + mpd_client_t *c = (mpd_client_t *) data; + filelist_entry_t *entry; + mpd_InfoEntity *entity; + + *highlight = 0; + if( (entry=(filelist_entry_t *) g_list_nth_data(c->filelist, index))==NULL ) + return NULL; + + entity = entry->entity; + *highlight = entry->selected; + + if( entity == NULL ) + { + return "[Back]"; + } + if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) + { + + mpd_Directory *dir = entity->info.directory; + + return utf8(basename(dir->path)); + } + else if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) + { + mpd_Song *song = entity->info.song; + return mpc_get_song_name(song); + } + + return NULL; +} + +static void +change_directory(screen_t *screen, mpd_client_t *c) +{ + list_window_t *w = screen->filelist; + filelist_entry_t *entry; + mpd_InfoEntity *entity; + + entry = ( filelist_entry_t *) g_list_nth_data(c->filelist, w->selected); + if( entry==NULL ) + return; + + entity = entry->entity; + if( entity==NULL ) + { + char *parent = g_path_get_dirname(c->cwd); + + if( strcmp(parent,".") == 0 ) + { + parent[0] = '\0'; + } + if( c->cwd ) + free(c->cwd); + c->cwd = parent; + } + else + if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY) + { + mpd_Directory *dir = entity->info.directory; + if( c->cwd ) + free(c->cwd); + c->cwd = strdup(dir->path); + } + else + return; + + mpc_update_filelist(c); + list_window_reset(w); +} + + +static int +add_directory(mpd_client_t *c, char *dir) +{ + mpd_InfoEntity *entity; + GList *subdir_list = NULL; + GList *list = NULL; + char buf[80]; + + snprintf(buf, 80, "Adding directory %s...\n", dir); + screen_status_message(c, buf); + + mpd_sendLsInfoCommand(c->connection, dir); + mpd_sendCommandListBegin(c->connection); + while( (entity=mpd_getNextInfoEntity(c->connection)) ) + { + if( entity->type==MPD_INFO_ENTITY_TYPE_SONG ) + { + mpd_Song *song = entity->info.song; + mpd_sendAddCommand(c->connection, song->file); + mpd_freeInfoEntity(entity); + } + else if( entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) + { + subdir_list = g_list_append(subdir_list, (gpointer) entity); + } + else + mpd_freeInfoEntity(entity); + } + mpd_sendCommandListEnd(c->connection); + mpd_finishCommand(c->connection); + + list = g_list_first(subdir_list); + while( list!=NULL ) + { + mpd_Directory *dir; + + entity = list->data; + dir = entity->info.directory; + add_directory(c, dir->path); + mpd_freeInfoEntity(entity); + list->data=NULL; + list=list->next; + } + g_list_free(subdir_list); + return 0; +} + +static void +select_entry(screen_t *screen, mpd_client_t *c) +{ + list_window_t *w = screen->filelist; + filelist_entry_t *entry; + + entry = ( filelist_entry_t *) g_list_nth_data(c->filelist, w->selected); + if( entry==NULL || entry->entity==NULL) + return; + + if( entry->entity->type==MPD_INFO_ENTITY_TYPE_DIRECTORY ) + { + mpd_Directory *dir = entry->entity->info.directory; + add_directory(c, dir->path); + return; + } + + if( entry->entity->type!=MPD_INFO_ENTITY_TYPE_SONG ) + return; /* No support for adding dirs... :( */ + + entry->selected = !entry->selected; + + if( entry->selected ) + { + if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG ) + { + char buf[80]; + mpd_Song *song = entry->entity->info.song; + + mpd_sendAddCommand(c->connection, song->file); + mpd_finishCommand(c->connection); + + snprintf(buf, 80, + "Adding \'%s\' to playlist\n", + mpc_get_song_name(song)); + screen_status_message(c, buf); + } + } + else + { + if( entry->entity->type==MPD_INFO_ENTITY_TYPE_SONG ) + { + int i; + char buf[80]; + mpd_Song *song = entry->entity->info.song; + + i = mpc_playlist_get_song_index(c, song->file); + if( i>=0 ) + { + mpd_sendDeleteCommand(c->connection, i); + mpd_finishCommand(c->connection); + snprintf(buf, 80, + "Removed \'%s\' from playlist\n", + mpc_get_song_name(song)); + screen_status_message(c, buf); + } + } + } + +} + +void +file_clear_highlights(mpd_client_t *c) +{ + GList *list = g_list_first(c->filelist); + + while( list ) + { + filelist_entry_t *entry = list->data; + + entry->selected = 0; + list = list->next; + } +} + +void +file_clear_highlight(mpd_client_t *c, mpd_Song *song) +{ + GList *list = g_list_first(c->filelist); + + if( !song ) + return; + + while( list ) + { + filelist_entry_t *entry = list->data; + mpd_InfoEntity *entity = entry->entity; + + if( entity && entity->type==MPD_INFO_ENTITY_TYPE_SONG ) + { + mpd_Song *song2 = entity->info.song; + + if( strcmp(song->file, song2->file) == 0 ) + { + entry->selected = 0; + } + } + list = list->next; + } +} + +char * +file_get_header(mpd_client_t *c) +{ + static char buf[64]; + + snprintf(buf, 64, + TOP_HEADER_FILE ": %s ", + basename(c->cwd) + ); + + return buf; +} + +void +file_open(screen_t *screen, mpd_client_t *c) +{ + if( c->filelist == NULL ) + { + mpc_update_filelist(c); + } +} + +void +file_close(screen_t *screen, mpd_client_t *c) +{ +} + +void +file_paint(screen_t *screen, mpd_client_t *c) +{ + list_window_t *w = screen->filelist; + + w->clear = 1; + + list_window_paint(screen->filelist, list_callback, (void *) c); + wrefresh(screen->filelist->w); +} + +void +file_update(screen_t *screen, mpd_client_t *c) +{ + if( c->filelist_updated ) + { + file_paint(screen, c); + c->filelist_updated = 0; + return; + } + list_window_paint(screen->filelist, list_callback, (void *) c); + wrefresh(screen->filelist->w); +} + + +int +file_cmd(screen_t *screen, mpd_client_t *c, command_t cmd) +{ + switch(cmd) + { + case CMD_PLAY: + change_directory(screen, c); + break; + case CMD_LIST_PREVIOUS: + list_window_previous(screen->filelist); + screen->filelist->repaint=1; + break; + case CMD_SELECT: + select_entry(screen, c); + /* continue and select next item... */ + case CMD_LIST_NEXT: + list_window_next(screen->filelist, c->filelist_length); + screen->filelist->repaint=1; + break; + case CMD_LIST_FIRST: + list_window_first(screen->filelist); + screen->filelist->repaint = 1; + break; + case CMD_LIST_LAST: + list_window_last(screen->filelist, c->filelist_length); + screen->filelist->repaint = 1; + break; + case CMD_LIST_NEXT_PAGE: + list_window_next_page(screen->filelist, c->filelist_length); + screen->filelist->repaint = 1; + break; + case CMD_LIST_PREVIOUS_PAGE: + list_window_previous_page(screen->filelist); + screen->filelist->repaint = 1; + break; + default: + return 0; + } + return 1; +} diff --git a/screen_file.h b/screen_file.h new file mode 100644 index 0000000..2d5ab5e --- /dev/null +++ b/screen_file.h @@ -0,0 +1,14 @@ + +char *file_get_header(mpd_client_t *c); + +void file_clear_highlight(mpd_client_t *c, mpd_Song *song); +void file_clear_highlights(mpd_client_t *c); + +void file_open(screen_t *screen, mpd_client_t *c); +void file_close(screen_t *screen, mpd_client_t *c); + +void file_paint(screen_t *screen, mpd_client_t *c); +void file_update(screen_t *screen, mpd_client_t *c); + +int file_cmd(screen_t *screen, mpd_client_t *c, command_t cmd); + diff --git a/screen_help.c b/screen_help.c new file mode 100644 index 0000000..d38e4a6 --- /dev/null +++ b/screen_help.c @@ -0,0 +1,165 @@ +/* + * $Id: screen_help.c,v 1.8 2004/03/17 13:40:25 kalle Exp $ + * + */ + +#include +#include +#include +#include + +#include "config.h" +#include "libmpdclient.h" +#include "mpc.h" +#include "command.h" +#include "screen.h" +#include "screen_help.h" + +typedef struct +{ + char highlight; + command_t command; + char *text; +} help_text_row_t; + +static help_text_row_t help_text[] = +{ + { 1, CMD_NONE, " Keys " }, + { 0, CMD_NONE, " --------" }, + { 0, CMD_STOP, "Stop" }, + { 0, CMD_PAUSE, "Pause" }, + { 0, CMD_TRACK_NEXT, "Next track" }, + { 0, CMD_TRACK_PREVIOUS, "Prevoius track (back)" }, + { 0, CMD_VOLUME_DOWN, "Volume down" }, + { 0, CMD_VOLUME_UP, "Volume up" }, + { 0, CMD_NONE, " " }, + { 0, CMD_LIST_NEXT, "Move cursor up" }, + { 0, CMD_LIST_PREVIOUS, "Move cursor down" }, + { 0, CMD_SCREEN_NEXT, "Change screen" }, + { 0, CMD_SCREEN_HELP, "Help screen" }, + { 0, CMD_SCREEN_PLAY, "Playlist screen" }, + { 0, CMD_SCREEN_FILE, "Browse screen" }, + { 0, CMD_QUIT, "Quit" }, + { 0, CMD_NONE, " " }, + { 0, CMD_NONE, " " }, + { 1, CMD_NONE, " Keys - Playlist screen " }, + { 0, CMD_NONE, " --------------------------" }, + { 0, CMD_PLAY, "Play selected entry" }, + { 0, CMD_DELETE, "Delete selected entry from platlist" }, + { 0, CMD_SHUFFLE, "Shuffle playlist" }, + { 0, CMD_CLEAR, "Clear playlist" }, + { 0, CMD_REPEAT, "Toggle repeat mode" }, + { 0, CMD_RANDOM, "Toggle random mode" }, + { 0, CMD_NONE, " " }, + { 0, CMD_NONE, " " }, + { 1, CMD_NONE, " Keys - Browse screen " }, + { 0, CMD_NONE, " ------------------------" }, + { 0, CMD_PLAY, "Change to selected directory" }, + { 0, CMD_SELECT, "Add/Remove selected file" }, + { 0, CMD_NONE, " " }, + { 0, CMD_NONE, " " }, + { 1, CMD_NONE, " " PACKAGE " version " VERSION }, + { 0, CMD_NONE, NULL } +}; + +static int help_text_rows = -1; + + + +static char * +list_callback(int index, int *highlight, void *data) +{ + static char buf[256]; + + if( help_text_rows<0 ) + { + help_text_rows = 0; + while( help_text[help_text_rows].text ) + help_text_rows++; + } + + *highlight = 0; + if( indexhelplist; + + w->clear = 1; + list_window_paint(screen->helplist, list_callback, NULL); + wrefresh(screen->helplist->w); +} + +void +help_update(screen_t *screen, mpd_client_t *c) +{ + list_window_t *w = screen->helplist; + + if( w->repaint ) + { + list_window_paint(screen->helplist, list_callback, NULL); + wrefresh(screen->helplist->w); + w->repaint = 0; + } +} + + +int +help_cmd(screen_t *screen, mpd_client_t *c, command_t cmd) +{ + switch(cmd) + { + case CMD_LIST_PREVIOUS: + list_window_previous(screen->helplist); + screen->helplist->repaint=1; + break; + case CMD_LIST_NEXT: + list_window_next(screen->helplist, help_text_rows); + screen->helplist->repaint=1; + break; + case CMD_LIST_FIRST: + list_window_first(screen->helplist); + screen->helplist->repaint = 1; + break; + case CMD_LIST_LAST: + list_window_last(screen->helplist, help_text_rows); + screen->helplist->repaint = 1; + break; + case CMD_LIST_PREVIOUS_PAGE: + list_window_previous_page(screen->helplist); + screen->helplist->repaint = 1; + break; + case CMD_LIST_NEXT_PAGE: + list_window_next_page(screen->helplist, help_text_rows); + screen->helplist->repaint = 1; + break; + default: + return 0; + } + return 1; +} diff --git a/screen_help.h b/screen_help.h new file mode 100644 index 0000000..b6434dd --- /dev/null +++ b/screen_help.h @@ -0,0 +1,9 @@ + +void help_open(screen_t *screen, mpd_client_t *c); +void help_close(screen_t *screen, mpd_client_t *c); + +void help_paint(screen_t *screen, mpd_client_t *c); +void help_update(screen_t *screen, mpd_client_t *c); + +int help_cmd(screen_t *screen, mpd_client_t *c, command_t cmd); + diff --git a/screen_play.c b/screen_play.c new file mode 100644 index 0000000..161e611 --- /dev/null +++ b/screen_play.c @@ -0,0 +1,129 @@ +/* + * $Id: screen_play.c,v 1.6 2004/03/17 14:49:31 kalle Exp $ + * + */ + +#include +#include +#include +#include + +#include "libmpdclient.h" +#include "mpc.h" +#include "command.h" +#include "screen.h" +#include "screen_file.h" +#include "screen_play.h" + + +#define BUFSIZE 256 + +static char * +list_callback(int index, int *highlight, void *data) +{ + mpd_client_t *c = (mpd_client_t *) data; + mpd_Song *song; + + *highlight = 0; + if( (song=mpc_playlist_get_song(c, index)) == NULL ) + { + return NULL; + } + + if( IS_PLAYING(c->status->state) && index==c->song_id ) + { + *highlight = 1; + } + + return mpc_get_song_name(song); +} + +void +play_open(screen_t *screen, mpd_client_t *c) +{ + +} + +void +play_close(screen_t *screen, mpd_client_t *c) +{ +} + +void +play_paint(screen_t *screen, mpd_client_t *c) +{ + list_window_t *w = screen->playlist; + + w->clear = 1; + + list_window_paint(screen->playlist, list_callback, (void *) c); + wrefresh(screen->playlist->w); +} + +void +play_update(screen_t *screen, mpd_client_t *c) +{ + if( c->playlist_updated ) + { + if( screen->playlist->selected >= c->playlist_length ) + screen->playlist->selected = c->playlist_length-1; + if( screen->playlist->start >= c->playlist_length ) + list_window_reset(screen->playlist); + + play_paint(screen, c); + c->playlist_updated = 0; + } + else if( screen->playlist->repaint || 1) + { + list_window_paint(screen->playlist, list_callback, (void *) c); + wrefresh(screen->playlist->w); + screen->playlist->repaint = 0; + } +} + +int +play_cmd(screen_t *screen, mpd_client_t *c, command_t cmd) +{ + char buf[256]; + mpd_Song *song; + + switch(cmd) + { + case CMD_DELETE: + song = mpc_playlist_get_song(c, screen->playlist->selected); + file_clear_highlight(c, song); + mpd_sendDeleteCommand(c->connection, screen->playlist->selected); + mpd_finishCommand(c->connection); + snprintf(buf, 256, + "Removed \'%s\' from playlist!", + mpc_get_song_name(song)); + screen_status_message(c, buf); + break; + case CMD_LIST_PREVIOUS: + list_window_previous(screen->playlist); + screen->playlist->repaint=1; + break; + case CMD_LIST_NEXT: + list_window_next(screen->playlist, c->playlist_length); + screen->playlist->repaint=1; + break; + case CMD_LIST_FIRST: + list_window_first(screen->playlist); + screen->playlist->repaint = 1; + break; + case CMD_LIST_LAST: + list_window_last(screen->playlist, c->playlist_length); + screen->playlist->repaint = 1; + case CMD_LIST_NEXT_PAGE: + list_window_next_page(screen->playlist, c->playlist_length); + screen->playlist->repaint = 1; + break; + case CMD_LIST_PREVIOUS_PAGE: + list_window_previous_page(screen->playlist); + screen->playlist->repaint = 1; + break; + default: + return 0; + } + return 1; +} diff --git a/screen_play.h b/screen_play.h new file mode 100644 index 0000000..c88f4c7 --- /dev/null +++ b/screen_play.h @@ -0,0 +1,10 @@ + + +void play_open(screen_t *screen, mpd_client_t *c); +void play_close(screen_t *screen, mpd_client_t *c); + +void play_paint(screen_t *screen, mpd_client_t *c); +void play_update(screen_t *screen, mpd_client_t *c); + +int play_cmd(screen_t *screen, mpd_client_t *c, command_t cmd); + diff --git a/screen_search.c b/screen_search.c new file mode 100644 index 0000000..cb975a8 --- /dev/null +++ b/screen_search.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include + +#include "libmpdclient.h" +#include "config.h" +#include "mpc.h" +#include "command.h" +#include "screen.h" +#include "screen_search.h" + +void +search_open(screen_t *screen, mpd_client_t *c) +{ +} + +void +search_close(screen_t *screen, mpd_client_t *c) +{ +} + +void +search_paint(screen_t *screen, mpd_client_t *c) +{ +} + +void +search_update(screen_t *screen, mpd_client_t *c) +{ +} + +int +search_cmd(screen_t *screen, mpd_client_t *c, command_t cmd) +{ + return 0; +} diff --git a/screen_search.h b/screen_search.h new file mode 100644 index 0000000..b2b7f51 --- /dev/null +++ b/screen_search.h @@ -0,0 +1,8 @@ + +void search_open(screen_t *screen, mpd_client_t *c); +void search_close(screen_t *screen, mpd_client_t *c); + +void search_paint(screen_t *screen, mpd_client_t *c); +void search_update(screen_t *screen, mpd_client_t *c); + +int search_cmd(screen_t *screen, mpd_client_t *c, command_t cmd); diff --git a/screen_utils.c b/screen_utils.c new file mode 100644 index 0000000..e100cbf --- /dev/null +++ b/screen_utils.c @@ -0,0 +1,152 @@ +/* + * $Id: screen_utils.c,v 1.4 2004/03/16 13:57:24 kalle Exp $ + * + */ + +#include +#include +#include +#include + +#include "libmpdclient.h" +#include "mpc.h" +#include "command.h" +#include "screen.h" + + + +list_window_t * +list_window_init(WINDOW *w, int width, int height) +{ + list_window_t *lw; + + lw = malloc(sizeof(list_window_t)); + memset(lw, 0, sizeof(list_window_t)); + lw->w = w; + lw->cols = width; + lw->rows = height; + lw->clear = 1; + return lw; +} + +list_window_t * +list_window_free(list_window_t *lw) +{ + if( lw ) + { + memset(lw, 0, sizeof(list_window_t)); + free(lw); + } + return NULL; +} + +void +list_window_reset(list_window_t *lw) +{ + lw->selected = 0; + lw->start = 0; + lw->clear = 1; +} + +void +list_window_set_selected(list_window_t *lw, int n) +{ + lw->selected=n; +} + +void +list_window_paint(list_window_t *lw, + list_window_callback_fn_t callback, + void *callback_data) +{ + int i; + + while( lw->selected < lw->start ) + { + lw->start--; + lw->clear=1; + } + while( lw->selected >= lw->start+lw->rows ) + { + lw->start++; + lw->clear=1; + } + if( lw->clear ) + { + wclear(lw->w); + lw->clear=0; + } + + for(i=0; irows; i++) + { + int highlight; + char *label; + + label = (callback) (lw->start+i, &highlight, callback_data); + if( label ) + { + wmove(lw->w, i, 0); + if( highlight ) + wattron(lw->w, A_BOLD); + if( lw->start+i == lw->selected ) + wattron(lw->w, A_REVERSE); + + waddnstr(lw->w, label, lw->cols); + + if( highlight ) + wattroff(lw->w, A_BOLD); + if( lw->start+i == lw->selected ) + wattroff(lw->w, A_REVERSE); + } + } +} + +void +list_window_next(list_window_t *lw, int length) +{ + if( lw->selected < length-1 ) + lw->selected++; +} + +void +list_window_previous(list_window_t *lw) +{ + if( lw->selected > 0 ) + lw->selected--; +} + +void +list_window_first(list_window_t *lw) +{ + lw->selected = 0; +} + +void +list_window_last(list_window_t *lw, int length) +{ + lw->selected = length-1; +} + +void +list_window_next_page(list_window_t *lw, int length) +{ + int step = lw->rows-1; + if( step<= 0 ) + return; + if( lw->selected+step < length-1 ) + lw->selected+=step; + else + return list_window_last(lw,length); +} + +void +list_window_previous_page(list_window_t *lw) +{ + int step = lw->rows-1; + if( step<= 0 ) + return; + if( lw->selected-step > 0 ) + lw->selected-=step; + else + list_window_first(lw); +} diff --git a/screen_utils.h b/screen_utils.h new file mode 100644 index 0000000..7143832 --- /dev/null +++ b/screen_utils.h @@ -0,0 +1,42 @@ + +typedef char * (*list_window_callback_fn_t) (int index, + int *highlight, + void *data); + + + +typedef struct +{ + WINDOW *w; + int rows, cols; + + int start; + int selected; + int clear; + int repaint; + +} list_window_t; + + + +list_window_t *list_window_init(WINDOW *w, int width, int height); +list_window_t *list_window_free(list_window_t *lw); + + +void list_window_reset(list_window_t *lw); +void list_window_set_selected(list_window_t *lw, int n); + +void list_window_paint(list_window_t *lw, + list_window_callback_fn_t callback, + void *callback_data); + + + + +void list_window_previous(list_window_t *lw); +void list_window_next(list_window_t *lw, int length); +void list_window_first(list_window_t *lw); +void list_window_last(list_window_t *lw, int length); +void list_window_previous_page(list_window_t *lw); +void list_window_next_page(list_window_t *lw, int length); + diff --git a/support.c b/support.c new file mode 100644 index 0000000..ce67dc8 --- /dev/null +++ b/support.c @@ -0,0 +1,64 @@ +/* + * $Id: support.c,v 1.2 2004/03/17 23:17:09 kalle Exp $ + * + */ + +#include +#include +#include +#include + +#include "config.h" +#include "support.h" + +#ifndef HAVE_LIBGEN_H + +char * +remove_trailing_slash(char *path) +{ + int len; + + if( path==NULL ) + return NULL; + + len=strlen(path); + if( len>1 && path[len-1] == '/' ) + path[len-1] = '\0'; + + return path; +} + + +char * +basename(char *path) +{ + char *end; + + path = remove_trailing_slash(path); + end = path + strlen(path); + + while( end>path && *end!='/' ) + end--; + + if( *end=='/' && end!=path ) + return end+1; + + return path; +} + +#endif /* HAVE_LIBGEN_H */ + +char * +utf8(char *str) +{ + static const gchar *charset = NULL; + static gboolean locale_is_utf8 = FALSE; + + if( !charset ) + locale_is_utf8 = g_get_charset(&charset); + + if( locale_is_utf8 ) + return str; + + return g_locale_from_utf8(str, -1, NULL, NULL, NULL); +} diff --git a/support.h b/support.h new file mode 100644 index 0000000..834dd8c --- /dev/null +++ b/support.h @@ -0,0 +1,9 @@ + +#ifdef HAVE_LIBGEN_H +#include +#else +char *basename(char *path); +#endif + +char *utf8(char *str); + -- 2.30.2