From: Kalle Wallin Date: Fri, 19 Mar 2004 13:26:18 +0000 (+0000) Subject: Imported ncmpc (mpc-ncures). X-Git-Tag: v0.12_alpha1~696 X-Git-Url: https://git.tokkee.org/?a=commitdiff_plain;h=a0592c8cc08b802f86061c88dc6862352b6c3e94;p=ncmpc.git Imported ncmpc (mpc-ncures). git-svn-id: https://svn.musicpd.org/ncmpc/trunk@292 09075e82-0dd4-0310-85a5-a0d7c8717e4f --- a0592c8cc08b802f86061c88dc6862352b6c3e94 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); +