diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | common.h | 15 | ||||
-rw-r--r-- | configure.ac | 108 | ||||
-rw-r--r-- | deadbeef.h | 13 | ||||
-rw-r--r-- | main.c | 75 | ||||
-rw-r--r-- | plugins.c | 29 | ||||
-rw-r--r-- | plugins.h | 8 | ||||
-rw-r--r-- | plugins/adplug/Makefile.am | 8 | ||||
-rw-r--r-- | plugins/adplug/adplug-db.cpp | 12 | ||||
-rw-r--r-- | plugins/artwork/artwork.c | 35 | ||||
-rw-r--r-- | plugins/artwork/artwork.h | 1 | ||||
-rw-r--r-- | plugins/gme/Makefile.am | 4 | ||||
-rw-r--r-- | plugins/gme/cgme.c | 12 | ||||
-rw-r--r-- | plugins/gtkui/callbacks.c | 12 | ||||
-rw-r--r-- | plugins/gtkui/coverart.c | 7 | ||||
-rw-r--r-- | plugins/gtkui/gtkui.c | 3 | ||||
-rw-r--r-- | plugins/mpris/Makefile.am | 8 | ||||
-rw-r--r-- | plugins/mpris/mpris-spec.h | 91 | ||||
-rw-r--r-- | plugins/mpris/mpris.c | 1310 | ||||
-rw-r--r-- | plugins/shellexec/Makefile.am | 2 | ||||
-rw-r--r-- | plugins/sid/Makefile.am | 6 | ||||
-rw-r--r-- | plugins/sid/csid.cpp | 12 | ||||
-rw-r--r-- | plugins/supereq/Equ.cpp | 12 | ||||
-rw-r--r-- | plugins/supereq/Makefile.am | 4 | ||||
-rw-r--r-- | plugins/vfs_curl/Makefile.am | 2 |
25 files changed, 1726 insertions, 64 deletions
@@ -45,3 +45,4 @@ vala.stamp POTFILES stamp-it po/Makevars.template +*.so @@ -18,12 +18,21 @@ #ifndef __COMMON_H #define __COMMON_H +#include <limits.h> +#ifndef PATH_MAX +#define PATH_MAX 1024 /* max # of characters in a path name */ +#endif + #define min(x,y) ((x)<(y)?(x):(y)) #define max(x,y) ((x)>(y)?(x):(y)) // those are defined in main.c -extern char confdir[1024]; // $HOME/.config -extern char dbconfdir[1024]; // $HOME/.config/deadbeef -extern char sessfile[1024]; // $HOME/.config/deadbeef/session +extern char confdir[PATH_MAX]; // $HOME/.config +extern char dbconfdir[PATH_MAX]; // $HOME/.config/deadbeef +extern char dbinstalldir[PATH_MAX]; // see deadbeef->get_prefix +extern char dbdocdir[PATH_MAX]; // see deadbeef->get_doc_dir +extern char dbplugindir[PATH_MAX]; // see deadbeef->get_plugin_dir +extern char dbpixmapdir[PATH_MAX]; // see deadbeef->get_pixmap_dir + #endif // __COMMON_H diff --git a/configure.ac b/configure.ac index 7a113167..38455ae0 100644 --- a/configure.ac +++ b/configure.ac @@ -33,15 +33,19 @@ fi case "$host" in i386-*-* | i486-*-* | i586-*-* | i686-*-* | i86pc-*-*) AC_DEFINE(ARCH_X86_32, 1, [architecture is x86]) + LIB="lib-x86-32" ;; x86_64-*-* | amd64-*-*) AC_DEFINE(ARCH_X86_64, 1, [architecture is x86_64]) + LIB="lib-x86-64" ;; powerpc-*-* ) AC_DEFINE(ARCH_PPC_32, 1, [architecture is ppc32]) + LIB="lib-ppc-32" ;; powerpc64-*-* ) AC_DEFINE(ARCH_PPC_64, 1, [architecture is ppc64]) + LIB="lib-ppc-64" ;; *) AC_DEFINE(ARCH_UNKNOWN, 1, [architecture is unknown]) @@ -56,9 +60,6 @@ dnl INSANE_CXXFLAGS="-Wcomment -Wchar-subscripts -Wunused-function -Wunused-valu AC_SUBST(INSANE_CFLAGS) AC_SUBST(INSANE_CXXFLAGS) -CXXFLAGS="$CXXFLAGS $INSANE_CXXFLAGS -D_GNU_SOURCE -DLIBDIR=\\\"$libdir\\\" -DPREFIX=\\\"$prefix\\\" -DDOCDIR=\\\"$docdir\\\"" -CFLAGS="$CFLAGS $INSANE_CFLAGS -D_GNU_SOURCE -DLIBDIR=\\\"$libdir\\\" -DPREFIX=\\\"$prefix\\\" -DDOCDIR=\\\"$docdir\\\"" - AC_ARG_ENABLE(nullout, [AS_HELP_STRING([--disable-nullout ], [disable NULL output plugin (default: enabled)])], [enable_nullout=$enableval], [enable_nullout=yes]) AC_ARG_ENABLE(alsa, [AS_HELP_STRING([--disable-alsa ], [disable ALSA output plugin (default: enabled)])], [enable_alsa=$enableval], [enable_alsa=yes]) AC_ARG_ENABLE(oss, [AS_HELP_STRING([--disable-oss ], [disable Open Sound System output plugin (default: enabled)])], [enable_oss=$enableval], [enable_oss=yes]) @@ -93,8 +94,27 @@ AC_ARG_ENABLE(aac, [AS_HELP_STRING([--disable-aac ], [disable AAC decod AC_ARG_ENABLE(mms, [AS_HELP_STRING([--disable-mms ], [disable MMS streaming vfs plugin (default: enabled)])], [enable_mms=$enableval], [enable_mms=yes]) AC_ARG_ENABLE(shn, [AS_HELP_STRING([--disable-shn ], [disable shorten plugin (default: enabled)])], [enable_shn=$enableval], [enable_shn=yes]) AC_ARG_ENABLE(ao, [AS_HELP_STRING([--disable-ao ], [disable audio overload plugin (default: enabled)])], [enable_ao=$enableval], [enable_ao=yes]) +AC_ARG_ENABLE(mpris, [ --enable-mpris enable Ubuntu Sound Menu plugin (default: disabled)], [enable_mpris=$enableval], [enable_mpris=no]) +AC_ARG_ENABLE(portable, [ --enable-portable make portable static build (default: disabled)], [enable_portable=$enableval], [enable_portable=no]) + +if test "x$enable_portable" != "xno" ; then + AC_DEFINE_UNQUOTED([PORTABLE], [1], [Define if building portable version]) + PORTABLE=yes + PREFIXFLAGS="-DPREFIX=donotuse -DLIBDIR=donotuse -DDOCDIR=donotuse -I./include" +else + PREFIXFLAGS=" -DLIBDIR=\\\"$libdir\\\" -DPREFIX=\\\"$prefix\\\" -DDOCDIR=\\\"$docdir\\\"" +fi + +CXXFLAGS="$CXXFLAGS $INSANE_CXXFLAGS -D_GNU_SOURCE $PREFIXFLAGS" +CFLAGS="$CFLAGS $INSANE_CFLAGS -D_GNU_SOURCE $PREFIXFLAGS" PKG_CHECK_MODULES(DEPS, samplerate) +if test "x$enable_portable" != "xno" ; then + DEPS_LIBS="$LIB/libsamplerate.a -lpthread -ldl" + AC_SUBST(DEPS_LIBS) +else + PKG_CHECK_MODULES(DEPS, samplerate) +fi if test "x$enable_gtkui" != "xno" ; then if test "x$enable_gtk3" == "xyes" ; then @@ -118,8 +138,8 @@ if test "x$enable_pulse" != "xno" ; then PKG_CHECK_MODULES(PULSE_DEPS, libpulse-simple, HAVE_PULSE=yes, HAVE_PULSE=no) fi -AC_CHECK_LIB([pthread], [main]) -AC_CHECK_LIB([dl], [main]) +dnl AC_CHECK_LIB([pthread], [main]) +dnl AC_CHECK_LIB([dl], [main]) AC_CHECK_HEADER([iconv.h],[],[iconv.h not found.]) @@ -136,17 +156,33 @@ if test ${HAVE_SSE2}; then fi dnl curl lib -AC_CHECK_LIB([curl], [main], [HAVE_CURL=yes]) -if test "x$HAVE_CURL" = "xyes"; then +if test "x$enable_portable" != "xno" ; then + HAVE_CURL=yes + CURL_LIBS="../../$LIB/libcurl.a -lrt" + AC_SUBST(CURL_LIBS) +else + AC_CHECK_LIB([curl], [main], [HAVE_CURL=yes]) CURL_LIBS="-lcurl" AC_SUBST(CURL_LIBS) fi -PKG_CHECK_MODULES(DBUS_DEPS, dbus-1, HAVE_DBUS=yes, HAVE_DBUS=no) +if test "x$enable_portable" != "xno" ; then + HAVE_DBUS=yes + DBUS_DEPS_LIBS="../../$LIB/libdbus-1.a ../../$LIB/libexpat.a -lrt" + DBUS_DEPS_CFLAGS="-I../../include/dbus-1" + AC_SUBST(DBUS_DEPS_LIBS) +else + PKG_CHECK_MODULES(DBUS_DEPS, dbus-1, HAVE_DBUS=yes, HAVE_DBUS=no) +fi dnl mpgmad plugin if test "x$enable_mpgmad" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_MPGMAD=yes + MAD_LIBS="../../$LIB/libmad.a" + AC_SUBST(MAD_LIBS) +else AC_CHECK_LIB([mad], [main], [HAVE_LIBMAD=yes]) if test "x$HAVE_LIBMAD" = "xyes" ; then HAVE_MPGMAD=yes @@ -154,9 +190,15 @@ if test "x$enable_mpgmad" != "xno" ; then AC_SUBST(MAD_LIBS) fi fi +fi dnl vorbis plugin if test "x$enable_vorbis" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_VORBISPLUGIN=yes + VORBIS_LIBS="../../$LIB/libogg.a ../../$LIB/libvorbis.a ../../$LIB/libvorbisenc.a ../../$LIB/libvorbisfile.a" + AC_SUBST(VORBIS_LIBS) +else AC_CHECK_LIB([vorbis], [main], [HAVE_VORBIS=yes]) AC_CHECK_LIB([vorbisfile], [main], [HAVE_VORBISFILE=yes]) if test "x$HAVE_VORBIS" = "xyes" && test "x$HAVE_VORBISFILE" = "xyes" ; then @@ -165,9 +207,15 @@ if test "x$enable_vorbis" != "xno" ; then AC_SUBST(VORBIS_LIBS) fi fi +fi dnl flac plugin if test "x$enable_flac" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_FLACPLUGIN=yes + FLAC_LIBS="../../$LIB/libFLAC.a ../../$LIB/libogg.a" + AC_SUBST(FLAC_LIBS) +else AC_CHECK_LIB([FLAC], [main], [HAVE_FLAC=yes]) if test "x$HAVE_FLAC" = "xyes" ; then HAVE_FLACPLUGIN=yes @@ -175,9 +223,15 @@ if test "x$enable_flac" != "xno" ; then AC_SUBST(FLAC_LIBS) fi fi +fi dnl wavpack plugin if test "x$enable_wavpack" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_WAVPACKPLUGIN=yes + WAVPACK_LIBS="../../$LIB/libwavpack.a" + AC_SUBST(WAVPACK_LIBS) +else AC_CHECK_LIB([wavpack], [main], [HAVE_WAVPACK=yes]) if test "x$HAVE_WAVPACK" = "xyes" ; then HAVE_WAVPACKPLUGIN=yes @@ -185,9 +239,15 @@ if test "x$enable_wavpack" != "xno" ; then AC_SUBST(WAVPACK_LIBS) fi fi +fi dnl libsndfile plugin if test "x$enable_sndfile" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_SNDFILEPLUGIN=yes + SNDFILE_LIBS="../../$LIB/libsndfile.a" + AC_SUBST(SNDFILE_LIBS) +else AC_CHECK_LIB([sndfile], [main], [HAVE_SNDFILE=yes]) if test "x$HAVE_SNDFILE" = "xyes" ; then HAVE_SNDFILEPLUGIN=yes @@ -195,18 +255,24 @@ if test "x$enable_sndfile" != "xno" ; then AC_SUBST(SNDFILE_LIBS) fi fi +fi dnl vfs_curl plugin if test "x$enable_vfs_curl" != "xno" ; then if test "x$HAVE_CURL" = "xyes" ; then HAVE_VFS_CURL=yes - VFS_CURL_LIBS="-lcurl" + VFS_CURL_LIBS="$CURL_LIBS" AC_SUBST(VFS_CURL_LIBS) fi fi dnl cdda plugin if test "x$enable_cdda" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_CDDAPLUGIN=yes + CDDA_LIBS="../../$LIB/libcdio.a ../../$LIB/libudf.a ../../$LIB/libiso9660.a ../../$LIB/libcddb.a" + AC_SUBST(CDDA_LIBS) +else AC_CHECK_LIB([cdio], [main], [HAVE_CDIO=yes]) AC_CHECK_LIB([cddb], [main], [HAVE_CDDB=yes]) if test "x$HAVE_CDIO" = "xyes" && test "x$HAVE_CDDB" = "xyes" ; then @@ -215,6 +281,7 @@ if test "x$enable_cdda" != "xno" ; then AC_SUBST(CDDA_LIBS) fi fi +fi dnl gtkui plugin if test "x$enable_gtkui" != "xno" ; then @@ -365,6 +432,11 @@ if test "x$enable_dca" != "xno" ; then fi if test "x$enable_aac" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + FAAD2_LIBS="../../$LIB/libfaad.a" + AC_SUBST(FAAD2_LIBS) + HAVE_AAC=yes +else AC_CHECK_LIB([faad], [main], [HAVE_FAAD=1]) if test ${HAVE_FAAD} ; then FAAD2_LIBS="-lfaad" @@ -372,6 +444,7 @@ if test "x$enable_aac" != "xno" ; then HAVE_AAC=yes fi fi +fi if test "x$enable_mms" != "xno" ; then LIBMMS_LIBS="" @@ -384,15 +457,24 @@ if test "x$enable_shn" != "xno" ; then fi if test "x$enable_ao" != "xno" ; then +if test "x$enable_portable" != "xno" ; then + HAVE_ZLIB=yes + ZLIB_LIBS="../../$LIB/libz.a" +else AC_CHECK_LIB([z], [main], [HAVE_ZLIB=yes]) + ZLIB_LIBS="-lz" +fi if test "x$HAVE_ZLIB" = "xyes"; then - ZLIB_LIBS="-lz" AC_SUBST(ZLIB_LIBS) HAVE_AO=yes fi fi -PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/dumb plugins/pulse plugins/notify plugins/musepack plugins/wildmidi plugins/tta plugins/dca plugins/aac plugins/mms plugins/shn plugins/ao plugins/shellexec" +if test "x$enable_mpris" != "xno" ; then + PKG_CHECK_MODULES(MPRIS_DEPS, gio-2.0 glib-2.0 >= 2.26.0, HAVE_MPRIS=yes, HAVE_MPRIS=no) +fi + +PLUGINS_DIRS="plugins/lastfm plugins/mpgmad plugins/vorbis plugins/flac plugins/wavpack plugins/sndfile plugins/vfs_curl plugins/cdda plugins/gtkui plugins/alsa plugins/ffmpeg plugins/hotkeys plugins/oss plugins/artwork plugins/adplug plugins/ffap plugins/sid plugins/nullout plugins/supereq plugins/vtx plugins/gme plugins/dumb plugins/pulse plugins/notify plugins/musepack plugins/wildmidi plugins/tta plugins/dca plugins/aac plugins/mms plugins/shn plugins/ao plugins/shellexec plugins/mpris" AM_CONDITIONAL(HAVE_VORBIS, test "x$HAVE_VORBISPLUGIN" = "xyes") AM_CONDITIONAL(HAVE_FLAC, test "x$HAVE_FLACPLUGIN" = "xyes") @@ -427,6 +509,8 @@ AM_CONDITIONAL(HAVE_AAC, test "x$HAVE_AAC" = "xyes") AM_CONDITIONAL(HAVE_MMS, test "x$HAVE_MMS" = "xyes") AM_CONDITIONAL(HAVE_SHN, test "x$HAVE_SHN" = "xyes") AM_CONDITIONAL(HAVE_AO, test "x$HAVE_AO" = "xyes") +AM_CONDITIONAL(HAVE_MPRIS, test "x$HAVE_MPRIS" = "xyes") +AM_CONDITIONAL(PORTABLE, test "x$PORTABLE" = "xyes") AC_SUBST(PLUGINS_DIRS) @@ -487,6 +571,7 @@ PRINT_PLUGIN_INFO([aac],[AAC player (m4a, aac, mp4) based on FAAD2],[test "x$HAV PRINT_PLUGIN_INFO([mms],[mms streaming support],[test "x$HAVE_MMS" = "xyes"]) PRINT_PLUGIN_INFO([shn],[shorten player based on xmms-shn],[test "x$HAVE_SHN" = "xyes"]) PRINT_PLUGIN_INFO([ao],[psf1/psf2/spu/ssf player using Audio Overload],[test "x$HAVE_AO" = "xyes"]) +PRINT_PLUGIN_INFO([mpris],[Ubuntu Sound Menu integration],[test "x$HAVE_MPRIS" = "xyes"]) echo @@ -527,6 +612,7 @@ plugins/aac/Makefile plugins/mms/Makefile plugins/shn/Makefile plugins/ao/Makefile +plugins/mpris/Makefile intl/Makefile po/Makefile.in deadbeef.desktop @@ -64,8 +64,8 @@ extern "C" { // 0.2 -- deadbeef-0.2.3 // 0.1 -- deadbeef-0.2.0 -#define DB_API_VERSION_MAJOR 0 -#define DB_API_VERSION_MINOR 8 +#define DB_API_VERSION_MAJOR 9 +#define DB_API_VERSION_MINOR 9 #define DB_PLUGIN_SET_API_VERSION\ .plugin.api_vmajor = DB_API_VERSION_MAJOR,\ @@ -317,8 +317,15 @@ typedef struct { int (*streamer_get_apx_bitrate) (void); struct DB_fileinfo_s *(*streamer_get_current_fileinfo) (void); int (*streamer_get_current_playlist) (void); + // system folders + // normally functions will return standard folders derived from --prefix + // portable version will return pathes specified in comments below + const char *(*get_config_dir) (void); // installdir/config | $XDG_CONFIG_HOME/.config/deadbeef + const char *(*get_prefix) (void); // installdir | PREFIX + const char *(*get_doc_dir) (void); // installdir/doc | DOCDIR + const char *(*get_plugin_dir) (void); // installdir/plugins | LIBDIR/deadbeef + const char *(*get_pixmap_dir) (void); // installdir/pixmaps | PREFIX "/share/deadbeef/pixmaps" // process control - const char *(*get_config_dir) (void); void (*quit) (void); // threading intptr_t (*thread_start) (void (*fn)(void *ctx), void *ctx); @@ -54,10 +54,7 @@ #include "conf.h" #include "volume.h" #include "plugins.h" - -#ifndef PATH_MAX -#define PATH_MAX 1024 /* max # of characters in a path name */ -#endif +#include "common.h" #ifndef PREFIX #error PREFIX must be defined @@ -67,12 +64,18 @@ #define USE_ABSTRACT_NAME 0 #endif -//#define trace(...) { fprintf(stderr, __VA_ARGS__); } -#define trace(fmt,...) +#define trace(...) { fprintf(stderr, __VA_ARGS__); } +//#define trace(fmt,...) + // some common global variables -char confdir[1024]; // $HOME/.config -char dbconfdir[1024]; // $HOME/.config/deadbeef +char sys_install_path[PATH_MAX]; // see deadbeef->get_prefix +char confdir[PATH_MAX]; // $HOME/.config +char dbconfdir[PATH_MAX]; // $HOME/.config/deadbeef +char dbinstalldir[PATH_MAX]; // see deadbeef->get_prefix +char dbdocdir[PATH_MAX]; // see deadbeef->get_doc_dir +char dbplugindir[PATH_MAX]; // see deadbeef->get_plugin_dir +char dbpixmapdir[PATH_MAX]; // see deadbeef->get_pixmap_dir // client-side commandline support // -1 error, program must exit with error code -1 @@ -531,11 +534,45 @@ main (int argc, char *argv[]) { bind_textdomain_codeset (PACKAGE, "UTF-8"); textdomain (PACKAGE); #endif - fprintf (stderr, "starting deadbeef " VERSION "\n"); + fprintf (stderr, "starting deadbeef " VERSION "%s\n", PORTABLE ? " [portable build]" : ""); srand (time (NULL)); #ifdef __linux__ prctl (PR_SET_NAME, "deadbeef-main", 0, 0, 0, 0); #endif + +#if PORTABLE + strcpy (dbinstalldir, argv[0]); + char *e = dbinstalldir + strlen (dbinstalldir); + while (e >= dbinstalldir && *e != '/') { + e--; + } + *e = 0; + if (snprintf (confdir, sizeof (confdir), "%s/config", dbinstalldir) > sizeof (confdir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } + + strcpy (dbconfdir, confdir); + + if (snprintf (dbdocdir, sizeof (dbdocdir), "%s/doc", dbinstalldir) > sizeof (dbdocdir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } + if (snprintf (dbplugindir, sizeof (dbplugindir), "%s/plugins", dbinstalldir) > sizeof (dbplugindir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } + if (snprintf (dbpixmapdir, sizeof (dbpixmapdir), "%s/pixmaps", dbinstalldir) > sizeof (dbpixmapdir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } + trace ("installdir: %s\n", dbinstalldir); + trace ("confdir: %s\n", confdir); + trace ("docdir: %s\n", dbdocdir); + trace ("plugindir: %s\n", dbplugindir); + mkdir (dbplugindir, 0755); + trace ("pixmapdir: %s\n", dbpixmapdir); +#else char *homedir = getenv ("HOME"); if (!homedir) { fprintf (stderr, "unable to find home directory. stopping.\n"); @@ -555,11 +592,25 @@ main (int argc, char *argv[]) { return -1; } } - mkdir (confdir, 0755); if (snprintf (dbconfdir, sizeof (dbconfdir), "%s/deadbeef", confdir) > sizeof (dbconfdir)) { fprintf (stderr, "fatal: out of memory while configuring\n"); return -1; } + mkdir (confdir, 0755); + if (snprintf (dbdocdir, sizeof (dbdocdir), "%s", DOCDIR) > sizeof (dbdocdir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } + if (snprintf (dbplugindir, sizeof (dbplugindir), "%s/deadbeef", LIBDIR) > sizeof (dbplugindir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } + if (snprintf (dbpixmapdir, sizeof (dbpixmapdir), "%s/share/deadbeef/pixmaps", PREFIX) > sizeof (dbpixmapdir)) { + fprintf (stderr, "fatal: too long install path %s\n", dbinstalldir); + return -1; + } +#endif + mkdir (dbconfdir, 0755); char cmdline[2048]; @@ -681,7 +732,9 @@ main (int argc, char *argv[]) { conf_load (); // required by some plugins at startup volume_set_db (conf_get_float ("playback.volume", 0)); // volume need to be initialized before plugins start messagepump_init (); // required to push messages while handling commandline - plug_load_all (); // required to add files to playlist from commandline + if (plug_load_all ()) { // required to add files to playlist from commandline + exit (-1); + } pl_load_all (); plt_set_curr (conf_get_int ("playlist.current", 0)); @@ -88,8 +88,13 @@ static DB_functions_t deadbeef_api = { .streamer_get_apx_bitrate = streamer_get_apx_bitrate, .streamer_get_current_fileinfo = streamer_get_current_fileinfo, .streamer_get_current_playlist = streamer_get_current_playlist, - // process control + // folders .get_config_dir = plug_get_config_dir, + .get_prefix = plug_get_prefix, + .get_doc_dir = plug_get_doc_dir, + .get_plugin_dir = plug_get_plugin_dir, + .get_pixmap_dir = plug_get_pixmap_dir, + // process control .quit = plug_quit, // threading .thread_start = thread_start, @@ -267,6 +272,26 @@ plug_get_config_dir (void) { return dbconfdir; } +const char * +plug_get_prefix (void) { + return dbinstalldir; +} + +const char * +plug_get_doc_dir (void) { + return dbdocdir; +} + +const char * +plug_get_plugin_dir (void) { + return dbplugindir; +} + +const char * +plug_get_pixmap_dir (void) { + return dbpixmapdir; +} + void plug_volume_set_db (float db) { volume_set_db (db); @@ -566,7 +591,7 @@ plug_load_all (void) { const char *conf_blacklist_plugins = conf_get_str ("blacklist_plugins", ""); trace ("plug: mutex_create\n"); mutex = mutex_create (); - const char *dirname = LIBDIR "/deadbeef"; + const char *dirname = deadbeef->get_plugin_dir (); struct dirent **namelist = NULL; char *xdg_local_home = getenv ("XDG_LOCAL_HOME"); @@ -113,6 +113,14 @@ plug_volume_set_amp (float amp); const char * plug_get_config_dir (void); +const char * +plug_get_prefix (void); +const char * +plug_get_doc_dir (void); +const char * +plug_get_plugin_dir (void); +const char * +plug_get_pixmap_dir (void); int plug_activate (DB_plugin_t *plug, int activate); diff --git a/plugins/adplug/Makefile.am b/plugins/adplug/Makefile.am index a75f38b5..393f2d59 100644 --- a/plugins/adplug/Makefile.am +++ b/plugins/adplug/Makefile.am @@ -5,11 +5,12 @@ adlibdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = adplug.la AM_CFLAGS = $(CFLAGS) -std=c99 -I$(adplugpath)/adplug -I$(adplugpath)/libbinio +adplug_la_LDFLAGS = -module -nostdlib -lsupc++ -AM_CPPFLAGS = $(CXXFLAGS) -Dstricmp=strcasecmp -DVERSION=\"2.1\" -I$(adplugpath)/adplug -I$(adplugpath)/libbinio +AM_CPPFLAGS = $(CXXFLAGS) -Dstricmp=strcasecmp -DVERSION=\"2.1\" -I$(adplugpath)/adplug -I$(adplugpath)/libbinio -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables -adplug_la_SOURCES = adplug-db.cpp\ - plugin.c\ +adplug_la_SOURCES = plugin.c\ + adplug-db.cpp\ libbinio/binfile.h\ libbinio/binio.h\ libbinio/binstr.h\ @@ -134,5 +135,4 @@ adplug_la_SOURCES = adplug-db.cpp\ # adplug/database.cpp # adplug/database.h -adplug_la_LDFLAGS = -module endif diff --git a/plugins/adplug/adplug-db.cpp b/plugins/adplug/adplug-db.cpp index 567d55ca..a7b1f6b2 100644 --- a/plugins/adplug/adplug-db.cpp +++ b/plugins/adplug/adplug-db.cpp @@ -31,6 +31,18 @@ //#define trace(...) { fprintf (stderr, __VA_ARGS__); } #define trace(fmt,...) +int _Unwind_Resume_or_Rethrow; +int _Unwind_RaiseException; +int _Unwind_GetLanguageSpecificData; +int _Unwind_Resume; +int _Unwind_DeleteException; +int _Unwind_GetTextRelBase; +int _Unwind_SetIP; +int _Unwind_GetDataRelBase; +int _Unwind_GetRegionStart; +int _Unwind_SetGR; +int _Unwind_GetIPInfo; + extern "C" { extern DB_decoder_t adplug_plugin; diff --git a/plugins/artwork/artwork.c b/plugins/artwork/artwork.c index fbbe71d9..469ec8bb 100644 --- a/plugins/artwork/artwork.c +++ b/plugins/artwork/artwork.c @@ -16,7 +16,7 @@ //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(...) -#define DEFAULT_COVER_PATH (PREFIX "/share/deadbeef/pixmaps/noartwork.jpg") +static char default_cover[PATH_MAX]; #define DEFAULT_FILEMASK "*cover*.jpg;*front*.jpg" static DB_artwork_plugin_t plugin; @@ -41,12 +41,16 @@ static volatile int terminate; static volatile int clear_queue; static intptr_t tid; -int artwork_enable_embedded; -int artwork_enable_local; -int artwork_enable_lfm; -int artwork_enable_aao; -int artwork_reset_time; -char artwork_filemask[200]; +static int artwork_enable_embedded; +static int artwork_enable_local; +static int artwork_enable_lfm; +static int artwork_enable_aao; +static int artwork_reset_time; +static char artwork_filemask[200]; + +static const char *get_default_cover (void) { + return default_cover; +} void make_cache_dir_path (char *path, int size, const char *album, const char *artist) { @@ -568,11 +572,11 @@ get_album_art (const char *fname, const char *artist, const char *album, artwork if (!*artist || !*album) { //give up - return strdup (DEFAULT_COVER_PATH); + return strdup (get_default_cover ()); } if (!deadbeef->is_local_file (fname)) { - return strdup (DEFAULT_COVER_PATH); + return strdup (get_default_cover ()); } make_cache_path (path, sizeof (path), album, artist); @@ -586,7 +590,7 @@ get_album_art (const char *fname, const char *artist, const char *album, artwork trace ("reloading cached file %s\n", path); unlink (path); queue_add (fname, artist, album, callback, user_data); - return strdup (DEFAULT_COVER_PATH); + return strdup (get_default_cover ()); } trace ("found %s in cache\n", path); @@ -594,7 +598,7 @@ get_album_art (const char *fname, const char *artist, const char *album, artwork } queue_add (fname, artist, album, callback, user_data); - return strdup (DEFAULT_COVER_PATH); + return strdup (get_default_cover ()); } DB_plugin_t * @@ -664,6 +668,13 @@ artwork_on_configchanged (DB_event_t *ev, uintptr_t data) { static int artwork_plugin_start (void) { + const char *def_art = deadbeef->conf_get_str ("gtkui.nocover_pixmap", NULL); + if (!def_art) { + snprintf (default_cover, sizeof (default_cover), "%s/noartwork.jpg", deadbeef->get_pixmap_dir ()); + } + else { + strcpy (default_cover, def_art); + } terminate = 0; artwork_enable_embedded = deadbeef->conf_get_int ("artwork.enable_embedded", 1); @@ -720,6 +731,7 @@ static const char settings_dlg[] = "property \"Fetch from last.fm\" checkbox artwork.enable_lastfm 0;\n" "property \"Fetch from albumart.org\" checkbox artwork.enable_albumartorg 0;\n" ; + // define plugin interface static DB_artwork_plugin_t plugin = { .plugin.plugin.api_vmajor = DB_API_VERSION_MAJOR, @@ -736,4 +748,5 @@ static DB_artwork_plugin_t plugin = { .plugin.plugin.configdialog = settings_dlg, .get_album_art = get_album_art, .reset = artwork_reset, + .get_default_cover = get_default_cover, }; diff --git a/plugins/artwork/artwork.h b/plugins/artwork/artwork.h index ab0fead6..ed5357df 100644 --- a/plugins/artwork/artwork.h +++ b/plugins/artwork/artwork.h @@ -14,6 +14,7 @@ typedef struct { // this has to be called to clear queue on exit, before caller terminates // `fast=1' means "don't wait, just flush queue" void (*reset) (int fast); + const char *(*get_default_cover) (void); } DB_artwork_plugin_t; #endif /*__ARTWORK_H*/ diff --git a/plugins/gme/Makefile.am b/plugins/gme/Makefile.am index 61351f9c..ed815899 100644 --- a/plugins/gme/Makefile.am +++ b/plugins/gme/Makefile.am @@ -112,8 +112,8 @@ game-music-emu-0.5.5/gme/Ym2413_Emu.h\ game-music-emu-0.5.5/gme/Ym2612_Emu.h\ game-music-emu-0.5.5/gme/gme_types.h -gme_la_LDFLAGS = -module +gme_la_LDFLAGS = -module -nostdlib -lsupc++ -gme_la_LIBADD = $(LDADD) -lstdc++ AM_CFLAGS = $(CFLAGS) -I$(gmepath) -std=c99 -DGME_VERSION_055 +AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables endif diff --git a/plugins/gme/cgme.c b/plugins/gme/cgme.c index 5d2920f0..27880d3f 100644 --- a/plugins/gme/cgme.c +++ b/plugins/gme/cgme.c @@ -21,6 +21,18 @@ #include "gme/gme.h" #include "../../deadbeef.h" +int _Unwind_Resume_or_Rethrow; +int _Unwind_RaiseException; +int _Unwind_GetLanguageSpecificData; +int _Unwind_Resume; +int _Unwind_DeleteException; +int _Unwind_GetTextRelBase; +int _Unwind_SetIP; +int _Unwind_GetDataRelBase; +int _Unwind_GetRegionStart; +int _Unwind_SetGR; +int _Unwind_GetIPInfo; + //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(fmt,...) diff --git a/plugins/gtkui/callbacks.c b/plugins/gtkui/callbacks.c index dd26a422..84a585ee 100644 --- a/plugins/gtkui/callbacks.c +++ b/plugins/gtkui/callbacks.c @@ -689,7 +689,7 @@ on_help1_activate (GtkMenuItem *menuitem, gpointer user_data) { char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", _("help.txt")); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), _("help.txt")); show_info_window (fname, _("Help"), &helpwindow); } @@ -702,7 +702,7 @@ on_about1_activate (GtkMenuItem *menuitem, char s[200]; snprintf (s, sizeof (s), _("About DeaDBeeF %s"), VERSION); char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "about.txt"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "about.txt"); show_info_window (fname, s, &aboutwindow); } @@ -715,7 +715,7 @@ on_changelog1_activate (GtkMenuItem *menuitem, char s[200]; snprintf (s, sizeof (s), _("DeaDBeeF %s ChangeLog"), VERSION); char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "ChangeLog"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "ChangeLog"); show_info_window (fname, s, &changelogwindow); } @@ -726,7 +726,7 @@ on_gpl1_activate (GtkMenuItem *menuitem, gpointer user_data) { char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "COPYING.GPLv2"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "COPYING.GPLv2"); show_info_window (fname, "GNU GENERAL PUBLIC LICENSE Version 2", &gplwindow); } @@ -737,7 +737,7 @@ on_lgpl1_activate (GtkMenuItem *menuitem, gpointer user_data) { char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "COPYING.LGPLv2.1"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "COPYING.LGPLv2.1"); show_info_window (fname, "GNU LESSER GENERAL PUBLIC LICENSE Version 2.1", &lgplwindow); } @@ -1079,7 +1079,7 @@ on_translators1_activate (GtkMenuItem *menuitem, char s[200]; snprintf (s, sizeof (s), _("DeaDBeeF Translators")); char fname[PATH_MAX]; - snprintf (fname, sizeof (fname), DOCDIR "/%s", "translators.txt"); + snprintf (fname, sizeof (fname), "%s/%s", deadbeef->get_doc_dir (), "translators.txt"); show_info_window (fname, s, &translatorswindow); } diff --git a/plugins/gtkui/coverart.c b/plugins/gtkui/coverart.c index 7fb50554..ffd2c2b2 100644 --- a/plugins/gtkui/coverart.c +++ b/plugins/gtkui/coverart.c @@ -25,8 +25,6 @@ #include "../artwork/artwork.h" #include "gtkui.h" -#define DEFAULT_COVER_PATH (PREFIX "/share/deadbeef/pixmaps/noartwork.jpg") - //#define trace(...) { fprintf(stderr, __VA_ARGS__); } #define trace(...) @@ -151,9 +149,10 @@ loading_thread (void *none) { g_error_free (error); error = NULL; } - pixbuf = gdk_pixbuf_new_from_file_at_scale (DEFAULT_COVER_PATH, queue->width, queue->width, TRUE, &error); + const char *defpath = coverart_plugin->get_default_cover (); + pixbuf = gdk_pixbuf_new_from_file_at_scale (defpath, queue->width, queue->width, TRUE, &error); if (!pixbuf) { - fprintf (stderr, "gdk_pixbuf_new_from_file_at_scale %s %d failed, error: %s\n", DEFAULT_COVER_PATH, queue->width, error->message); + fprintf (stderr, "gdk_pixbuf_new_from_file_at_scale %s %d failed, error: %s\n", defpath, queue->width, error->message); } } if (error) { diff --git a/plugins/gtkui/gtkui.c b/plugins/gtkui/gtkui.c index 35da1527..1c2b9b9f 100644 --- a/plugins/gtkui/gtkui.c +++ b/plugins/gtkui/gtkui.c @@ -900,7 +900,8 @@ void gtkui_thread (void *ctx) { // let's start some gtk g_thread_init (NULL); - add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); +// add_pixmap_directory (PREFIX "/share/deadbeef/pixmaps"); + add_pixmap_directory (deadbeef->get_pixmap_dir ()); gdk_threads_init (); gdk_threads_enter (); diff --git a/plugins/mpris/Makefile.am b/plugins/mpris/Makefile.am new file mode 100644 index 00000000..da59d242 --- /dev/null +++ b/plugins/mpris/Makefile.am @@ -0,0 +1,8 @@ +if HAVE_MPRIS +mprisdir = $(libdir)/$(PACKAGE) +pkglib_LTLIBRARIES = mpris.la +AM_CFLAGS = $(CFLAGS) $(MPRIS_DEPS_CFLAGS) -std=c99 +mpris_la_SOURCES = mpris.c mpris-spec.h +mpris_la_LDFLAGS = -module +mpris_la_LIBADD = $(LDADD) $(MPRIS_DEPS_LIBS) +endif diff --git a/plugins/mpris/mpris-spec.h b/plugins/mpris/mpris-spec.h new file mode 100644 index 00000000..dba8f542 --- /dev/null +++ b/plugins/mpris/mpris-spec.h @@ -0,0 +1,91 @@ +#define MPRIS_BUS_NAME_PREFIX "org.mpris.MediaPlayer2" +#define MPRIS_OBJECT_NAME "/org/mpris/MediaPlayer2" + +#define MPRIS_ROOT_INTERFACE "org.mpris.MediaPlayer2" +#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player" +#define MPRIS_TRACKLIST_INTERFACE "org.mpris.MediaPlayer2.TrackList" + +const char *mpris_introspection_xml = + "<node>" + " <interface name='org.mpris.MediaPlayer2'>" + " <method name='Raise'/>" + " <method name='Quit'/>" + " <property name='CanQuit' type='b' access='read'/>" + " <property name='CanRaise' type='b' access='read'/>" + " <property name='HasTrackList' type='b' access='read'/>" + " <property name='Identity' type='s' access='read'/>" + " <property name='DesktopEntry' type='s' access='read'/>" + " <property name='SupportedUriSchemes' type='as' access='read'/>" + " <property name='SupportedMimeTypes' type='as' access='read'/>" + " </interface>" + " <interface name='org.mpris.MediaPlayer2.Player'>" + " <method name='Next'/>" + " <method name='Previous'/>" + " <method name='Pause'/>" + " <method name='PlayPause'/>" + " <method name='Stop'/>" + " <method name='Play'/>" + " <method name='Seek'>" + " <arg direction='in' name='Offset' type='x'/>" + " </method>" + " <method name='SetPosition'>" + " <arg direction='in' name='TrackId' type='o'/>" + " <arg direction='in' name='Position' type='x'/>" + " </method>" + " <method name='OpenUri'>" + " <arg direction='in' name='Uri' type='s'/>" + " </method>" + " <signal name='Seeked'>" + " <arg name='Position' type='x'/>" + " </signal>" + " <property name='PlaybackStatus' type='s' access='read'/>" + " <property name='LoopStatus' type='s' access='readwrite'/>" + " <property name='Rate' type='d' access='readwrite'/>" + " <property name='Shuffle' type='b' access='readwrite'/>" + " <property name='Metadata' type='a{sv}' access='read'/>" + " <property name='Volume' type='d' access='readwrite'/>" + " <property name='Position' type='x' access='read'/>" + " <property name='MinimumRate' type='d' access='read'/>" + " <property name='MaximumRate' type='d' access='read'/>" + " <property name='CanGoNext' type='b' access='read'/>" + " <property name='CanGoPrevious' type='b' access='read'/>" + " <property name='CanPlay' type='b' access='read'/>" + " <property name='CanPause' type='b' access='read'/>" + " <property name='CanSeek' type='b' access='read'/>" + " <property name='CanControl' type='b' access='read'/>" + " </interface>" + " <interface name='org.mpris.MediaPlayer2.TrackList'>" + " <method name='GetTracksMetadata'>" + " <arg direction='in' name='TrackIds' type='ao'/>" + " <arg direction='out' name='Metadata' type='aa{sv}'/>" + " </method>" + " <method name='AddTrack'>" + " <arg direction='in' name='Uri' type='s'/>" + " <arg direction='in' name='AfterTrack' type='o'/>" + " <arg direction='in' name='SetAsCurrent' type='b'/>" + " </method>" + " <method name='RemoveTrack'>" + " <arg direction='in' name='TrackId' type='o'/>" + " </method>" + " <method name='GoTo'>" + " <arg direction='in' name='TrackId' type='o'/>" + " </method>" + " <signal name='TrackListReplaced'>" + " <arg name='Tracks' type='ao'/>" + " <arg name='CurrentTrack' type='o'/>" + " </signal>" + " <signal name='TrackAdded'>" + " <arg name='Metadata' type='a{sv}'/>" + " <arg name='AfterTrack' type='o'/>" + " </signal>" + " <signal name='TrackRemoved'>" + " <arg name='TrackId' type='o'/>" + " </signal>" + " <signal name='TrackMetadataChanged'>" + " <arg name='TrackId' type='o'/>" + " <arg name='Metadata' type='a{sv}'/>" + " </signal>" + " <property name='Tracks' type='ao' access='read'/>" + " <property name='CanEditTracks' type='b' access='read'/>" + " </interface>" + "</node>"; diff --git a/plugins/mpris/mpris.c b/plugins/mpris/mpris.c new file mode 100644 index 00000000..040de84c --- /dev/null +++ b/plugins/mpris/mpris.c @@ -0,0 +1,1310 @@ +/* + Sound Menu plugin for DeaDBeeF + Copyright (C) 2010 Robert Y <Decatf@gmail.com> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include <time.h> +#include <gio/gio.h> +//#include <libindicate/server.h> + +#include <deadbeef/deadbeef.h> +#include "../artwork/artwork.h" + +#include "mpris-spec.h" + +#define trace(...) +#define ENTRY_OBJECT_PATH_PREFIX "/org/mpris/MediaPlayer2/Track/" +#define DESKTOP_FILE "/usr/share/applications/deadbeef.desktop" + +static DB_dsp_t plugin; +DB_functions_t *deadbeef; + +static short enabled = 0; +DB_artwork_plugin_t *coverart_plugin = NULL; + +//static IndicateServer *indicate_server; +static GDBusConnection *connection; +static guint name_own_id; +static guint root_id; +static guint player_id; + +static GHashTable *player_property_changes = NULL; +static guint player_property_emit_id = 0; +uintptr_t hash_table_mtx; +uintptr_t emit_id_mtx; + +double prev_track_pos; + + +static gboolean check_can_go_next(); +static gboolean check_can_go_prev(); +void cover_avail_callback (const char *fname, const char *artist, const char *album, void *user_data); + + +/* MPRIS root interface */ +static void +handle_root_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer *user_data) +{ + if (g_strcmp0 (object_path, MPRIS_OBJECT_NAME) != 0 || + g_strcmp0 (interface_name, MPRIS_ROOT_INTERFACE) != 0) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Method %s.%s not supported", + interface_name, + method_name); + return; + } + + if (g_strcmp0 (method_name, "Raise") == 0) { + /* TODO: need way to talk to gtkui plugin */ + g_dbus_method_invocation_return_value (invocation, NULL); + } else if (g_strcmp0 (method_name, "Quit") == 0) { + deadbeef->quit(); + g_dbus_method_invocation_return_value (invocation, NULL); + } else { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Method %s.%s not supported", + interface_name, + method_name); + } +} + +static GVariant * +get_root_property (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *property_name, + GError **error, + gpointer *user_data) +{ + if (g_strcmp0 (object_path, MPRIS_OBJECT_NAME) != 0 || + g_strcmp0 (interface_name, MPRIS_ROOT_INTERFACE) != 0) { + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Property %s.%s not supported", + interface_name, + property_name); + return NULL; + } + + if (g_strcmp0 (property_name, "CanQuit") == 0) { + return g_variant_new_boolean (TRUE); + } else if (g_strcmp0 (property_name, "CanRaise") == 0) { + return g_variant_new_boolean (FALSE); + } else if (g_strcmp0 (property_name, "HasTrackList") == 0) { + return g_variant_new_boolean (FALSE); + } else if (g_strcmp0 (property_name, "Identity") == 0) { + return g_variant_new_string ("DeaDBeef audio player"); + } else if (g_strcmp0 (property_name, "DesktopEntry") == 0) { + return g_variant_new_string(DESKTOP_FILE); + } else if (g_strcmp0 (property_name, "SupportedUriSchemes") == 0) { + /* need some way to fetch these values */ + const char *fake_supported_schemes[] = { + "file", "http", "cdda", "smb", "sftp", NULL + }; + return g_variant_new_strv (fake_supported_schemes, -1); + } else if (g_strcmp0 (property_name, "SupportedMimeTypes") == 0) { + const char *fake_supported_mimetypes[] = { + "application/ogg", "audio/x-vorbis+ogg", "audio/x-flac", "audio/mpeg", NULL + }; + return g_variant_new_strv (fake_supported_mimetypes, -1); + } + + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Property %s.%s not supported", + interface_name, + property_name); + return NULL; +} + +static const GDBusInterfaceVTable root_vtable = +{ + (GDBusInterfaceMethodCallFunc) handle_root_method_call, + (GDBusInterfaceGetPropertyFunc) get_root_property, + NULL +}; + + +/* MPRIS player interface */ + +static void +playpause () { + DB_output_t *output = NULL; + int state = -1; + + output = deadbeef->get_output(); + if (output == NULL) { + //printf ("[mpris] playpause(): Could not get output\n"); + return; + } + + state = output->state(); + + if (state == OUTPUT_STATE_STOPPED) { + deadbeef->playback_play(); + } + else if (state == OUTPUT_STATE_PLAYING) { + deadbeef->playback_pause(); + } + else if (state == OUTPUT_STATE_PAUSED) { + deadbeef->playback_play(); + } + else { + printf ("[mpris] playpause(): Error\n"); + } +} + +static void +seek (gint64 offset) +{ + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + //printf ("[mpris] seek(): No play item\n"); + return; + } + + // Play position in seconds + float playpos = deadbeef->streamer_get_playpos(); + float newpos = playpos+(offset/G_USEC_PER_SEC); + + if (newpos < 0) { + newpos = 0.f; + } else if (newpos > play_item->playtime) { + deadbeef->playback_next(); + return; + } + + deadbeef->pl_item_unref(play_item); + deadbeef->streamer_seek (newpos); +} + +static GVariant * +get_playback_status (void) +{ + GVariant *v = NULL; + DB_output_t *output = NULL; + int state = -1; + + output = deadbeef->get_output(); + if (output == NULL) { + printf ("[mpris] get_playback_status: Could not get output\n"); + return v; + } + + state = output->state(); + + if (state == OUTPUT_STATE_STOPPED) { + v = g_variant_new_string ("Stopped"); + //printf ("[mpris] get_playback_status: Stopped\n"); + } + else if (state == OUTPUT_STATE_PLAYING) { + v = g_variant_new_string ("Playing"); + //printf ("[mpris] get_playback_status: Playing\n"); + } + else if (state == OUTPUT_STATE_PAUSED) { + v = g_variant_new_string ("Paused"); + //printf ("[mpris] get_playback_status: Paused\n"); + } + else { + //printf ("[mpris] get_playback_status: Error\n"); + } + + return v; +} + +static GVariant * +get_loop_status (void) +{ + GVariant *v = NULL; + int loop_mode; + loop_mode = deadbeef->conf_get_int ("playback.loop", 0); + + if (loop_mode == PLAYBACK_MODE_NOLOOP) { + //printf ("[mpris] get_loop_status: None\n"); + v = g_variant_new_string ("None"); + } + else if (loop_mode == PLAYBACK_MODE_LOOP_ALL) { + //printf ("[mpris] get_loop_status: Playlist\n"); + v = g_variant_new_string ("Playlist"); + } + else if (loop_mode == PLAYBACK_MODE_LOOP_SINGLE) { + //printf ("[mpris] get_loop_status: Track\n"); + v = g_variant_new_string ("Track"); + } + + return v; +} + +static gboolean +set_loop_status (GVariant *value) +{ + gchar *loop_status; + g_variant_get (value, "s", &loop_status); + + if (g_strcmp0 (loop_status, "None") == 0) { + deadbeef->conf_set_int ("playback.loop", PLAYBACK_MODE_NOLOOP); + return TRUE; + } else if (g_strcmp0 (loop_status, "Playlist") == 0) { + deadbeef->conf_set_int ("playback.loop", PLAYBACK_MODE_LOOP_ALL); + return TRUE; + } else if (g_strcmp0 (loop_status, "Track") == 0) { + deadbeef->conf_set_int ("playback.loop", PLAYBACK_MODE_LOOP_SINGLE); + return TRUE; + } + return FALSE; +} + +static GVariant * +get_shuffle (void) +{ + GVariant *v = NULL; + int shuffle_mode; + shuffle_mode = deadbeef->conf_get_int ("playback.order", 0); + + if (shuffle_mode == PLAYBACK_ORDER_LINEAR) { + //printf ("[mpris] get_shuffle: None\n"); + v = g_variant_new_boolean (FALSE); + } + else if (shuffle_mode == PLAYBACK_ORDER_SHUFFLE) { + //printf ("[mpris] get_shuffle: Playlist\n"); + v = g_variant_new_boolean (TRUE); + } + else if (shuffle_mode == PLAYBACK_ORDER_RANDOM) { + //printf ("[mpris] get_shuffle: Track\n"); + v = g_variant_new_boolean (TRUE); + } + + return v; +} + +static gboolean +set_shuffle (GVariant *value) +{ + gboolean shuffle; + g_variant_get (value, "b", &shuffle); + + if (shuffle == FALSE) { + deadbeef->conf_set_int ("playback.order",PLAYBACK_ORDER_LINEAR); + } else { + deadbeef->conf_set_int ("playback.order", PLAYBACK_ORDER_SHUFFLE); + } + + return TRUE; +} + +static GVariant * +get_metadata (void) +{ + GVariantBuilder *b; + DB_playItem_t *play_item = NULL; + + b = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}")); + + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + //printf ("[mpris] get_metadata: No play item\n"); + return g_variant_builder_end (b);; + } + + char buf[200]; + int buf_size = sizeof(buf); + + gchar *trackid_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, DB_COLUMN_FILENUMBER, NULL); + trackid_str = g_strdup_printf(ENTRY_OBJECT_PATH_PREFIX "%s", buf); + //printf ("[mpris] get_metadata: trackid %s\n", trackid_str); + g_variant_builder_add (b, "{sv}", "mpris:trackid", g_variant_new("s", trackid_str)); + g_free(trackid_str); + + gchar *title_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%t"); + title_str = g_strdup_printf("%s", buf); + //printf ("[mpris] get_metadata: title_str %s\n", title_str); + g_variant_builder_add (b, "{sv}", "xesam:title", g_variant_new("s", title_str)); + g_free(title_str); + + gchar *artist_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%a"); + artist_str = g_strdup_printf("%s", buf); + //printf ("[mpris] get_metadata: artist_str %s\n", artist_str); + const char *artist_strv[] = {artist_str, NULL}; + g_variant_builder_add (b, "{sv}", "xesam:artist", g_variant_new_strv (artist_strv, -1)); + + + gchar *albumartist_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%B"); + albumartist_str = g_strdup_printf("%s", buf); + //printf ("[mpris] get_metadata: albumartist_str %s\n", albumartist_str); + const char *albumartist_strv[] = {albumartist_str, NULL}; + g_variant_builder_add (b, "{sv}", "xesam:albumArtist", g_variant_new_strv (albumartist_strv, -1)); + g_free(albumartist_str); + + gchar *album_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%b"); + album_str = g_strdup_printf("%s", buf); + //printf ("[mpris] get_metadata: album_str %s\n", album_str); + g_variant_builder_add (b, "{sv}", "xesam:album", g_variant_new("s", album_str)); + + if (coverart_plugin != NULL) { + gchar *image_fname = coverart_plugin->get_album_art (play_item->fname, + artist_str, + album_str, + cover_avail_callback, NULL); + g_free(artist_str); + g_free(album_str); + if (image_fname) { + gchar *art_str = g_strjoin (NULL, "file://", image_fname, NULL); + g_variant_builder_add (b, "{sv}", "mpris:artUrl", g_variant_new("s", art_str)); + //printf ("[mpris] get_metadata: image_fname %s\n", art_str); + g_free (image_fname); + g_free (art_str); + } + } + + gchar *track_num_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%n"); + track_num_str = g_strdup_printf("%s", buf); + gint64 track_num = g_ascii_strtoll (track_num_str, NULL, 10); + //printf ("[mpris] get_metadata: tracknum %ld\n", (long int) track_num); + g_variant_builder_add (b, "{sv}", "xesam:trackNumber", g_variant_new("x", track_num)); + g_free(track_num_str); + + gint64 length = play_item->playtime * G_USEC_PER_SEC; + //printf ("[mpris] get_metadata: length %ld\n", (long int) length); + g_variant_builder_add (b, "{sv}", "mpris:length", g_variant_new("x", length)); + + gchar *genre_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%g"); + genre_str = g_strdup_printf("%s", buf); + //printf ("[mpris] get_metadata: genre_str %s\n", genre_str); + const char *genre_strv[] = {genre_str, NULL}; + g_variant_builder_add (b, "{sv}", "xesam:genre", g_variant_new_strv (genre_strv, -1)); + g_free(genre_str); + + gchar *comment_str; + deadbeef->pl_format_title (play_item, -1, buf, buf_size, -1, "%c"); + comment_str = g_strdup_printf("%s", buf); + //printf ("[mpris] get_metadata: comment_str %s\n", comment_str); + const char *comment_strv[] = {comment_str, NULL}; + g_variant_builder_add (b, "{sv}", "xesam:comment", g_variant_new_strv (comment_strv, -1)); + g_free(comment_str); + + deadbeef->pl_item_unref(play_item); + //printf ("[mpris] get_metadata: done\n"); + + return g_variant_builder_end (b); +} + +static GVariant * +get_position (void) +{ + GVariant *v = NULL; + + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + //printf ("[mpris] get_position: No play item\n"); + return v; + } + + deadbeef->pl_item_unref(play_item); + + float playpos = deadbeef->streamer_get_playpos(); + return g_variant_new_int64 (playpos*G_USEC_PER_SEC); +} + +void +set_position (const gchar* track_id, gint64 position) +{ + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + //printf ("[mpris] set_position: No play item\n"); + return; + } + + gint64 play_time = play_item->playtime*G_USEC_PER_SEC; + if (position > play_time) + return; + + char *trackid_str; + trackid_str = g_strdup_printf(ENTRY_OBJECT_PATH_PREFIX "%s", play_item->fname); + if (g_strcmp0(trackid_str, track_id) != 0) { + g_free(trackid_str); + return; + } + g_free(trackid_str); + deadbeef->pl_item_unref(play_item); + + deadbeef->streamer_seek((float)position/G_USEC_PER_SEC); +} + +static GVariant * +get_canplay (void) +{ + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + return g_variant_new_boolean(FALSE); + } + deadbeef->pl_item_unref(play_item); + + return g_variant_new_boolean(TRUE); +} + +static GVariant * +get_canpause (void) +{ + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + return g_variant_new_boolean(FALSE); + } + deadbeef->pl_item_unref(play_item); + + DB_output_t *output = NULL; + output = deadbeef->get_output(); + if (output != NULL) { + if (output->state == OUTPUT_STATE_STOPPED) { + return g_variant_new_boolean(FALSE); + } + } + //else printf ("[mpris] get_canpause: Could not get output\n"); + + return g_variant_new_boolean(TRUE); +} + + +/// +// PropertiesChanged signal +/// +/* +static void +emit_property_change (const gchar *name, GVariant *value) +{ + GError *error = NULL; + GVariantBuilder *properties; + GVariant *properties_changed = NULL; + const char *invalidated[] = { NULL }; + + properties = g_variant_builder_new (G_VARIANT_TYPE("a{sv}")); + g_variant_builder_add (properties, "{sv}", name, value); + + properties_changed = g_variant_new ("(sa{sv}^as)", + MPRIS_PLAYER_INTERFACE, + properties, + invalidated); + g_variant_builder_unref (properties); + + g_dbus_connection_emit_signal (connection, + NULL, + MPRIS_OBJECT_NAME, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + properties_changed, + &error); + + if (error != NULL) { + printf ("[mpris] Unable to send MPRIS property changes: %s", error->message); + g_clear_error (&error); + } + //printf ("[mpris] Sent property change: %s\n", name); +} +*/ + +static gboolean +emit_player_properties (gpointer data) +{ + //printf ("[mpris] emit_player_properties\n"); + + GError *error = NULL; + GVariantBuilder *properties; + GVariant *properties_changed = NULL; + const char *invalidated[] = { NULL }; + GHashTableIter iter; + gpointer propname, propvalue; + + + deadbeef->mutex_lock(hash_table_mtx); + properties = g_variant_builder_new (G_VARIANT_TYPE("a{sv}")); + g_hash_table_iter_init (&iter, player_property_changes); + while (g_hash_table_iter_next (&iter, &propname, &propvalue)) { + g_variant_builder_add (properties, "{sv}", propname, propvalue); + } + g_hash_table_destroy (player_property_changes); + player_property_changes = NULL; + deadbeef->mutex_unlock(hash_table_mtx); + + properties_changed = g_variant_new ("(sa{sv}^as)", + MPRIS_PLAYER_INTERFACE, + properties, + invalidated); + g_variant_builder_unref (properties); + + g_dbus_connection_emit_signal (connection, + NULL, + MPRIS_OBJECT_NAME, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + properties_changed, + &error); + + deadbeef->mutex_lock(emit_id_mtx); + player_property_emit_id = 0; + deadbeef->mutex_unlock(emit_id_mtx); + + if (error != NULL) { + printf ("[mpris] Unable to send MPRIS property changes: %s", error->message); + g_clear_error (&error); + } + + //printf ("[mpris] emit_player_properties: done %d\n", player_property_emit_id); + + return FALSE; +} + + +static void +add_property_change (const gchar *property, GVariant *value) +{ + //printf ("[mpris] add_property_change\n"); + + deadbeef->mutex_lock(hash_table_mtx); + if (player_property_changes == NULL) { + player_property_changes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + } + g_hash_table_insert (player_property_changes, g_strdup (property), value); + deadbeef->mutex_unlock(hash_table_mtx); + + if (player_property_emit_id == 0) { + deadbeef->mutex_lock(emit_id_mtx); + player_property_emit_id = g_idle_add ((GSourceFunc)emit_player_properties, NULL); + //printf ("[mpris] add_property_change: done\n"); + deadbeef->mutex_unlock(emit_id_mtx); + + return; + } + + //printf ("[mpris] add_property_change: source exists %d\n", player_property_emit_id); +} + + +// Events from DeaDBeef + +/* + PlaybackStatus: Working + LoopStatus: need event on playback.loop config change + Rate: not supported + Shuffle: need event on playback.order config change + Metadata: Working + Volume: Working(?) + MinimumRate: not supported + MaximumRate: not supported + CanGoNext: check&emit value on song start + CanGoPrevious: check&emit value on song start + CanPlay: not implemented + CanPause: dunno how to check this + CanSeek: dunno how to check this +*/ + +static int +db_paused (DB_event_t *ev, uintptr_t data) +{ + //printf ("[mpris] db_paused: PlaybackStatus\n"); + //emit_property_change ("PlaybackStatus", get_playback_status()); + add_property_change ("PlaybackStatus", get_playback_status()); + return 0; +} + +static int +db_track_info_changed (DB_event_t *ev, uintptr_t data) +{ + + //printf ("[mpris] db_track_info_changed\n"); + GVariant *v = NULL; + DB_output_t *output = NULL; + int state = -1; + + // Playback state change + output = deadbeef->get_output(); + if (output == NULL) { + //printf ("[mpris] db_track_info_changed(): Could not get output\n"); + return 0; + } + + state = output->state(); + if (state == OUTPUT_STATE_STOPPED) { + v = g_variant_new_string ("Stopped"); + //printf ("[mpris] db_track_info_changed: Stopped\n"); + } + else if (state == OUTPUT_STATE_PLAYING) { + v = g_variant_new_string ("Playing"); + //printf ("[mpris] db_track_info_changed: Playing\n"); + } + else if (state == OUTPUT_STATE_PAUSED) { + v = g_variant_new_string ("Paused"); + //printf ("[mpris] db_track_info_changed: Paused\n"); + } + else { + //printf ("[mpris] db_track_info_changed: Error\n"); + } + //printf ("[mpris] db_track_info_changed: PlaybackStatus\n"); + //emit_property_change ("PlaybackStatus", v); + add_property_change ("PlaybackStatus", v); + + // Seek + + // A change of greater than 1 second from the + // previous track position is considered a seek. + // (including the time change from track changes) + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item != NULL) { + + float playpos = deadbeef->streamer_get_playpos(); + float diff = playpos - prev_track_pos; + if (diff > 1.f || diff < -1.f) { + //printf ("[mpris] db_track_info_changed(): Seeked: %f\n", diff); + + GError *error = NULL; + g_dbus_connection_emit_signal (connection, + NULL, + MPRIS_OBJECT_NAME, + MPRIS_PLAYER_INTERFACE, + "Seeked", + g_variant_new ("(x)", (int)(deadbeef->streamer_get_playpos())*G_USEC_PER_SEC), + &error); + if (error != NULL) { + g_warning ("[mpris] Unable to set MPRIS Seeked signal: %s", error->message); + g_clear_error (&error); + } + } + deadbeef->pl_item_unref(play_item); + } + + + // Metadata changes + v = get_metadata(); + if (v != NULL) { + //emit_property_change ("Metadata", v); + add_property_change ("Metadata", v); + } + else { + printf ("[mpris] db_track_info_changed: no track\n"); + } + + return 0; +} + +static int +db_song_changed (DB_event_t *ev, uintptr_t data) +{ +/* + printf ("[mpris] db_song_changed\n"); + GVariant *v = NULL; + v = get_metadata(); + if (v != NULL) { + emit_property_change ("Metadata", v); + } +*/ + return 0; +} + +static int +db_song_started (DB_event_t *ev, uintptr_t data) +{ + //printf ("[mpris] db_song_started\n"); + + GVariant *v = NULL; + v = get_metadata(); + if (v != NULL) { + //emit_property_change ("Metadata", v); + add_property_change ("Metadata", v); + } + + gboolean can_go_next = check_can_go_next(); + //emit_property_change ("CanGoNext", g_variant_new_boolean(can_go_next)); + add_property_change ("CanGoNext", g_variant_new_boolean(can_go_next)); + + gboolean can_go_prev = check_can_go_prev(); + //emit_property_change ("CanGoPrev", g_variant_new_boolean(can_go_prev)); + add_property_change ("CanGoPrev", g_variant_new_boolean(can_go_prev)); + + return 0; +} + +static int +db_song_finished (DB_event_t *ev, uintptr_t data) +{ + //printf ("[mpris] db_song_finished\n"); +/* + GVariant *v = NULL; + v = get_metadata(); + if (v != NULL) { + emit_property_change ("Metadata", v); + } +*/ + return 0; +} + +static int +db_volume_changed (DB_event_t *ev, uintptr_t data) +{ + gdouble volume = deadbeef->volume_get_amp(); + if (volume < 0) + volume = 0; + //emit_property_change ("Volume", g_variant_new_double (volume)); + add_property_change ("Volume", g_variant_new_double (volume)); + printf ("[mpris] db_volume_changed: Volume %lf\n", volume); + return 0; +} + +static int +db_frameupdate (DB_event_t *ev, uintptr_t data) +{ + + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item != NULL) { + prev_track_pos = deadbeef->streamer_get_playpos(); + deadbeef->pl_item_unref(play_item); + } + return 0; +} + +static int +db_playlist_switched (DB_event_t *ev, uintptr_t data) +{ + //printf ("[mpris] db_playlist_switched\n"); + return 0; +} + +static int +db_activate (DB_event_t *ev, uintptr_t data) +{ + printf ("[mpris] db_activate()\n"); + return 0; +} + +// non callback emit helper functions +static gboolean +check_can_go_next() { + + + int loop_mode = deadbeef->conf_get_int ("playback.loop", 0); + if (loop_mode == PLAYBACK_MODE_NOLOOP) { + int playlist_size = deadbeef->pl_getcount(deadbeef->plt_get_curr()); + + // Check if playlist is on last track + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + deadbeef->pl_item_unref(play_item); + return FALSE; + } + + int pl_current = deadbeef->pl_get_idx_of(play_item); + if (pl_current == playlist_size) { + deadbeef->pl_item_unref(play_item); + return FALSE; + } + deadbeef->pl_item_unref(play_item); + //printf ("[mpris] check_cangonext() plt count: %d plt current: %d\n", playlist_size, pl_current); + } + + return TRUE; +} + +static gboolean +check_can_go_prev() { + + int loop_mode = deadbeef->conf_get_int ("playback.loop", 0); + if (loop_mode == PLAYBACK_MODE_NOLOOP) { + // Check if playlist is on first track + DB_playItem_t *play_item = NULL; + play_item = deadbeef->streamer_get_playing_track (); + if (play_item == NULL) { + return FALSE; + } + + int pl_current = deadbeef->pl_get_idx_of(play_item); + if (pl_current == 0) { + deadbeef->pl_item_unref(play_item); + return TRUE; + } + deadbeef->pl_item_unref(play_item); + //printf ("[mpris] check_cangoprev() plt current: %d\n", pl_current); + } + return TRUE; +} + +// Artwork plugin callback +void cover_avail_callback (const char *fname, const char *artist, const char *album, void *user_data) { + //printf ("[mpris] cover_avail_callback\n"); + GVariant *v = get_metadata(); + //emit_property_change ("Metadata", v); + add_property_change ("Metadata", v); +} + + +/// +// MediaPlayer2.Player handlers +/// +static void +handle_result (GDBusMethodInvocation *invocation) +{ + g_dbus_method_invocation_return_error_literal (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Unknown error"); +} + +static void +handle_player_method_call (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer *user_data) +{ + + if (g_strcmp0 (object_path, MPRIS_OBJECT_NAME) != 0 || + g_strcmp0 (interface_name, MPRIS_PLAYER_INTERFACE) != 0) { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Method %s.%s not supported", + interface_name, + method_name); + return; + } + + if (g_strcmp0 (method_name, "Next") == 0) { + deadbeef->playback_next(); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "Previous") == 0) { + deadbeef->playback_prev(); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "PlayPause") == 0) { + playpause(); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "Stop") == 0) { + deadbeef->playback_stop(); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "Play") == 0) { + deadbeef->playback_play(); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "Seek") == 0) { + gint64 offset; + g_variant_get (parameters, "(x)", &offset); + seek (offset); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "SetPosition") == 0) { + gint64 position; + const gchar *track_id; + g_variant_get (parameters, "&ox", &track_id, &position); + set_position (track_id, position); + handle_result (invocation); + } else if (g_strcmp0 (method_name, "OpenUri") == 0) { + /* TODO: implement this */ + const char *uri; + g_variant_get (parameters, "(&s)", &uri); + + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Method %s.%s not supported", + interface_name, + method_name); + + } else { + g_dbus_method_invocation_return_error (invocation, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Method %s.%s not supported", + interface_name, + method_name); + } +} + +static GVariant * +get_player_property (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *property_name, + GError **error, + gpointer *user_data) +{ + + //printf ("[mpris] get_player_property\n"); + if (g_strcmp0 (property_name, "PlaybackStatus") == 0) { + return get_playback_status(); + } else if (g_strcmp0 (property_name, "LoopStatus") == 0) { + return get_loop_status (); + } else if (g_strcmp0 (property_name, "Rate") == 0) { + return g_variant_new_double (1.0); + } else if (g_strcmp0 (property_name, "Shuffle") == 0) { + return get_shuffle (); + } else if (g_strcmp0 (property_name, "Metadata") == 0) { + //printf ("[mpris] get_player_property: Metadata\n"); + return get_metadata (); + } else if (g_strcmp0 (property_name, "Volume") == 0) { + gdouble volume = deadbeef->volume_get_amp(); + return g_variant_new_double(volume); + } else if (g_strcmp0 (property_name, "Position") == 0) { + return get_position(); + } else if (g_strcmp0 (property_name, "MinimumRate") == 0) { + return g_variant_new_double (1.0); + } else if (g_strcmp0 (property_name, "MaximumRate") == 0) { + return g_variant_new_double (1.0); + } else if (g_strcmp0 (property_name, "CanGoNext") == 0) { + return g_variant_new_boolean (TRUE); + } else if (g_strcmp0 (property_name, "CanGoPrevious") == 0) { + return g_variant_new_boolean (TRUE); + } else if (g_strcmp0 (property_name, "CanPlay") == 0) { + return get_canplay(); + } else if (g_strcmp0 (property_name, "CanPause") == 0) { + return get_canpause(); + } else if (g_strcmp0 (property_name, "CanSeek") == 0) { + /* Not sure how to check if a track is seekable + return true anyways */ + return g_variant_new_boolean(TRUE); + } else if (g_strcmp0 (property_name, "CanControl") == 0) { + return g_variant_new_boolean(TRUE); + } + + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Property %s.%s not supported", + interface_name, + property_name); + return NULL; +} + +static gboolean +set_player_property (GDBusConnection *connection, + const char *sender, + const char *object_path, + const char *interface_name, + const char *property_name, + GVariant *value, + GError **error, + gpointer *user_data) +{ + + if (g_strcmp0 (object_path, MPRIS_OBJECT_NAME) != 0 || + g_strcmp0 (interface_name, MPRIS_PLAYER_INTERFACE) != 0) { + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "%s:%s not supported", + object_path, + interface_name); + return FALSE; + } + + if (g_strcmp0 (property_name, "LoopStatus") == 0) { + return set_loop_status(value); + } else if (g_strcmp0 (property_name, "Rate") == 0) { + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "[mpris] Can't modify playback rate"); + return FALSE; + } else if (g_strcmp0 (property_name, "Shuffle") == 0) { + set_shuffle (value); + return TRUE; + } else if (g_strcmp0 (property_name, "Volume") == 0) { + gdouble volume; + volume = g_variant_get_double (value); + if (volume < 0) volume = 0; + deadbeef->volume_set_amp((float)volume); + printf ("[mpris] set_player_property: volume %lf\n", volume); + return TRUE; + } + + g_set_error (error, + G_DBUS_ERROR, + G_DBUS_ERROR_NOT_SUPPORTED, + "Property %s.%s not supported", + interface_name, + property_name); + return FALSE; +} + +static const GDBusInterfaceVTable player_vtable = +{ + (GDBusInterfaceMethodCallFunc) handle_player_method_call, + (GDBusInterfaceGetPropertyFunc) get_player_property, + (GDBusInterfaceSetPropertyFunc) set_player_property, +}; + + +/* DeaDBeef plugin */ + +static void +name_acquired_cb (GDBusConnection *connection, const char *name, gpointer *user_data) +{ + trace ("[mpris] Successfully acquired dbus name %s\n", name); +} + +static void +name_lost_cb (GDBusConnection *connection, const char *name, gpointer *user_data) +{ + trace ("[mpris] Lost dbus name %s\n", name); +} + +static gboolean +mpris_begin () +{ +// indicate_server = indicate_server_ref_default (); +// indicate_server_set_type (indicate_server, "music.deadbeef"); +// indicate_server_set_desktop_file (indicate_server, DESKTOP_FILE); +// indicate_server_show (indicate_server); + + GError *error = NULL; + GDBusInterfaceInfo *ifaceinfo; + GDBusNodeInfo *node_info; + + connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + if (error != NULL) { + g_warning ("[mpris] Unnable to connect to D-Bus session bus: %s", error->message); + return FALSE; + } + + // Introspection data + node_info = g_dbus_node_info_new_for_xml (mpris_introspection_xml, &error); + if (error != NULL) { + g_warning ("[mpris] Unnable to read MPRIS interface specification: %s", error->message); + return -1; + } + + // Root interface + ifaceinfo = g_dbus_node_info_lookup_interface (node_info, MPRIS_ROOT_INTERFACE); + root_id = g_dbus_connection_register_object (connection, + MPRIS_OBJECT_NAME, + ifaceinfo, + &root_vtable, + NULL, + NULL, + &error); + if (error != NULL) { + g_warning ("[mpris] Unnable to register MPRIS root interface: %s", error->message); + return FALSE; + } + + // Player interface + ifaceinfo = g_dbus_node_info_lookup_interface (node_info, MPRIS_PLAYER_INTERFACE); + player_id = g_dbus_connection_register_object (connection, + MPRIS_OBJECT_NAME, + ifaceinfo, + &player_vtable, + NULL, + NULL, + &error); + if (error != NULL) { + g_warning ("[mpris] Unable to register MPRIS player interface: %s", error->message); + g_error_free (error); + return FALSE; + } + + name_own_id = g_bus_own_name (G_BUS_TYPE_SESSION, + MPRIS_BUS_NAME_PREFIX ".deadbeef", + G_BUS_NAME_OWNER_FLAGS_NONE, + NULL, + (GBusNameAcquiredCallback) name_acquired_cb, + (GBusNameLostCallback) name_lost_cb, + NULL, + NULL); + + + // DeaDBeef event callbacks + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (db_paused), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (db_track_info_changed), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (db_song_changed), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGSTARTED, DB_CALLBACK (db_song_started), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_SONGFINISHED, DB_CALLBACK (db_song_finished), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (db_volume_changed), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (db_activate), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTSWITCH, DB_CALLBACK (db_playlist_switched), 0); + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (db_frameupdate), 0); + + // + player_property_changes = NULL; + player_property_emit_id = 0; + hash_table_mtx = deadbeef->mutex_create(); + emit_id_mtx = deadbeef->mutex_create(); + + //printf ("[mpris] begin %d\n", name_own_id); + return TRUE; +} + +static void +mpris_end () +{ + if (coverart_plugin) { + //coverart_plugin->plugin.plugin.stop (); + coverart_plugin = NULL; + } + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PAUSED, DB_CALLBACK (db_paused), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_TRACKINFOCHANGED, DB_CALLBACK (db_track_info_changed), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGCHANGED, DB_CALLBACK (db_song_changed), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGSTARTED, DB_CALLBACK (db_song_started), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_SONGFINISHED, DB_CALLBACK (db_song_finished), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_VOLUMECHANGED, DB_CALLBACK (db_volume_changed), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_ACTIVATE, DB_CALLBACK (db_activate), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_PLAYLISTSWITCH, DB_CALLBACK (db_playlist_switched), 0); + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_FRAMEUPDATE, DB_CALLBACK (db_frameupdate), 0); + + if (root_id != 0) { + g_dbus_connection_unregister_object (connection, root_id); + root_id = 0; + } + + if (player_id != 0) { + g_dbus_connection_unregister_object (connection, player_id); + player_id = 0; + } + + if (name_own_id != 0) { + g_bus_unown_name (name_own_id); + name_own_id = 0; + } + +// indicate_server_hide (indicate_server); + + deadbeef->mutex_lock(emit_id_mtx); + if (player_property_emit_id != 0) { + g_source_remove (player_property_emit_id); + player_property_emit_id = 0; + } + deadbeef->mutex_unlock(emit_id_mtx); + deadbeef->mutex_free (emit_id_mtx); + + deadbeef->mutex_lock(hash_table_mtx); + if (player_property_changes != NULL) { + g_hash_table_destroy (player_property_changes); + player_property_changes = NULL; + } + deadbeef->mutex_unlock(hash_table_mtx); + deadbeef->mutex_free (hash_table_mtx); + + //printf ("[mpris] end\n"); +} + + +static int +mpris_connect (void) { + //printf ("[mpris] mpris_connect\n"); + DB_plugin_t **plugins = deadbeef->plug_get_list (); + for (int i = 0; plugins[i]; i++) { + DB_plugin_t *p = plugins[i]; + if (p->id && !g_strcmp0 (p->id, "cover_loader")) { + trace ("gtkui: found cover-art loader plugin\n"); + coverart_plugin = (DB_artwork_plugin_t *)p; + break; + } + } + return FALSE; +} + +static int +mpris_on_configchanged (DB_event_t *ev, uintptr_t data) { + int e = deadbeef->conf_get_int ("mpris.enable", 0); + //printf ("[mpris] configchanged(): enable %d\n", e); + if (e != enabled) { + if (e) { + mpris_begin (); + mpris_connect (); + db_track_info_changed (NULL, 0); + } else { + mpris_end (); + } + enabled = e; + } + return 0; +} + +static int +mpris_plugin_start (void) { + + deadbeef->ev_subscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (mpris_on_configchanged), 0); + + int e = deadbeef->conf_get_int ("mpris.enable", 0); + if (e != enabled) { + enabled = e; + if (enabled == 1) { + mpris_begin (); + } + } + + return 0; +} + +static int +mpris_plugin_stop (void) { + + deadbeef->ev_unsubscribe (DB_PLUGIN (&plugin), DB_EV_CONFIGCHANGED, DB_CALLBACK (mpris_on_configchanged), 0); + + if (enabled == 1) { + mpris_end (); + } + + return 0; +} + +static const char settings_dlg[] = + "property \"Enable\" checkbox mpris.enable 0;\n" +; + +static DB_dsp_t plugin = { + .plugin.api_vmajor = DB_API_VERSION_MAJOR, + .plugin.api_vminor = DB_API_VERSION_MINOR, + .plugin.type = DB_PLUGIN_MISC, + .plugin.id = "mpris", + .plugin.name = "MPRIS", + .plugin.descr = "MPRIS", + .plugin.author = "Robert Y", + .plugin.email = "Decatf@gmail.com", + .plugin.website = "http://deadbeef.sf.net", + .plugin.start = mpris_plugin_start, + .plugin.stop = mpris_plugin_stop, + .plugin.configdialog = settings_dlg, + .plugin.connect = mpris_connect, +}; + +DB_plugin_t * +mpris_load (DB_functions_t *api) { + deadbeef = api; + return DB_PLUGIN (&plugin); +} diff --git a/plugins/shellexec/Makefile.am b/plugins/shellexec/Makefile.am index 65d6a5b8..212f6a0b 100644 --- a/plugins/shellexec/Makefile.am +++ b/plugins/shellexec/Makefile.am @@ -4,6 +4,6 @@ pkglib_LTLIBRARIES = shellexec.la shellexec_la_SOURCES = shellexec.c shellexec_la_LDFLAGS = -module -shellexec_la_LIBADD = $(LDADD) $(HOTKEYS_LIBS) +shellexec_la_LIBADD = $(LDADD) AM_CFLAGS = $(CFLAGS) -std=c99 endif diff --git a/plugins/sid/Makefile.am b/plugins/sid/Makefile.am index 38caa014..f5408088 100644 --- a/plugins/sid/Makefile.am +++ b/plugins/sid/Makefile.am @@ -103,9 +103,9 @@ sidplay-libs/libsidplay/src/kernal.bin\ sidplay-libs/libsidplay/src/psiddrv.bin\ sidplay-libs/libsidplay/src/poweron.bin -sid_la_LIBADD = -lstdc++ -sid_la_LDFLAGS = -module +sid_la_LDFLAGS = -module -nostdlib -lsupc++ AM_CFLAGS = $(CFLAGS) -std=c99 -I$(sidpath)/libsidplay/include -I$(sidpath)/builders/resid-builder/include -fPIC -AM_CPPFLAGS = $(CXXFLAGS) -DHAVE_UNIX -I$(sidpath) -I$(sidpath)/unix -I$(sidpath)/libsidplay -I$(sidpath)/libsidplay/include -I$(sidpath)/libsidplay/include/sidplay -I$(sidpath)/libsidutils/include/sidplay/utils -I$(sidpath)/builders/resid-builder/include/sidplay/builders -I$(sidpath)/builders/resid-builder/include -DHAVE_STRCASECMP -DHAVE_STRNCASECMP +AM_CPPFLAGS = $(CXXFLAGS) -DHAVE_UNIX -I$(sidpath) -I$(sidpath)/unix -I$(sidpath)/libsidplay -I$(sidpath)/libsidplay/include -I$(sidpath)/libsidplay/include/sidplay -I$(sidpath)/libsidutils/include/sidplay/utils -I$(sidpath)/builders/resid-builder/include/sidplay/builders -I$(sidpath)/builders/resid-builder/include -DHAVE_STRCASECMP -DHAVE_STRNCASECMP -fno-exceptions -fno-rtti -fno-unwind-tables + endif diff --git a/plugins/sid/csid.cpp b/plugins/sid/csid.cpp index ccb2ecb3..57035291 100644 --- a/plugins/sid/csid.cpp +++ b/plugins/sid/csid.cpp @@ -34,6 +34,18 @@ #include "../../deadbeef.h" #include "csid.h" +int _Unwind_Resume_or_Rethrow; +int _Unwind_RaiseException; +int _Unwind_GetLanguageSpecificData; +int _Unwind_Resume; +int _Unwind_DeleteException; +int _Unwind_GetTextRelBase; +int _Unwind_SetIP; +int _Unwind_GetDataRelBase; +int _Unwind_GetRegionStart; +int _Unwind_SetGR; +int _Unwind_GetIPInfo; + extern DB_decoder_t sid_plugin; //#define trace(...) { fprintf(stderr, __VA_ARGS__); } diff --git a/plugins/supereq/Equ.cpp b/plugins/supereq/Equ.cpp index f53b99d1..4411dbd9 100644 --- a/plugins/supereq/Equ.cpp +++ b/plugins/supereq/Equ.cpp @@ -4,6 +4,18 @@ #include <assert.h>
#include "paramlist.hpp"
+int _Unwind_Resume_or_Rethrow;
+int _Unwind_RaiseException;
+int _Unwind_GetLanguageSpecificData;
+int _Unwind_Resume;
+int _Unwind_DeleteException;
+int _Unwind_GetTextRelBase;
+int _Unwind_SetIP;
+int _Unwind_GetDataRelBase;
+int _Unwind_GetRegionStart;
+int _Unwind_SetGR;
+int _Unwind_GetIPInfo;
+
typedef float REAL;
void rfft(int n,int isign,REAL x[]);
diff --git a/plugins/supereq/Makefile.am b/plugins/supereq/Makefile.am index 0fffd6d6..eecade68 100644 --- a/plugins/supereq/Makefile.am +++ b/plugins/supereq/Makefile.am @@ -3,7 +3,9 @@ supereqdir = $(libdir)/$(PACKAGE) pkglib_LTLIBRARIES = supereq.la supereq_la_SOURCES = supereq.c supereq.h Equ.cpp Fftsg_fl.cpp paramlist.hpp -supereq_la_LDFLAGS = -module +AM_CPPFLAGS = $(CXXFLAGS) -fno-exceptions -fno-rtti -nostdlib -fno-unwind-tables + +supereq_la_LDFLAGS = -module -nostdlib -lsupc++ supereq_la_LIBADD = $(LDADD) AM_CFLAGS = -std=c99 diff --git a/plugins/vfs_curl/Makefile.am b/plugins/vfs_curl/Makefile.am index 95794831..4c69c51f 100644 --- a/plugins/vfs_curl/Makefile.am +++ b/plugins/vfs_curl/Makefile.am @@ -4,6 +4,6 @@ pkglib_LTLIBRARIES = vfs_curl.la vfs_curl_la_SOURCES = vfs_curl.c vfs_curl_la_LDFLAGS = -module -vfs_curl_la_LIBADD = $(LDADD) $(CURL_LIBS) +vfs_curl_la_LIBADD = $(LDADD) $(VFS_CURL_LIBS) AM_CFLAGS = $(CFLAGS) -std=c99 endif |