Code

Imported ncmpc (mpc-ncures).
authorKalle Wallin <kaw@linux.se>
Fri, 19 Mar 2004 13:26:18 +0000 (13:26 +0000)
committerKalle Wallin <kaw@linux.se>
Fri, 19 Mar 2004 13:26:18 +0000 (13:26 +0000)
git-svn-id: https://svn.musicpd.org/ncmpc/trunk@292 09075e82-0dd4-0310-85a5-a0d7c8717e4f

38 files changed:
AUTHORS [new file with mode: 0644]
ChangeLog [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
NEWS [new file with mode: 0644]
README [new file with mode: 0644]
TODO [new file with mode: 0644]
bootstrap.sh [new file with mode: 0755]
cleanup.sh [new file with mode: 0755]
command.c [new file with mode: 0644]
command.h [new file with mode: 0644]
configure.ac [new file with mode: 0644]
doc/ncmpc.1 [new file with mode: 0644]
libmpdclient.c [new file with mode: 0644]
libmpdclient.h [new file with mode: 0644]
m4/CVS/Entries [new file with mode: 0644]
m4/CVS/Repository [new file with mode: 0644]
m4/CVS/Root [new file with mode: 0644]
m4/glib-2.0.m4 [new file with mode: 0644]
main.c [new file with mode: 0644]
mpc.c [new file with mode: 0644]
mpc.h [new file with mode: 0644]
options.c [new file with mode: 0644]
options.h [new file with mode: 0644]
out [new file with mode: 0644]
screen.c [new file with mode: 0644]
screen.h [new file with mode: 0644]
screen_file.c [new file with mode: 0644]
screen_file.h [new file with mode: 0644]
screen_help.c [new file with mode: 0644]
screen_help.h [new file with mode: 0644]
screen_play.c [new file with mode: 0644]
screen_play.h [new file with mode: 0644]
screen_search.c [new file with mode: 0644]
screen_search.h [new file with mode: 0644]
screen_utils.c [new file with mode: 0644]
screen_utils.h [new file with mode: 0644]
support.c [new file with mode: 0644]
support.h [new file with mode: 0644]

diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..06557c0
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Kalle Wallin <kaw@linux.se>
diff --git a/ChangeLog b/ChangeLog
new file mode 100644 (file)
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 (file)
index 0000000..82cfd58
--- /dev/null
@@ -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 (file)
index 0000000..a09f8d3
--- /dev/null
+++ b/NEWS
@@ -0,0 +1,3 @@
+
+
+       
diff --git a/README b/README
new file mode 100644 (file)
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 (file)
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 (executable)
index 0000000..8d47e61
--- /dev/null
@@ -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 (executable)
index 0000000..f847f2b
--- /dev/null
@@ -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 (file)
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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 (file)
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 (file)
index 0000000..0b39bec
--- /dev/null
@@ -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 (file)
index 0000000..8bda42e
--- /dev/null
@@ -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 (file)
index 0000000..e3f3b95
--- /dev/null
@@ -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 <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <sys/param.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#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;i<strlen(arg);i++) {
+               if(arg[i]=='"' || arg[i]=='\\') count++;
+       }
+
+       ret = malloc(strlen(arg)+count+1);
+
+       count = 0;
+       for(i=0;i<strlen(arg)+1;i++) {
+               if(arg[i]=='"' || arg[i]=='\\') {
+                       ret[i+count] = '\\';
+                       count++;
+               }
+               ret[i+count] = arg[i];
+       }
+
+       return ret;
+}
+
+mpd_ReturnElement * mpd_newReturnElement(const char * name, const char * value)
+{
+       mpd_ReturnElement * ret = malloc(sizeof(mpd_ReturnElement));
+
+       ret->name = 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 (file)
index 0000000..0ea84b1
--- /dev/null
@@ -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 <sys/param.h>
+#include <sys/time.h>
+
+#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 (file)
index 0000000..629d8da
--- /dev/null
@@ -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 (file)
index 0000000..b320242
--- /dev/null
@@ -0,0 +1 @@
+apps/mpc-curses/m4
diff --git a/m4/CVS/Root b/m4/CVS/Root
new file mode 100644 (file)
index 0000000..c7f5064
--- /dev/null
@@ -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 (file)
index 0000000..28ccef4
--- /dev/null
@@ -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 <glib.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+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, &micro) != 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 <glib.h>
+#include <stdio.h>
+],      [ 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 (file)
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 <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <glib.h>
+
+#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 (file)
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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <string.h>
+#include <glib.h>
+
+#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 (file)
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 (file)
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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <popt.h>
+
+#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 (file)
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 (file)
index 0000000..a5b9bbf
--- /dev/null
+++ b/out
@@ -0,0 +1,4 @@
+charset: ANSI_X3.4-1968
+\e[1;30r\e[0;10m\e[4l\e[39;49m\e[?25l\e[?1c\e[39;49m\e[0;10m\e[H\e[J\e[0;10;1mMusic Player Client - Playlist\e[1;69H\e[0;10mVolume  80%\r\e[2d\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[H\e[J\e[0;10;1mMusic Player Client - Playlist\e[1;69H\e[0;10mVolume  80%\r\e[2d\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[29;1H\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[30;1H\e[0;10;1mStopped! \e[0;10m\e[H\e[J\e[0;10;1mMusic Player Client - Playlist\e[1;69H\e[0;10mVolume  80%\r\e[2d\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[29;1H\e[0;10;11mÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\e[10m\e[30;1H\e[0;10;1mStopped! \e[0;10m
+Exiting...
+\e[30;1H\e[?25h\e[?0c\r
\ No newline at end of file
diff --git a/screen.c b/screen.c
new file mode 100644 (file)
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 <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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, "<R>");
+      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( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
+    {
+      fprintf(stderr, "Error: Screen to small!\n");
+      exit(EXIT_FAILURE);
+    }
+  screen_init();
+}
+
+void 
+screen_status_message(mpd_client_t *c, char *msg)
+{
+  WINDOW *w = screen->status_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( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
+    {
+      fprintf(stderr, "Error: Screen to small!\n");
+      exit(EXIT_FAILURE);
+    }
+
+  screen = malloc(sizeof(screen_t));
+  memset(screen, 0, sizeof(screen_t));
+  screen->mode = 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 (file)
index 0000000..3512424
--- /dev/null
+++ b/screen.h
@@ -0,0 +1,71 @@
+#ifndef SCREEN_H
+#define SCREEN_H
+#include <ncurses.h>
+#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 (file)
index 0000000..bd60872
--- /dev/null
@@ -0,0 +1,333 @@
+/* 
+ * $Id: screen_file.c,v 1.9 2004/03/18 09:33:07 kalle Exp $ 
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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 (file)
index 0000000..2d5ab5e
--- /dev/null
@@ -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 (file)
index 0000000..d38e4a6
--- /dev/null
@@ -0,0 +1,165 @@
+/* 
+ * $Id: screen_help.c,v 1.8 2004/03/17 13:40:25 kalle Exp $ 
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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( index<help_text_rows )
+    {
+      *highlight = help_text[index].highlight;
+      if( help_text[index].command == CMD_NONE )
+       return help_text[index].text;
+      snprintf(buf, 256, 
+              "%20s : %s", 
+              command_get_keys(help_text[index].command),
+              help_text[index].text);
+      return buf;
+    }
+
+  return NULL;
+}
+
+
+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)
+{
+  list_window_t *w = screen->helplist;
+
+  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 (file)
index 0000000..b6434dd
--- /dev/null
@@ -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 (file)
index 0000000..161e611
--- /dev/null
@@ -0,0 +1,129 @@
+/* 
+ * $Id: screen_play.c,v 1.6 2004/03/17 14:49:31 kalle Exp $ 
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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 (file)
index 0000000..c88f4c7
--- /dev/null
@@ -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 (file)
index 0000000..cb975a8
--- /dev/null
@@ -0,0 +1,37 @@
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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 (file)
index 0000000..b2b7f51
--- /dev/null
@@ -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 (file)
index 0000000..e100cbf
--- /dev/null
@@ -0,0 +1,152 @@
+/* 
+ * $Id: screen_utils.c,v 1.4 2004/03/16 13:57:24 kalle Exp $ 
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <ncurses.h>
+
+#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; i<lw->rows; 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 (file)
index 0000000..7143832
--- /dev/null
@@ -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 (file)
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+
+#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 (file)
index 0000000..834dd8c
--- /dev/null
+++ b/support.h
@@ -0,0 +1,9 @@
+
+#ifdef HAVE_LIBGEN_H
+#include <libgen.h>
+#else
+char *basename(char *path);
+#endif
+
+char *utf8(char *str);
+