diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 7684050fa..5d78452be 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -3,7 +3,7 @@ name: Bug report about: Create a report to help us improve title: "[BUG]" labels: bug -assignees: larson-carter +assignees: --- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index c4e096cf7..e5ad16264 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -3,7 +3,7 @@ name: Feature request about: Suggest an idea for this project title: "[FEATURE]" labels: enhancement -assignees: larson-carter +assignees: --- diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 000000000..13c89733f --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,79 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + name: build ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + os: [ubuntu-22.04, ubuntu-24.04] + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install build dependencies + env: + PACKAGES: > + ccache + libboost-dev + libboost-filesystem-dev + libboost-locale-dev + libboost-program-options-dev + libboost-regex-dev + libboost-thread-dev + libcurl4-gnutls-dev + libfftw3-dev + libmpdclient-dev + libncurses-dev + libreadline-dev + libtag1-dev + zlib1g-dev + run: | + sudo apt update -qq + sudo apt install -y --no-install-recommends ${{ env.PACKAGES }} + + - name: Set up ccache + uses: actions/cache@v4 + with: + path: ${{ github.workspace }}/.ccache + key: ccache-${{ matrix.os }}-${{ github.run_id }} + restore-keys: ccache-${{ matrix.os }}- + + - name: Run autoreconf + run: | + autoreconf -fiv + + - name: Build ncmpcpp (light) + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache/light + run: | + CC='ccache gcc' CXX='ccache g++' ./configure \ + --disable-outputs \ + --disable-visualizer \ + --disable-clock \ + --without-fftw \ + --without-taglib \ + --with-lto=$(nproc) + make -j$(nproc) || exit 1 + + - name: Build ncmpcpp (full) + env: + CCACHE_DIR: ${{ github.workspace }}/.ccache/full + run: | + CC='ccache gcc' CXX='ccache g++' ./configure \ + --enable-outputs \ + --enable-visualizer \ + --enable-clock \ + --with-fftw \ + --with-taglib \ + --with-lto=$(nproc) + make -j$(nproc) || exit 1 diff --git a/CHANGELOG.md b/CHANGELOG.md index 531599e32..d48d84dbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,34 @@ -# ncmpcpp-0.10 (????-??-??) +# ncmpcpp-0.10.2 (2025-??-??) +* Update lyrics fetchers. +* Add support for hexadecimal HTML escape codes. + +# ncmpcpp-0.10.1 (2024-10-24) +* Fix compilation with `libc++`. +* Remove `autogen.sh` in favour of `autoreconf`. +* Do not crash when trying to reverse an empty playlist. + +# ncmpcpp-0.10 (2024-09-03) * Add the configuration option `mpd_password`. * Separate chunks of lyrics with a double newline. * Fix separator between albums with the same name, to check for album artist instead of artist. +* Column widths are now configurable via `media_library_column_width_ratio_two`, + `media_library_column_width_ratio_three` and + `playlist_editor_column_width_ratio`. +* Removed deprecated `visualizer_fifo_path` and `visualizer_sync_interval` + options. +* Added support for italic text. +* Framerate limit for visualizer was removed. +* Performer tag is now read from the `TPE4` frame instead of `TPE3`. +* Potential crash when fetching lyrics for streams in the background was fixed. +* Seeking now accepts the `hh:mm:ss` format. +* Tag editor now only writes to files with modified tags. +* Column view can now display full filepaths. +* Updated the list of working lyrics fetchers. +* Add `visualizer_spectrum_smooth_look_legacy_chars` option (enabled by default) + for potentially improved bottom part of the spectrum visualizer in terminals + with transparent background. +* Add support for fetching lyrics from tags. # ncmpcpp-0.9.2 (2021-01-24) * Revert suppression of output of all external commands as that makes e.g album diff --git a/INSTALL b/INSTALL index cf1d5c6c7..bb8c71ba6 100644 --- a/INSTALL +++ b/INSTALL @@ -30,7 +30,7 @@ The simplest way to compile this package is: For the next two commands, `csh' users will need to prefix them with `sh '. - 2. Run `./autogen.sh' to generate the `configure' script. + 2. Run `autoreconf -fiv' to generate the `configure' script. 3. Run `./configure' to configure the package for your system. This will take a while. While running, it prints some messages diff --git a/README.md b/README.md index 4a186de8b..bcd14e9f5 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,18 @@ # NCurses Music Player Client (Plus Plus) -Project page - https://rybczak.net/ncmpcpp/ - ## ncmpcpp – featureful ncurses based MPD client inspired by ncmpc -### Main features: +### Project status + +The project is officially in maintenance mode. I (Andrzej Rybczak) still use it +daily, but it's feature complete for me and there is very limited time I have +for tending to the issue tracker and open pull requests. + +No new, substantial features should be expected (at least from me). However, if +there are any serious bugs or the project outright stops compiling because of +new, incompatible versions of dependencies, it will be fixed. +### Main features: * tag editor * playlist editor * easy to use search engine @@ -18,19 +25,18 @@ Project page - https://rybczak.net/ncmpcpp/ …and a lot more minor functions. ### Dependencies: - -* boost library [https://www.boost.org/] -* ncurses library [http://www.gnu.org/software/ncurses/ncurses.html] -* readline library [https://tiswww.case.edu/php/chet/readline/rltop.html] -* curl library (optional, required for fetching lyrics and last.fm data) [https://curl.haxx.se/] -* fftw library (optional, required for frequency spectrum music visualization mode) [http://www.fftw.org/] -* tag library (optional, required for tag editing) [https://taglib.org/] +* [boost](https://www.boost.org/) +* [ncurses](https://invisible-island.net/ncurses/announce.html) +* [readline](https://tiswww.case.edu/php/chet/readline/rltop.html) +* [curl](https://curl.se), for fetching lyrics and last.fm data +#### Optional libraries +* [fftw](http://www.fftw.org), for frequency spectrum music visualization mode +* [taglib](https://taglib.org/), for tag editing ### Known issues: * No full support for handling encodings other than UTF-8. ### Installation: - The simplest way to compile this package is: 1. `cd` to the directory containing the package's source code. @@ -38,7 +44,7 @@ The simplest way to compile this package is: For the next two commands, `csh` users will need to prefix them with `sh `. - 2. Run `./autogen.sh` to generate the `configure` script. + 2. Run `autoreconf -fiv` to generate the `configure` script. 3. Run `./configure` to configure the package for your system. This will take a while. While running, it prints some messages @@ -55,7 +61,6 @@ The simplest way to compile this package is: Detailed intallation instructions can be found in the `INSTALL` file. ### Optional features: - Optional features can be enable by specifying them during configure. For example, to enable visualizer run `./configure --enable-visualizer`. diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 48c9878b8..000000000 --- a/autogen.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/sh -# Run this to set up the build system: configure, makefiles, etc. -# (at one point this was based on the version in enlightenment's cvs) - -package="ncmpcpp" - -olddir="`pwd`" -srcdir="`dirname $0`" -test -z "$srcdir" && srcdir=. -cd "$srcdir" -DIE= -AM_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9]\).*/\1/" -AC_VERSIONGREP="sed -e s/.*[^0-9\.]\([0-9]\.[0-9][0-9]\).*/\1/" -VERSIONMKINT="sed -e s/[^0-9]//" -if test -n "$AM_FORCE_VERSION" -then - AM_VERSIONS="$AM_FORCE_VERSION" -else - AM_VERSIONS='1.6 1.7 1.8 1.9' -fi -if test -n "$AC_FORCE_VERSION" -then - AC_VERSIONS="$AC_FORCE_VERSION" -else - AC_VERSIONS='2.58 2.59' -fi - -versioned_bins () -{ - bin="$1" - needed_int=`echo $VERNEEDED | $VERSIONMKINT` - for i in $VERSIONS - do - i_int=`echo $i | $VERSIONMKINT` - if test $i_int -ge $needed_int - then - echo $bin-$i $bin$i $bin-$i_int $bin$i_int - fi - done - echo $bin -} - -for c in autoconf autoheader automake aclocal -do - uc=`echo $c | tr a-z A-Z` - eval "val=`echo '$'$uc`" - if test -n "$val" - then - echo "$uc=$val in environment, will not attempt to auto-detect" - continue - fi - - case "$c" in - autoconf|autoheader) - VERNEEDED=`fgrep AC_PREREQ configure.ac | $AC_VERSIONGREP` - VERSIONS="$AC_VERSIONS" - pkg=autoconf - ;; - automake|aclocal) - VERNEEDED=`fgrep AUTOMAKE_OPTIONS Makefile.am | $AM_VERSIONGREP` - VERSIONS="$AM_VERSIONS" - pkg=automake - ;; - esac - printf "checking for $c ... " - for x in `versioned_bins $c`; do - ($x --version < /dev/null > /dev/null 2>&1) > /dev/null 2>&1 - if test $? -eq 0 - then - echo $x - eval $uc=$x - break - fi - done - eval "val=`echo '$'$uc`" - if test -z "$val" - then - if test $c = $pkg - then - DIE="$DIE $c=$VERNEEDED" - else - DIE="$DIE $c($pkg)=$VERNEEDED" - fi - fi -done - -if test -n "$LIBTOOLIZE" -then - echo "LIBTOOLIZE=$LIBTOOLIZE in environment," \ - "will not attempt to auto-detect" -else - printf "checking for libtoolize ... " - for x in libtoolize glibtoolize - do - ($x --version < /dev/null > /dev/null 2>&1) > /dev/null 2>&1 - if test $? -eq 0 - then - echo $x - LIBTOOLIZE=$x - break - fi - done -fi - -if test -z "$LIBTOOLIZE" -then - DIE="$DIE libtoolize(libtool)" -fi - -if test -n "$DIE" -then - echo "You must have the following installed to compile $package:" - for i in $DIE - do - printf ' ' - echo $i | sed -e 's/(/ (from /' -e 's/=\(.*\)/ (>= \1)/' - done - echo "Download the appropriate package(s) for your system," - echo "or get the source from one of the GNU ftp sites" - echo "listed in http://www.gnu.org/order/ftp.html" - exit 1 -fi - -echo "Generating configuration files for $package, please wait...." - -ACLOCAL_FLAGS="$ACLOCAL_FLAGS" - -# /usr/share/aclocal is most likely included by default, already... -ac_local_paths=' -/usr/local/share/aclocal -/sw/share/aclocal -/usr/pkg/share/aclocal -/opt/share/aclocal -/usr/gnu/share/aclocal -' - -for i in $ac_local_paths; do - if test -d "$i"; then - ACLOCAL_FLAGS="$ACLOCAL_FLAGS -I $i" - # we probably only want one of these... - break - fi -done - -echo " $ACLOCAL $ACLOCAL_FLAGS" -$ACLOCAL $ACLOCAL_FLAGS || exit 1 - -echo " $AUTOHEADER" -$AUTOHEADER || exit 1 - -echo " $LIBTOOLIZE --automake" -$LIBTOOLIZE --automake || exit 1 - -echo " $AUTOMAKE --add-missing $AUTOMAKE_FLAGS" -$AUTOMAKE --add-missing $AUTOMAKE_FLAGS || exit 1 - -echo " $AUTOCONF" -$AUTOCONF || exit 1 diff --git a/configure.ac b/configure.ac index ef0c52b79..a3303aaa2 100644 --- a/configure.ac +++ b/configure.ac @@ -1,15 +1,15 @@ -AC_INIT([ncmpcpp], [0.10_dev]) +AC_INIT([ncmpcpp],[0.10.2_dev]) AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_HEADERS(config.h) AM_INIT_AUTOMAKE([subdir-objects]) AC_CONFIG_MACRO_DIR([m4]) -AC_PREREQ(2.59) +AC_PREREQ([2.71]) -AC_LANG_CPLUSPLUS +AC_LANG([C++]) AC_PROG_CXX -AC_PROG_LIBTOOL +LT_INIT AC_ARG_ENABLE(outputs, AS_HELP_STRING([--enable-outputs], [Enable outputs screen @<:@default=no@:>@]), [outputs=$enableval], [outputs=no]) AC_ARG_ENABLE(visualizer, AS_HELP_STRING([--enable-visualizer], [Enable music visualizer screen @<:@default=no@:>@]), [visualizer=$enableval], [visualizer=no]) @@ -70,15 +70,15 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]])], ) CXXFLAGS="$old_CXXFLAGS $fast_math" -# -std=c++14 -AC_MSG_CHECKING([whether compiler supports -std=c++14]) +# -std=c++20 +AC_MSG_CHECKING([whether compiler supports -std=c++20]) old_CXXFLAGS="$CXXFLAGS" -CXXFLAGS="-std=c++14" +CXXFLAGS="-std=c++20" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]])], AC_MSG_RESULT([yes]) - std_cpp14="-std=c++14", + std_cpp14="-std=c++20", AC_MSG_RESULT([no]) - AC_MSG_ERROR([[Your compiler doesn't seem to support C++14, please upgrade (GCC >= 5)]]) + AC_MSG_ERROR([[Your compiler doesn't seem to support C++20, please upgrade]]) ) CXXFLAGS="$old_CXXFLAGS $std_cpp14" @@ -90,6 +90,9 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [[auto f = [](auto n) { return n*n; }; f( AC_MSG_ERROR([[Your compiler doesn't seem to support generic lambda expressions, please upgrade (GCC >= 5)]]) ) +# warnings +CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wshadow -Wimplicit-fallthrough" + # boost BOOST_REQUIRE([1.60]) AC_SUBST(BOOST_CPPFLAGS) @@ -139,7 +142,7 @@ LIBS="$LIBS $BOOST_REGEX_LIBS" BOOST_THREAD AC_SUBST(BOOST_THREAD_LDFLAGS) AC_SUBST(BOOST_THREAD_LIBS) -LDFLAGS+="$LDFLAGS $BOOST_THREAD_LDFLAGS" +LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS" LIBS="$LIBS $BOOST_THREAD_LIBS" # icu @@ -149,7 +152,7 @@ PKG_CHECK_MODULES([ICU], [icu-i18n icu-uc], [ old_LIBS="$LIBS" AC_SUBST(ICU_CFLAGS) AC_SUBST(ICU_LIBS) - CPPFLAGS="$CPPFLAGS $ICU_CFLAGS -DU_USING_ICU_NAMESPACE=0" + CPPFLAGS="$CPPFLAGS $ICU_CFLAGS" LIBS="$LIBS $ICU_LIBS" AC_MSG_CHECKING([whether boost.regex was compiled with ICU support]) AC_LINK_IFELSE([AC_LANG_PROGRAM([ @@ -257,19 +260,30 @@ PKG_CHECK_MODULES([libcurl], [libcurl], [ # taglib if test "$taglib" != "no" ; then - AC_PATH_PROG(TAGLIB_CONFIG, taglib-config) - if test "$TAGLIB_CONFIG" != "" ; then - CPPFLAGS="$CPPFLAGS `$TAGLIB_CONFIG --cflags`" - LIBS="$LIBS `$TAGLIB_CONFIG --libs`" + PKG_CHECK_MODULES([taglib], [taglib], [ + AC_SUBST(taglib_CFLAGS) + AC_SUBST(taglib_LIBS) + ], [ + AC_PATH_PROG([TAGLIB_CONFIG], [taglib-config]) + if test "$TAGLIB_CONFIG" != ""; then + taglib_CFLAGS=`$TAGLIB_CONFIG --cflags` + taglib_LIBS=`$TAGLIB_CONFIG --libs` + else + if test "$taglib" = "yes" ; then + AC_MSG_ERROR([could not find taglib.pc or taglib-config executable]) + fi + fi + ]) + + if test "$TAGLIB_CONFIG$taglib_LIBS" != "" ; then + CPPFLAGS="$CPPFLAGS $taglib_CFLAGS" + LIBS="$LIBS $taglib_LIBS" + AC_CHECK_HEADERS([taglib.h], , if test "$taglib" = "yes" ; then AC_MSG_ERROR([missing taglib.h header]) fi ) - else - if test "$taglib" = "yes" ; then - AC_MSG_ERROR([taglib-config executable is missing]) - fi fi fi diff --git a/doc/config b/doc/config index 57daa8ef2..791f2084a 100644 --- a/doc/config +++ b/doc/config @@ -125,6 +125,14 @@ # #visualizer_spectrum_smooth_look = yes # +## Use unicode block characters from "symbols for legacy computing". This +## improves the smooth look on transparent terminals by using special unicode +## chars instead of reversing the background and foreground color on the bottom +## edge of the spectrum. If it leads to a garbled output on the bottom edge of +## the spectrum, you can either change the font or disable this option. +# +#visualizer_spectrum_smooth_look_legacy_chars = yes +# ## A value between 1 and 5 inclusive. Specifying a larger value makes the ## visualizer look at a larger slice of time, which results in less jumpy ## visualizer output. @@ -141,6 +149,11 @@ # #visualizer_spectrum_hz_max = 20000 # +## Use log scaling for the frequency spectrum axes +# +#visualizer_spectrum_log_scale_x = yes +#visualizer_spectrum_log_scale_y = yes +# ##### system encoding ##### ## ## ncmpcpp should detect your charset encoding but if it failed to do so, you @@ -172,6 +185,7 @@ ## ## %l - length ## %f - filename +## %F - full filepath ## %D - directory ## %a - artist ## %A - album artist @@ -408,7 +422,7 @@ # #cyclic_scrolling = no # -#lyrics_fetchers = azlyrics, genius, musixmatch, sing365, metrolyrics, justsomelyrics, jahlyrics, plyrics, tekstowo, zeneszoveg, internet +#lyrics_fetchers = tags, genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet # #follow_now_playing_lyrics = no # diff --git a/doc/ncmpcpp.1 b/doc/ncmpcpp.1 index e1889f802..075f68aec 100644 --- a/doc/ncmpcpp.1 +++ b/doc/ncmpcpp.1 @@ -114,6 +114,13 @@ Automatically scale visualizer size. .B visualizer_spectrum_smooth_look = yes/no For spectrum visualizer, use unicode block characters for a smoother, more continuous look. This will override the visualizer_look option. With transparent terminals and visualizer_in_stereo set, artifacts may be visible on the bottom half of the visualization. .TP +.B visualizer_spectrum_smooth_look_legacy_chars = yes/no +Use unicode block characters from "symbols for legacy computing". This improves +the smooth look on transparent terminals by using special unicode chars instead +of reversing the background and foreground color on the bottom edge of the +spectrum. If it leads to a garbled output on the bottom edge of the spectrum, +you can either change the font or disable this option. +.TP .B visualizer_spectrum_dft_size = NUMBER For spectrum visualizer, a value between 1 and 5 inclusive. Specifying a larger value makes the visualizer look at a larger slice of time, which results in less jumpy visualizer output. .TP @@ -472,6 +479,7 @@ For song format you can use: %l - length %f - filename + %F - full filepath %D - directory %a - artist %A - album artist diff --git a/extras/artist_to_albumartist.cpp b/extras/artist_to_albumartist.cpp index c3ddfee3a..0349d0aed 100644 --- a/extras/artist_to_albumartist.cpp +++ b/extras/artist_to_albumartist.cpp @@ -34,7 +34,7 @@ enum class CopyResult { Success, NoArtist, AlbumArtistAlreadyInPlace }; bool is_framelist_empty(const TagLib::ID3v2::FrameList &list) { for (auto it = list.begin(); it != list.end(); ++it) - if ((*it)->toString() != TagLib::String::null) + if (!(*it)->toString().isEmpty()) return false; return true; } diff --git a/m4/boost.m4 b/m4/boost.m4 index a0894f335..342516e44 100644 --- a/m4/boost.m4 +++ b/m4/boost.m4 @@ -22,7 +22,7 @@ # along with this program. If not, see . m4_define([_BOOST_SERIAL], [m4_translit([ -# serial 26 +# serial 39 ], [# ], [])]) @@ -226,7 +226,7 @@ AC_LANG_POP([C++])dnl AC_CACHE_CHECK([for Boost's header version], [boost_cv_lib_version], [m4_pattern_allow([^BOOST_LIB_VERSION$])dnl - _BOOST_SED_CPP([[/^boost-lib-version = /{s///;s/[\" ]//g;p;q;}]], + _BOOST_SED_CPP([[/^.*boost-lib-version = /{s///;s/[\" ]//g;p;q;}]], [#include boost-lib-version = BOOST_LIB_VERSION], [boost_cv_lib_version=`cat conftest.i`])]) @@ -288,14 +288,17 @@ fi # BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], -# [CXX-PROLOGUE]) +# [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE], +# [ERROR_ON_UNUSABLE]) # -------------------------------------------------------------- # Look for the Boost library COMPONENT-NAME (e.g., `thread', for # libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g., # "thread_win32 thread"). Check that HEADER-NAME works and check that # libboost_LIB-NAME can link with the code CXX-TEST. The optional # argument CXX-PROLOGUE can be used to include some C++ code before -# the `main' function. +# the `main' function. The CXX-POST-INCLUDE-PROLOGUE can be used to +# include some code before the `main' function, but after the +# `#include '. # # Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above). # @@ -309,6 +312,9 @@ fi # builds. Some sample values for PREFERRED-RT-OPT: (nothing), mt, d, mt-d, gdp # ... If you want to make sure you have a specific version of Boost # (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro. +# +# ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their +# configure to fail AC_DEFUN([BOOST_FIND_LIBS], [AC_REQUIRE([BOOST_REQUIRE])dnl AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])dnl @@ -317,26 +323,32 @@ AC_REQUIRE([_BOOST_GUESS_WHETHER_TO_USE_MT])dnl if test x"$boost_cv_inc_path" = xno; then AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library]) else -dnl The else branch is huge and wasn't intended on purpose. +dnl The else branch is huge and wasn't indented on purpose. AC_LANG_PUSH([C++])dnl AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])dnl AS_VAR_PUSHDEF([Boost_lib_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl AS_VAR_PUSHDEF([Boost_lib_LDPATH], [boost_cv_lib_$1_LDPATH])dnl AS_VAR_PUSHDEF([Boost_lib_LIBS], [boost_cv_lib_$1_LIBS])dnl -BOOST_FIND_HEADER([$4]) +AS_IF([test x"$8" = "xno"], [not_found_header='true']) +BOOST_FIND_HEADER([$4], [$not_found_header]) boost_save_CPPFLAGS=$CPPFLAGS CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib], [_BOOST_FIND_LIBS($@)]) case $Boost_lib in #( + (yes) _AC_MSG_LOG_CONFTEST + AC_DEFINE(AS_TR_CPP([HAVE_BOOST_$1]), [1], [Defined if the Boost $1 library is available])dnl + AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl + AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl + AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl + AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl + ;; (no) _AC_MSG_LOG_CONFTEST - AC_MSG_ERROR([cannot find the flags to link with Boost $1]) + AS_IF([test x"$8" != "xno"], [ + AC_MSG_ERROR([cannot find flags to link with the Boost $1 library (libboost-$1)]) + ]) ;; esac -AC_SUBST(AS_TR_CPP([BOOST_$1_LDFLAGS]), [$Boost_lib_LDFLAGS])dnl -AC_SUBST(AS_TR_CPP([BOOST_$1_LDPATH]), [$Boost_lib_LDPATH])dnl -AC_SUBST([BOOST_LDPATH], [$Boost_lib_LDPATH])dnl -AC_SUBST(AS_TR_CPP([BOOST_$1_LIBS]), [$Boost_lib_LIBS])dnl CPPFLAGS=$boost_save_CPPFLAGS AS_VAR_POPDEF([Boost_lib])dnl AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl @@ -349,16 +361,20 @@ fi # BOOST_FIND_LIB([LIB-NAME], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], -# [CXX-PROLOGUE]) +# [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE], +# [ERROR_ON_UNUSABLE]) # -------------------------------------------------------------- # Backward compatibility wrapper for BOOST_FIND_LIBS. +# ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their +# configure to fail AC_DEFUN([BOOST_FIND_LIB], [BOOST_FIND_LIBS([$1], $@)]) # _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES], # [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], -# [CXX-PROLOGUE]) +# [CXX-PROLOGUE], [CXX-POST-INCLUDE-PROLOGUE], +# [ERROR_ON_UNUSABLE]) # -------------------------------------------------------------- # Real implementation of BOOST_FIND_LIBS: rely on these local macros: # Boost_lib, Boost_lib_LDFLAGS, Boost_lib_LDPATH, Boost_lib_LIBS @@ -370,6 +386,9 @@ AC_DEFUN([BOOST_FIND_LIB], # usually installed. If we can't find the standard variants, we try # to enforce -mt (for instance on MacOSX, libboost_thread.dylib # doesn't exist but there's -obviously- libboost_thread-mt.dylib). +# +# ERROR_ON_UNUSABLE can be set to "no" if the caller does not want their +# configure to fail AC_DEFUN([_BOOST_FIND_LIBS], [Boost_lib=no case "$3" in #( @@ -396,7 +415,8 @@ AC_DEFUN([_BOOST_FIND_LIBS], AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?]) boost_save_ac_objext=$ac_objext # Generate the test file. - AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$4> + AC_LANG_CONFTEST([AC_LANG_PROGRAM([$7 +#include <$4> $6], [$5])]) dnl Optimization hacks: compiling C++ is slow, especially with Boost. What dnl we're trying to do here is guess the right combination of link flags @@ -416,7 +436,10 @@ dnl empty because the test file is generated only once above (before we dnl start the for loops). AC_COMPILE_IFELSE([], [ac_objext=do_not_rm_me_plz], - [AC_MSG_ERROR([cannot compile a test that uses Boost $1])]) + [AS_IF([test x"$8" != x"no"], [ + AC_MSG_ERROR([cannot compile a test that uses Boost $1]) + ]) + ]) ac_objext=$boost_save_ac_objext boost_failed_libs= # Don't bother to ident the following nested for loops, only the 2 @@ -426,12 +449,15 @@ for boost_tag_ in -$boost_cv_lib_tag ''; do for boost_ver_ in -$boost_cv_lib_version ''; do for boost_mt_ in $boost_mt -mt ''; do for boost_rtopt_ in $boost_rtopt '' -d; do - for boost_lib in \ - boost_$boost_lib_$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ - boost_$boost_lib_$boost_tag_$boost_rtopt_$boost_ver_ \ - boost_$boost_lib_$boost_tag_$boost_mt_$boost_ver_ \ - boost_$boost_lib_$boost_tag_$boost_ver_ + for boost_full_suffix in \ + $boost_last_suffix \ + x$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ + x$boost_tag_$boost_rtopt_$boost_ver_ \ + x$boost_tag_$boost_mt_$boost_ver_ \ + x$boost_tag_$boost_ver_ do + boost_real_suffix=`echo "$boost_full_suffix" | sed 's/^x//'` + boost_lib="boost_$boost_lib_$boost_real_suffix" # Avoid testing twice the same lib case $boost_failed_libs in #( (*@$boost_lib@*) continue;; @@ -480,7 +506,7 @@ dnl generated only once above (before we start the for loops). *) for boost_cv_rpath_link_ldflag in -Wl,-R, -Wl,-rpath,; do LDFLAGS="$boost_save_LDFLAGS -L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" - LIBS="$boost_save_LIBS $Boost_lib_LIBS" + LIBS="$Boost_lib_LIBS $boost_save_LIBS" _BOOST_AC_LINK_IFELSE([], [boost_rpath_link_ldflag_found=yes break], @@ -496,6 +522,7 @@ dnl generated only once above (before we start the for loops). test x"$boost_ldpath" != x && Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" Boost_lib_LDPATH="$boost_ldpath" + boost_last_suffix="$boost_full_suffix" break 7 else boost_failed_libs="$boost_failed_libs@$boost_lib@" @@ -534,6 +561,14 @@ m4_popdef([BOOST_Library])dnl ]) ]) + +# BOOST_ANY() +# ------------ +# Look for Boost.Any +BOOST_DEFUN([Any], +[BOOST_FIND_HEADER([boost/any.hpp])]) + + # BOOST_ARRAY() # ------------- # Look for Boost.Array @@ -548,6 +583,12 @@ BOOST_DEFUN([Asio], [AC_REQUIRE([BOOST_SYSTEM])dnl BOOST_FIND_HEADER([boost/asio.hpp])]) +# BOOST_BIMAP() +# ------------ +# Look for Boost.Bimap +BOOST_DEFUN([Bimap], +[BOOST_FIND_HEADER([boost/bimap.hpp])]) + # BOOST_ASSIGN() # ------------- @@ -556,6 +597,24 @@ BOOST_DEFUN([Assign], [BOOST_FIND_HEADER([boost/assign.hpp])]) +# BOOST_ATOMIC([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) +# ------------------------------- +# Look for Boost.Atomic. For the documentation of PREFERRED-RT-OPT, see the +# documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Atomic], +[BOOST_FIND_LIB([atomic], [$1], + [boost/atomic.hpp], + [boost::atomic a;], + [ ], + [#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_STDINT_H +#include +#endif], [$2]) +])# BOOST_ATOMIC + + # BOOST_BIND() # ------------ # Look for Boost.Bind. @@ -563,7 +622,14 @@ BOOST_DEFUN([Bind], [BOOST_FIND_HEADER([boost/bind.hpp])]) -# BOOST_CHRONO() +# BOOST_CAST() +# ------------ +# Look for Boost.Cast +BOOST_DEFUN([Cast], +[BOOST_FIND_HEADER([boost/cast.hpp])]) + + +# BOOST_CHRONO([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------- # Look for Boost.Chrono. BOOST_DEFUN([Chrono], @@ -571,7 +637,7 @@ BOOST_DEFUN([Chrono], # added as of 1.35.0. If we have a version <1.35, we must not attempt to # find Boost.System as it didn't exist by then. if test $boost_major_version -ge 135; then - BOOST_SYSTEM([$1]) + BOOST_SYSTEM([$1], [$2]) fi # end of the Boost.System check. boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LDFLAGS=$LDFLAGS @@ -580,7 +646,7 @@ LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([chrono], [$1], [boost/chrono.hpp], - [boost::chrono::thread_clock d;]) + [boost::chrono::thread_clock d;], [], [], [$2]) if test $enable_static_boost = yes && test $boost_major_version -ge 135; then BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS" fi @@ -589,7 +655,7 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS ])# BOOST_CHRONO -# BOOST_CONTEXT([PREFERRED-RT-OPT]) +# BOOST_CONTEXT([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Context. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. @@ -597,17 +663,76 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS # * This library was introduced in Boost 1.51.0 # * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.0 # * A dependency on boost_thread appears in 1.57.0 +# * The implementation details were moved to boost::context::detail in 1.61.0 +# * 1.61 also introduces execution_context_v2, which is the "lowest common +# denominator" for boost::context presence since then. +# * boost::context::fiber was introduced in 1.69 and execution_context_v2 was +# removed in 1.72 BOOST_DEFUN([Context], [boost_context_save_LIBS=$LIBS boost_context_save_LDFLAGS=$LDFLAGS if test $boost_major_version -ge 157; then - BOOST_THREAD([$1]) + BOOST_THREAD([$1], [$2]) m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_THREAD_LIBS" LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS" fi + +if test $boost_major_version -ge 169; then + BOOST_FIND_LIB([context], [$1], - [boost/context/all.hpp],[[ + [boost/context/fiber.hpp], [[ +namespace ctx=boost::context; +int a; +ctx::fiber source{[&a](ctx::fiber&& sink){ + a=0; + int b=1; + for(;;){ + sink=std::move(sink).resume(); + int next=a+b; + a=b; + b=next; + } + return std::move(sink); +}}; +for (int j=0;j<10;++j) { + source=std::move(source).resume(); +} +return a == 34; +]], [], [], [$2]) + +elif test $boost_major_version -ge 161; then + +BOOST_FIND_LIB([context], [$1], + [boost/context/execution_context_v2.hpp], [[ +namespace ctx=boost::context; +int res=0; +int n=35; +ctx::execution_context source( + [n, &res](ctx::execution_context sink, int) mutable { + int a=0; + int b=1; + while(n-->0){ + auto result=sink(a); + sink=std::move(std::get<0>(result)); + auto next=a+b; + a=b; + b=next; + } + return sink; + }); +for(int i=0;i<10;++i){ + auto result=source(i); + source=std::move(std::get<0>(result)); + res = std::get<1>(result); +} +return res == 34; +]], [], [], [$2]) + +else + +BOOST_FIND_LIB([context], [$1], + [boost/context/fcontext.hpp],[[ // creates a stack void * stack_pointer = new void*[4096]; @@ -662,7 +787,10 @@ static void f(intptr_t i) { ctx::jump_fcontext(&fc, fcm, i * 2); } #endif -]) +], [], [], [$2]) + +fi + LIBS=$boost_context_save_LIBS LDFLAGS=$boost_context_save_LDFLAGS ])# BOOST_CONTEXT @@ -677,7 +805,7 @@ BOOST_FIND_HEADER([boost/lexical_cast.hpp]) ])# BOOST_CONVERSION -# BOOST_COROUTINE([PREFERRED-RT-OPT]) +# BOOST_COROUTINE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Coroutine. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. This library was introduced in Boost @@ -687,10 +815,10 @@ BOOST_DEFUN([Coroutine], boost_coroutine_save_LIBS=$LIBS boost_coroutine_save_LDFLAGS=$LDFLAGS # Link-time dependency from coroutine to context -BOOST_CONTEXT([$1]) +BOOST_CONTEXT([$1], [$2]) # Starting from Boost 1.55 a dependency on Boost.System is added if test $boost_major_version -ge 155; then - BOOST_SYSTEM([$1]) + BOOST_SYSTEM([$1], [$2]) fi m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)]) LIBS="$LIBS $BOOST_CONTEXT_LIBS $BOOST_SYSTEM_LIBS" @@ -698,7 +826,8 @@ LDFLAGS="$LDFLAGS $BOOST_CONTEXT_LDFLAGS" # in 1.53 coroutine was a header only library if test $boost_major_version -eq 153; then - BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp]) + AS_IF([test x"$2" = "xno"], [not_found_header='true']) + BOOST_FIND_HEADER([boost/coroutine/coroutine.hpp], [$not_found_header]) else BOOST_FIND_LIB([coroutine], [$1], [boost/coroutine/coroutine.hpp], @@ -709,7 +838,7 @@ else #else boost::coroutines::asymmetric_coroutine::pull_type coro; coro.get(); #endif - ]) + ], [], [], [$2]) fi # Link-time dependency from coroutine to context, existed only in 1.53, in 1.54 # coroutine doesn't use context from its headers but from its library. @@ -734,18 +863,25 @@ BOOST_DEFUN([CRC], ])# BOOST_CRC -# BOOST_DATE_TIME([PREFERRED-RT-OPT]) +# BOOST_DATE_TIME([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Date_Time], [BOOST_FIND_LIB([date_time], [$1], [boost/date_time/posix_time/posix_time.hpp], - [boost::posix_time::ptime t;]) + [boost::posix_time::ptime t;], [], [], [$2]) ])# BOOST_DATE_TIME -# BOOST_FILESYSTEM([PREFERRED-RT-OPT]) +# BOOST_EXCEPTION() +# ------------ +# Look for Boost.Exception +BOOST_DEFUN([Exception], +[BOOST_FIND_HEADER([boost/exception/all.hpp])]) + + +# BOOST_FILESYSTEM([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------------ # Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see # the documentation of BOOST_FIND_LIB above. @@ -756,7 +892,7 @@ BOOST_DEFUN([Filesystem], # added as of 1.35.0. If we have a version <1.35, we must not attempt to # find Boost.System as it didn't exist by then. if test $boost_major_version -ge 135; then - BOOST_SYSTEM([$1]) + BOOST_SYSTEM([$1], [$2]) fi # end of the Boost.System check. boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LDFLAGS=$LDFLAGS @@ -764,7 +900,8 @@ m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([filesystem], [$1], - [boost/filesystem/path.hpp], [boost::filesystem::path p;]) + [boost/filesystem/path.hpp], [boost::filesystem::path p;], + [], [], [$2]) if test $enable_static_boost = yes && test $boost_major_version -ge 135; then BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" fi @@ -809,6 +946,13 @@ BOOST_DEFUN([Function], [BOOST_FIND_HEADER([boost/function.hpp])]) +# BOOST_FUSION() +# ----------------- +# Look for Boost.Fusion +BOOST_DEFUN([Fusion], +[BOOST_FIND_HEADER([boost/fusion/sequence.hpp])]) + + # BOOST_GEOMETRY() # ---------------- # Look for Boost.Geometry (new since 1.47.0). @@ -817,7 +961,7 @@ BOOST_DEFUN([Geometry], ])# BOOST_GEOMETRY -# BOOST_GRAPH([PREFERRED-RT-OPT]) +# BOOST_GRAPH([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. @@ -826,34 +970,43 @@ BOOST_DEFUN([Graph], boost_graph_save_LDFLAGS=$LDFLAGS # Link-time dependency from graph to regex was added as of 1.40.0. if test $boost_major_version -ge 140; then - BOOST_REGEX([$1]) + BOOST_REGEX([$1], [$2]) m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_REGEX_LIBS" LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS" fi BOOST_FIND_LIB([graph], [$1], - [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;]) + [boost/graph/adjacency_list.hpp], [boost::adjacency_list<> g;], + [], [], [$2]) LIBS=$boost_graph_save_LIBS LDFLAGS=$boost_graph_save_LDFLAGS ])# BOOST_GRAPH -# BOOST_IOSTREAMS([PREFERRED-RT-OPT]) +# BOOST_HASH() +# ------------ +# Look for Boost.Functional/Hash +BOOST_DEFUN([Hash], +[BOOST_FIND_HEADER([boost/functional/hash.hpp])]) + + +# BOOST_IOSTREAMS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.IOStreams. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([IOStreams], [BOOST_FIND_LIB([iostreams], [$1], [boost/iostreams/device/file_descriptor.hpp], - [boost::iostreams::file_descriptor fd; fd.close();]) + [boost::iostreams::file_descriptor fd; fd.close();], + [], [], [$2]) ])# BOOST_IOSTREAMS -# BOOST_HASH() +# BOOST_ITERATOR() # ------------ -# Look for Boost.Functional/Hash -BOOST_DEFUN([Hash], -[BOOST_FIND_HEADER([boost/functional/hash.hpp])]) +# Look for Boost.Iterator +BOOST_DEFUN([Iterator], +[BOOST_FIND_HEADER([boost/iterator/iterator_adaptor.hpp])]) # BOOST_LAMBDA() @@ -863,7 +1016,7 @@ BOOST_DEFUN([Lambda], [BOOST_FIND_HEADER([boost/lambda/lambda.hpp])]) -# BOOST_LOCALE() +# BOOST_LOCALE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------- # Look for Boost.Locale BOOST_DEFUN([Locale], @@ -872,40 +1025,40 @@ boost_locale_save_LIBS=$LIBS boost_locale_save_LDFLAGS=$LDFLAGS # require SYSTEM for boost-1.50.0 and up if test $boost_major_version -ge 150; then - BOOST_SYSTEM([$1]) + BOOST_SYSTEM([$1], [$2]) m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" fi # end of the Boost.System check. BOOST_FIND_LIB([locale], [$1], [boost/locale.hpp], - [[boost::locale::generator gen; std::locale::global(gen(""));]]) + [[boost::locale::generator gen; std::locale::global(gen(""));]], [], [], [$2]) LIBS=$boost_locale_save_LIBS LDFLAGS=$boost_locale_save_LDFLAGS ])# BOOST_LOCALE -# BOOST_LOG([PREFERRED-RT-OPT]) +# BOOST_LOG([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------- # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Log], [boost_log_save_LIBS=$LIBS boost_log_save_LDFLAGS=$LDFLAGS -BOOST_SYSTEM([$1]) -BOOST_FILESYSTEM([$1]) -BOOST_DATE_TIME([$1]) +BOOST_SYSTEM([$1], [$2]) +BOOST_FILESYSTEM([$1], [$2]) +BOOST_DATE_TIME([$1], [$2]) m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS" BOOST_FIND_LIB([log], [$1], [boost/log/core/core.hpp], - [boost::log::attribute a; a.get_value();]) + [boost::log::attribute a; a.get_value();], [], [], [$2]) LIBS=$boost_log_save_LIBS LDFLAGS=$boost_log_save_LDFLAGS ])# BOOST_LOG -# BOOST_LOG_SETUP([PREFERRED-RT-OPT]) +# BOOST_LOG_SETUP([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------- # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. @@ -918,7 +1071,7 @@ LIBS="$LIBS $BOOST_LOG_LIBS" LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS" BOOST_FIND_LIB([log_setup], [$1], [boost/log/utility/setup/from_settings.hpp], - [boost::log::basic_settings bs; bs.empty();]) + [boost::log::basic_settings bs; bs.empty();], [], [], [$2]) LIBS=$boost_log_setup_save_LIBS LDFLAGS=$boost_log_setup_save_LDFLAGS ])# BOOST_LOG_SETUP @@ -936,7 +1089,7 @@ BOOST_DEFUN([Math], [BOOST_FIND_HEADER([boost/math/special_functions.hpp])]) -# BOOST_MPI([PREFERRED-RT-OPT]) +# BOOST_MPI([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost MPI. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. Uses MPICXX variable if it is @@ -953,12 +1106,20 @@ BOOST_FIND_LIB([mpi], [$1], [boost/mpi.hpp], [int argc = 0; char **argv = 0; - boost::mpi::environment env(argc,argv);]) + boost::mpi::environment env(argc,argv);], + [], [], [$2]) CXX=${boost_save_CXX} CXXCPP=${boost_save_CXXCPP} ])# BOOST_MPI +# BOOST_MPL() +# ------------------ +# Look for Boost.MPL +BOOST_DEFUN([MPL], +[BOOST_FIND_HEADER([boost/mpl/for_each.hpp])]) + + # BOOST_MULTIARRAY() # ------------------ # Look for Boost.MultiArray @@ -966,6 +1127,13 @@ BOOST_DEFUN([MultiArray], [BOOST_FIND_HEADER([boost/multi_array.hpp])]) +# BOOST_MULTIINDEXCCONTAINER() +# ------------------ +# Look for Boost.MultiIndexContainer +BOOST_DEFUN([MultiIndexContainer], +[BOOST_FIND_HEADER([boost/multi_index_container.hpp])]) + + # BOOST_NUMERIC_UBLAS() # -------------------------- # Look for Boost.NumericUblas (Basic Linear Algebra) @@ -996,6 +1164,25 @@ BOOST_DEFUN([Preprocessor], [BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])]) +# BOOST_PROPERTY_TREE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) +# ----------------------------------------- +# Look for Boost.Property_Tree. For the documentation of PREFERRED-RT-OPT, +# see the documentation of BOOST_FIND_LIB above. +BOOST_DEFUN([Property_Tree], +[BOOST_FIND_LIB([property_tree], [$1], + [boost/property_tree/ptree.hpp], + [boost::property_tree::ptree pt; boost::property_tree::read_xml d("test", pt);], + [], [], [$2]) +])# BOOST_PROPERTY_TREE + + +# BOOST_RANDOM() +# -------------------- +# Look for Boost.Random +BOOST_DEFUN([Random], +[BOOST_FIND_HEADER([boost/random/random_number_generator.hpp])]) + + # BOOST_RANGE() # -------------------- # Look for Boost.Range @@ -1016,14 +1203,15 @@ BOOST_DEFUN([Uuid], [BOOST_FIND_HEADER([boost/uuid/uuid.hpp])]) -# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT]) +# BOOST_PROGRAM_OPTIONS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ----------------------------------------- # Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Program_Options], [BOOST_FIND_LIB([program_options], [$1], [boost/program_options.hpp], - [boost::program_options::options_description d("test");]) + [boost::program_options::options_description d("test");], + [], [], [$2]) ])# BOOST_PROGRAM_OPTIONS @@ -1039,7 +1227,7 @@ boost_python_save_$1=$$1 $1="$$1 $BOOST_PYTHON_$1"]) -# BOOST_PYTHON([PREFERRED-RT-OPT]) +# BOOST_PYTHON([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------------------------- # Look for Boost.Python. For the documentation of PREFERRED-RT-OPT, # see the documentation of BOOST_FIND_LIB above. @@ -1050,7 +1238,7 @@ _BOOST_PYTHON_CONFIG([LIBS], [libs]) m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl BOOST_FIND_LIBS([python], [python python3], [$1], [boost/python.hpp], - [], [BOOST_PYTHON_MODULE(empty) {}]) + [], [BOOST_PYTHON_MODULE(empty) {}], [], [$2]) CPPFLAGS=$boost_python_save_CPPFLAGS LDFLAGS=$boost_python_save_LDFLAGS LIBS=$boost_python_save_LIBS @@ -1064,18 +1252,26 @@ BOOST_DEFUN([Ref], [BOOST_FIND_HEADER([boost/ref.hpp])]) -# BOOST_REGEX([PREFERRED-RT-OPT]) +# BOOST_REGEX([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------- # Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Regex], [BOOST_FIND_LIB([regex], [$1], [boost/regex.hpp], - [boost::regex exp("*"); boost::regex_match("foo", exp);]) + [boost::regex exp("*"); boost::regex_match("foo", exp);], + [], [], [$2]) ])# BOOST_REGEX -# BOOST_SERIALIZATION([PREFERRED-RT-OPT]) +# BOOST_SCOPE_EXIT() +# ------------ +# Look for Boost.ScopeExit. +BOOST_DEFUN([SCOPE_EXIT], +[BOOST_FIND_HEADER([boost/scope_exit.hpp])]) + + +# BOOST_SERIALIZATION([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # --------------------------------------- # Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see # the documentation of BOOST_FIND_LIB above. @@ -1083,18 +1279,20 @@ BOOST_DEFUN([Serialization], [BOOST_FIND_LIB([serialization], [$1], [boost/archive/text_oarchive.hpp], [std::ostream* o = 0; // Cheap way to get an ostream... - boost::archive::text_oarchive t(*o);]) + boost::archive::text_oarchive t(*o);], + [], [], [$2]) ])# BOOST_SERIALIZATION -# BOOST_SIGNALS([PREFERRED-RT-OPT]) +# BOOST_SIGNALS([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # --------------------------------- # Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. BOOST_DEFUN([Signals], [BOOST_FIND_LIB([signals], [$1], [boost/signal.hpp], - [boost::signal s;]) + [boost::signal s;], + [], [], [$2]) ])# BOOST_SIGNALS @@ -1130,19 +1328,24 @@ BOOST_DEFUN([String_Algo], ]) -# BOOST_SYSTEM([PREFERRED-RT-OPT]) +# BOOST_SYSTEM([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # -------------------------------- # Look for Boost.System. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. This library was introduced in Boost -# 1.35.0. +# 1.35.0 and is header only since 1.70. BOOST_DEFUN([System], -[BOOST_FIND_LIB([system], [$1], +[ +if test $boost_major_version -ge 170; then + BOOST_FIND_HEADER([boost/system/error_code.hpp]) +else + BOOST_FIND_LIB([system], [$1], [boost/system/error_code.hpp], - [boost::system::error_code e; e.clear();]) + [boost::system::error_code e; e.clear();], [], [], [$2]) +fi ])# BOOST_SYSTEM -# BOOST_TEST([PREFERRED-RT-OPT]) +# BOOST_TEST([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------ # Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. @@ -1152,11 +1355,11 @@ BOOST_FIND_LIB([unit_test_framework], [$1], [boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);], [using boost::unit_test::test_suite; test_suite* init_unit_test_suite(int argc, char ** argv) - { return NULL; }]) + { return NULL; }], [], [$2]) ])# BOOST_TEST -# BOOST_THREAD([PREFERRED-RT-OPT]) +# BOOST_THREAD([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # --------------------------------- # Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the # documentation of BOOST_FIND_LIB above. @@ -1170,7 +1373,7 @@ boost_thread_save_LDFLAGS=$LDFLAGS boost_thread_save_CPPFLAGS=$CPPFLAGS # Link-time dependency from thread to system was added as of 1.49.0. if test $boost_major_version -ge 149; then -BOOST_SYSTEM([$1]) +BOOST_SYSTEM([$1], [$2]) fi # end of the Boost.System check. m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag" @@ -1189,7 +1392,7 @@ if test $boost_major_version -lt 148; then fi BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext], [$1], - [boost/thread.hpp], [boost::thread t; boost::mutex m;]) + [boost/thread.hpp], [boost::thread t; boost::mutex m;], [], [], [$2]) case $host_os in (*mingw*) boost_thread_w32_socket_link=-lws2_32;; @@ -1265,7 +1468,7 @@ BOOST_FIND_HEADER([boost/ptr_container/ptr_map.hpp]) ])# BOOST_POINTER_CONTAINER -# BOOST_WAVE([PREFERRED-RT-OPT]) +# BOOST_WAVE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE]) # ------------------------------ # NOTE: If you intend to use Wave/Spirit with thread support, make sure you # call BOOST_THREAD first. @@ -1283,7 +1486,7 @@ LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS \ $BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS" BOOST_FIND_LIB([wave], [$1], [boost/wave.hpp], - [boost::wave::token_id id; get_token_name(id);]) + [boost::wave::token_id id; get_token_name(id);], [], [], [$2]) LIBS=$boost_wave_save_LIBS LDFLAGS=$boost_wave_save_LDFLAGS ])# BOOST_WAVE @@ -1351,10 +1554,11 @@ AC_CACHE_CHECK([for the flags needed to use pthreads], [boost_cv_pthread_flag], -pthreads -mthreads -lpthread --thread-safe -mt";; esac # Generate the test file. - AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include ], - [pthread_t th; pthread_join(th, 0); - pthread_attr_init(0); pthread_cleanup_push(0, 0); - pthread_create(0,0,0,0); pthread_cleanup_pop(0);])]) + AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include + void *f(void*){ return 0; }], + [pthread_t th; pthread_create(&th,0,f,0); pthread_join(th,0); + pthread_attr_t attr; pthread_attr_init(&attr); pthread_cleanup_push(0, 0); + pthread_cleanup_pop(0);])]) for boost_pthread_flag in '' $boost_pthread_flags; do boost_pthread_ok=false dnl Re-use the test file already generated. @@ -1416,12 +1620,77 @@ if test x$boost_cv_inc_path != xno; then # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines # the same defines as GCC's). for i in \ + "defined __clang__ && __clang_major__ == 14 && __clang_minor__ == 0 @ clang140" \ + "defined __clang__ && __clang_major__ == 13 && __clang_minor__ == 0 @ clang130" \ + "defined __clang__ && __clang_major__ == 12 && __clang_minor__ == 0 @ clang120" \ + "defined __clang__ && __clang_major__ == 11 && __clang_minor__ == 1 @ clang111" \ + "defined __clang__ && __clang_major__ == 11 && __clang_minor__ == 0 @ clang110" \ + "defined __clang__ && __clang_major__ == 10 && __clang_minor__ == 0 @ clang100" \ + "defined __clang__ && __clang_major__ == 9 && __clang_minor__ == 0 @ clang90" \ + "defined __clang__ && __clang_major__ == 8 && __clang_minor__ == 0 @ clang80" \ + "defined __clang__ && __clang_major__ == 7 && __clang_minor__ == 0 @ clang70" \ + "defined __clang__ && __clang_major__ == 6 && __clang_minor__ == 0 @ clang60" \ + "defined __clang__ && __clang_major__ == 5 && __clang_minor__ == 0 @ clang50" \ + "defined __clang__ && __clang_major__ == 4 && __clang_minor__ == 0 @ clang40" \ + "defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 9 @ clang39" \ + "defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 8 @ clang38" \ + "defined __clang__ && __clang_major__ == 3 && __clang_minor__ == 7 @ clang37" \ + _BOOST_mingw_test(11, 2) \ + _BOOST_gcc_test(11, 2) \ + _BOOST_mingw_test(11, 1) \ + _BOOST_gcc_test(11, 1) \ + _BOOST_mingw_test(10, 3) \ + _BOOST_gcc_test(10, 3) \ + _BOOST_mingw_test(10, 2) \ + _BOOST_gcc_test(10, 2) \ + _BOOST_mingw_test(10, 1) \ + _BOOST_gcc_test(10, 1) \ + _BOOST_mingw_test(9, 3) \ + _BOOST_gcc_test(9, 3) \ + _BOOST_mingw_test(9, 2) \ + _BOOST_gcc_test(9, 2) \ + _BOOST_mingw_test(9, 1) \ + _BOOST_gcc_test(9, 1) \ + _BOOST_mingw_test(9, 0) \ + _BOOST_gcc_test(9, 0) \ + _BOOST_mingw_test(8, 5) \ + _BOOST_gcc_test(8, 5) \ + _BOOST_mingw_test(8, 4) \ + _BOOST_gcc_test(8, 4) \ + _BOOST_mingw_test(8, 3) \ + _BOOST_gcc_test(8, 3) \ + _BOOST_mingw_test(8, 2) \ + _BOOST_gcc_test(8, 2) \ + _BOOST_mingw_test(8, 1) \ + _BOOST_gcc_test(8, 1) \ + _BOOST_mingw_test(8, 0) \ + _BOOST_gcc_test(8, 0) \ + _BOOST_mingw_test(7, 4) \ + _BOOST_gcc_test(7, 4) \ + _BOOST_mingw_test(7, 3) \ + _BOOST_gcc_test(7, 3) \ + _BOOST_mingw_test(7, 2) \ + _BOOST_gcc_test(7, 2) \ + _BOOST_mingw_test(7, 1) \ + _BOOST_gcc_test(7, 1) \ + _BOOST_mingw_test(7, 0) \ + _BOOST_gcc_test(7, 0) \ + _BOOST_mingw_test(6, 5) \ + _BOOST_gcc_test(6, 5) \ + _BOOST_mingw_test(6, 4) \ + _BOOST_gcc_test(6, 4) \ + _BOOST_mingw_test(6, 3) \ + _BOOST_gcc_test(6, 3) \ _BOOST_mingw_test(6, 2) \ _BOOST_gcc_test(6, 2) \ _BOOST_mingw_test(6, 1) \ _BOOST_gcc_test(6, 1) \ _BOOST_mingw_test(6, 0) \ _BOOST_gcc_test(6, 0) \ + _BOOST_mingw_test(5, 5) \ + _BOOST_gcc_test(5, 5) \ + _BOOST_mingw_test(5, 4) \ + _BOOST_gcc_test(5, 4) \ _BOOST_mingw_test(5, 3) \ _BOOST_gcc_test(5, 3) \ _BOOST_mingw_test(5, 2) \ diff --git a/src/actions.cpp b/src/actions.cpp index 9bcb15ecc..eaf75febd 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1746,33 +1746,46 @@ void JumpToPositionInSong::run() std::string spos; { Statusbar::ScopedLock slock; - Statusbar::put() << "Position to go (in %/m:ss/seconds(s)): "; + Statusbar::put() << "Position to go (in %/h:m:ss/m:ss/seconds(s)): "; spos = wFooter->prompt(); } boost::regex rx; boost::smatch what; - if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{2})"))) // mm:ss + // mm:ss + if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{2})"))) { auto mins = fromString(what[1]); auto secs = fromString(what[2]); boundsCheck(secs, 0u, 60u); Mpd.Seek(s.getPosition(), mins * 60 + secs); } - else if (boost::regex_match(spos, what, rx.assign("([0-9]+)s"))) // position in seconds + // position in seconds + else if (boost::regex_match(spos, what, rx.assign("([0-9]+)s"))) { auto secs = fromString(what[1]); Mpd.Seek(s.getPosition(), secs); } - else if (boost::regex_match(spos, what, rx.assign("([0-9]+)[%]{0,1}"))) // position in % + // position in% + else if (boost::regex_match(spos, what, rx.assign("([0-9]+)[%]{0,1}"))) { auto percent = fromString(what[1]); boundsCheck(percent, 0u, 100u); int secs = (percent * s.getDuration()) / 100.0; Mpd.Seek(s.getPosition(), secs); } + // position in hh:mm:ss + else if (boost::regex_match(spos, what, rx.assign("([0-9]+):([0-9]{2}):([0-9]{2})"))) + { + auto hours = fromString(what[1]); + auto mins = fromString(what[2]); + auto secs = fromString(what[3]); + boundsCheck(mins, 0u, 60u); + boundsCheck(secs, 0u, 60u); + Mpd.Seek(s.getPosition(), hours * 3600 + mins * 60 + secs); + } else - Statusbar::print("Invalid format ([m]:[ss], [s]s, [%]%, [%] accepted)"); + Statusbar::print("Invalid format ([h]:[mm]:[ss], [m]:[ss], [s]s, [%]%, [%] accepted)"); } bool SelectItem::canBeRun() @@ -1985,7 +1998,10 @@ bool ReversePlaylist::canBeRun() return false; m_begin = myPlaylist->main().begin(); m_end = myPlaylist->main().end(); - return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end); + if (m_begin == m_end) + return false; + else + return findSelectedRangeAndPrintInfoIfNot(m_begin, m_end); } void ReversePlaylist::run() diff --git a/src/configuration.cpp b/src/configuration.cpp index c9eb3fef3..455bce5e6 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -156,14 +156,10 @@ bool configure(int argc, char **argv) if (vm.count("test-lyrics-fetchers")) { std::vector> fetcher_data = { - std::make_tuple("azlyrics", "rihanna", "umbrella"), std::make_tuple("genius", "rihanna", "umbrella"), - std::make_tuple("musixmatch", "rihanna", "umbrella"), - std::make_tuple("sing365", "rihanna", "umbrella"), - std::make_tuple("metrolyrics", "rihanna", "umbrella"), std::make_tuple("justsomelyrics", "rihanna", "umbrella"), std::make_tuple("jahlyrics", "sean kingston", "dry your eyes"), - std::make_tuple("plyrics", "offspring", "genocide"), + std::make_tuple("plyrics", "rihanna", "umbrella"), std::make_tuple("tekstowo", "rihanna", "umbrella"), std::make_tuple("zeneszoveg", "rihanna", "umbrella"), }; @@ -175,7 +171,7 @@ bool configure(int argc, char **argv) << fetcher->name() << " : " << std::flush; - auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data)); + auto result = fetcher->fetch(std::get<1>(data), std::get<2>(data), {}); std::cout << (result.first ? "ok" : "failed") << "\n"; } diff --git a/src/curses/window.cpp b/src/curses/window.cpp index 59c45456d..6c0b6d6b3 100644 --- a/src/curses/window.cpp +++ b/src/curses/window.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include "utility/readline.h" @@ -198,6 +199,8 @@ int add_base() int color_pair_counter; std::vector color_pair_map; +termios orig_termios; + } namespace NC { @@ -400,6 +403,7 @@ int colorCount() void initScreen(bool enable_colors, bool enable_mouse) { + tcgetattr(STDIN_FILENO, &orig_termios); initscr(); if (has_colors() && enable_colors) { @@ -481,6 +485,7 @@ void destroyScreen() Mouse::disable(); curs_set(1); endwin(); + tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } Window::Window(size_t startx, size_t starty, size_t width, size_t height, @@ -501,12 +506,6 @@ Window::Window(size_t startx, size_t starty, size_t width, size_t height, m_alt_charset_counter(0), m_italic_counter(0) { - if (m_start_x > size_t(COLS) - || m_start_y > size_t(LINES) - || m_width+m_start_x > size_t(COLS) - || m_height+m_start_y > size_t(LINES)) - throw std::logic_error("constructed window doesn't fit into the terminal"); - if (m_border) { ++m_start_x; diff --git a/src/curses/window.h b/src/curses/window.h index 15728c41d..edb93cfe9 100644 --- a/src/curses/window.h +++ b/src/curses/window.h @@ -26,7 +26,6 @@ #include "config.h" #include "curses.h" -#include "gcc.h" #include #include diff --git a/src/display.cpp b/src/display.cpp index 4a52d8ced..ddf87932c 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -47,6 +47,8 @@ const wchar_t *toColumnName(char c) return L"Filename"; case 'D': return L"Directory"; + case 'F': + return L"Filepath"; case 'a': return L"Artist"; case 'A': diff --git a/src/gcc.h b/src/gcc.h deleted file mode 100644 index 0a2cbd92f..000000000 --- a/src/gcc.h +++ /dev/null @@ -1,29 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2008-2021 by Andrzej Rybczak * - * andrzej@rybczak.net * - * * - * 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 St, Fifth Floor, Boston, MA 02110-1301, USA. * - ***************************************************************************/ - -#if defined(__GNUC__) && __GNUC__ >= 3 -# define GNUC_NORETURN __attribute__((noreturn)) -# define GNUC_UNUSED __attribute__((unused)) -# define GNUC_PRINTF(a, b) __attribute__((format(printf, a, b))) -#else -# define GNUC_NORETURN -# define GNUC_UNUSED -# define GNUC_PRINTF(a, b) -#endif diff --git a/src/helpers/song_iterator_maker.h b/src/helpers/song_iterator_maker.h index 4c0f96ead..25b1b941f 100644 --- a/src/helpers/song_iterator_maker.h +++ b/src/helpers/song_iterator_maker.h @@ -46,8 +46,8 @@ SongIterator makeSongIterator(IteratorT it) > Extractor; static_assert( std::is_convertible< - typename std::result_of::type, - SongProperties & + std::invoke_result_t, + SongProperties & >::value, "invalid result type of SongPropertiesExtractor"); return SongIterator(boost::make_transform_iterator(it, Extractor{})); } @@ -60,8 +60,8 @@ ConstSongIterator makeConstSongIterator(ConstIteratorT it) > Extractor; static_assert( std::is_convertible< - typename std::result_of::type, - const SongProperties & + std::invoke_result_t, + const SongProperties & >::value, "invalid result type of SongPropertiesExtractor"); return ConstSongIterator(boost::make_transform_iterator(it, Extractor{})); } diff --git a/src/interfaces.h b/src/interfaces.h index abbc31e0b..869a2fc76 100644 --- a/src/interfaces.h +++ b/src/interfaces.h @@ -25,7 +25,6 @@ #include #include #include "enums.h" -#include "gcc.h" #include "screens/screen.h" #include "song.h" diff --git a/src/lyrics_fetcher.cpp b/src/lyrics_fetcher.cpp index 12a6fb2fd..c27e67024 100644 --- a/src/lyrics_fetcher.cpp +++ b/src/lyrics_fetcher.cpp @@ -19,7 +19,6 @@ ***************************************************************************/ #include "config.h" -#include "curl_handle.h" #include #include @@ -29,8 +28,15 @@ #include #include +#ifdef HAVE_TAGLIB_H +#include +#include +#endif // HAVE_TAGLIB_H + #include "charset.h" +#include "curl_handle.h" #include "lyrics_fetcher.h" +#include "settings.h" #include "utility/html.h" #include "utility/string.h" @@ -38,16 +44,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher) { std::string s; is >> s; - if (s == "azlyrics") - fetcher = std::make_unique(); - else if (s == "genius") + if (s == "genius") fetcher = std::make_unique(); - else if (s == "musixmatch") - fetcher = std::make_unique(); - else if (s == "sing365") - fetcher = std::make_unique(); - else if (s == "metrolyrics") - fetcher = std::make_unique(); else if (s == "justsomelyrics") fetcher = std::make_unique(); else if (s == "jahlyrics") @@ -60,6 +58,10 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher) fetcher = std::make_unique(); else if (s == "internet") fetcher = std::make_unique(); +#ifdef HAVE_TAGLIB_H + else if (s == "tags") + fetcher = std::make_unique(); +#endif // HAVE_TAGLIB_H else is.setstate(std::ios::failbit); return is; @@ -68,7 +70,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher) const char LyricsFetcher::msgNotFound[] = "Not found"; LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, - const std::string &title) + const std::string &title, + [[maybe_unused]] const MPD::Song &song) { Result result; result.first = false; @@ -76,7 +79,7 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, std::string url = urlTemplate(); boost::replace_all(url, "%artist%", Curl::escape(artist)); boost::replace_all(url, "%title%", Curl::escape(title)); - + std::string data; CURLcode code = Curl::perform(data, url, "", true); @@ -88,9 +91,11 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, auto lyrics = getContent(regex(), data); + //std::cerr << "URL: " << url << "\n"; + //std::cerr << "Data: " << data << "\n"; + if (lyrics.empty() || notLyrics(data)) { - //std::cerr << "Data: " << data << "\n"; //std::cerr << "Empty: " << lyrics.empty() << "\n"; //std::cerr << "Not Lyrics: " << notLyrics(data) << "\n"; result.second = msgNotFound; @@ -137,12 +142,14 @@ void LyricsFetcher::postProcess(std::string &data) const stripHtmlTags(data); // Remove indentation from each line and collapse multiple newlines into one. std::vector lines; - boost::split(lines, data, boost::is_any_of("\n")); + boost::split(lines, data, boost::is_any_of("\r\n")); for (auto &line : lines) boost::trim(line); - std::unique(lines.begin(), lines.end(), [](std::string &a, std::string &b) { - return a.empty() && b.empty(); - }); + auto last = std::unique( + lines.begin(), + lines.end(), + [](std::string &a, std::string &b) { return a.empty() && b.empty(); }); + lines.erase(last, lines.end()); data = boost::algorithm::join(lines, "\n"); boost::trim(data); } @@ -150,7 +157,8 @@ void LyricsFetcher::postProcess(std::string &data) const /**********************************************************************/ LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist, - const std::string &title) + const std::string &title, + const MPD::Song &song) { Result result; result.first = false; @@ -192,7 +200,7 @@ LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist, data = unescapeHtmlUtf8(urls[0]); URL = data.c_str(); - return LyricsFetcher::fetch("", ""); + return LyricsFetcher::fetch("", "", song); } bool GoogleLyricsFetcher::isURLOk(const std::string &url) @@ -202,18 +210,11 @@ bool GoogleLyricsFetcher::isURLOk(const std::string &url) /**********************************************************************/ -bool MetrolyricsFetcher::isURLOk(const std::string &url) -{ - // it sometimes return link to sitemap.xml, which is huge so we need to discard it - return GoogleLyricsFetcher::isURLOk(url) && url.find("sitemap") == std::string::npos; -} - -/**********************************************************************/ - LyricsFetcher::Result InternetLyricsFetcher::fetch(const std::string &artist, - const std::string &title) + const std::string &title, + const MPD::Song &song) { - GoogleLyricsFetcher::fetch(artist, title); + GoogleLyricsFetcher::fetch(artist, title, song); LyricsFetcher::Result result; result.first = false; result.second = "The following site may contain lyrics for this song: "; @@ -226,3 +227,42 @@ bool InternetLyricsFetcher::isURLOk(const std::string &url) URL = url; return false; } + +#ifdef HAVE_TAGLIB_H +LyricsFetcher::Result TagsLyricsFetcher::fetch([[maybe_unused]] const std::string &artist, + [[maybe_unused]] const std::string &title, + const MPD::Song &song) +{ + LyricsFetcher::Result result; + result.first = false; + + std::string path; + if (song.isFromDatabase()) + path += Config.mpd_music_dir; + path += song.getURI(); + + TagLib::FileRef f(path.c_str()); + if (f.isNull()) + { + result.second = "Could not open file"; + return result; + } + + TagLib::PropertyMap properties = f.file()->properties(); + + if (properties.contains("LYRICS")) + { + result.first = true; + result.second = properties["LYRICS"].toString("\n\n").to8Bit(true); + } + else if (properties.contains("UNSYNCEDLYRICS")) + { + result.first = true; + result.second = properties["UNSYNCEDLYRICS"].toString("\n\n").to8Bit(true); + } + else + result.second = "No lyrics in tags"; + + return result; +} +#endif // HAVE_TAGLIB_H diff --git a/src/lyrics_fetcher.h b/src/lyrics_fetcher.h index 672352ffe..e626fa726 100644 --- a/src/lyrics_fetcher.h +++ b/src/lyrics_fetcher.h @@ -26,6 +26,8 @@ #include #include +#include "song.h" + struct LyricsFetcher { typedef std::pair Result; @@ -33,7 +35,7 @@ struct LyricsFetcher virtual ~LyricsFetcher() { } virtual const char *name() const = 0; - virtual Result fetch(const std::string &artist, const std::string &title); + virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song); protected: virtual const char *urlTemplate() const = 0; @@ -57,7 +59,7 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher); struct GoogleLyricsFetcher : public LyricsFetcher { - virtual Result fetch(const std::string &artist, const std::string &title); + virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song); protected: virtual const char *urlTemplate() const { return URL; } @@ -69,34 +71,6 @@ struct GoogleLyricsFetcher : public LyricsFetcher const char *URL; }; -struct MusixmatchFetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "musixmatch.com"; } - -protected: - virtual const char *regex() const override { return "(.*?)"; } - - virtual void postProcess(std::string &) const override { } -}; - -struct MetrolyricsFetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "metrolyrics.com"; } - -protected: - virtual const char *regex() const override { return "
(.*?)(.*?)(.*?)
"; } - - virtual bool isURLOk(const std::string &url) override; -}; - -struct Sing365Fetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "lyrics007.com"; } - -protected: - virtual const char *regex() const override { return "
(.*?)
"; } -}; - struct JustSomeLyricsFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "justsomelyrics.com"; } @@ -105,20 +79,12 @@ struct JustSomeLyricsFetcher : public GoogleLyricsFetcher virtual const char *regex() const override { return "
(.*?)See also"; } }; -struct AzLyricsFetcher : public GoogleLyricsFetcher -{ - virtual const char *name() const override { return "azlyrics.com"; } - -protected: - virtual const char *regex() const override { return "
.*?.*
(.*?)
"; } -}; - struct GeniusFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "genius.com"; } protected: - virtual const char *regex() const override { return "(.*?)
"; } + virtual const char *regex() const override { return "
(.*?)
"; } }; struct JahLyricsFetcher : public GoogleLyricsFetcher @@ -142,7 +108,7 @@ struct TekstowoFetcher : public GoogleLyricsFetcher virtual const char *name() const override { return "tekstowo.pl"; } protected: - virtual const char *regex() const override { return "
.*?(.*?)(.*?)
"; } }; struct ZeneszovegFetcher : public GoogleLyricsFetcher @@ -150,13 +116,13 @@ struct ZeneszovegFetcher : public GoogleLyricsFetcher virtual const char *name() const override { return "zeneszoveg.hu"; } protected: - virtual const char *regex() const override { return "
(.*?)
"; } + virtual const char *regex() const override { return "
(.*?)
"; } }; struct InternetLyricsFetcher : public GoogleLyricsFetcher { virtual const char *name() const override { return "the Internet"; } - virtual Result fetch(const std::string &artist, const std::string &title) override; + virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song) override; protected: virtual const char *siteKeyword() const override { return nullptr; } @@ -168,4 +134,16 @@ struct InternetLyricsFetcher : public GoogleLyricsFetcher std::string URL; }; +#ifdef HAVE_TAGLIB_H +struct TagsLyricsFetcher : public LyricsFetcher +{ + virtual const char *name() const override { return "tags"; } + virtual Result fetch(const std::string &artist, const std::string &title, const MPD::Song &song) override; + +protected: + virtual const char *urlTemplate() const override { return ""; } + virtual const char *regex() const override { return ""; } +}; +#endif // HAVE_TAGLIB_H + #endif // NCMPCPP_LYRICS_FETCHER_H diff --git a/src/mpdpp.h b/src/mpdpp.h index ce23c8357..902606d7a 100644 --- a/src/mpdpp.h +++ b/src/mpdpp.h @@ -353,8 +353,14 @@ struct Output }; template -struct Iterator: std::iterator +struct Iterator { + using iterator_category = std::input_iterator_tag; + using value_type = ObjectT; + using difference_type = std::ptrdiff_t; + using pointer = ObjectT *; + using reference = ObjectT &; + // shared state of the iterator struct State { diff --git a/src/ncmpcpp.cpp b/src/ncmpcpp.cpp index 87bd54475..533b31b55 100644 --- a/src/ncmpcpp.cpp +++ b/src/ncmpcpp.cpp @@ -219,7 +219,10 @@ int main(int argc, char **argv) try { auto k = Bindings.get(input); - std::any_of(k.first, k.second, std::bind(&Binding::execute, ph::_1)); + [[maybe_unused]] bool executed = std::any_of( + k.first, + k.second, + std::bind(&Binding::execute, ph::_1)); } catch (ConversionError &e) { diff --git a/src/screens/lyrics.cpp b/src/screens/lyrics.cpp index d34ba617b..70ea26cb6 100644 --- a/src/screens/lyrics.cpp +++ b/src/screens/lyrics.cpp @@ -151,7 +151,7 @@ boost::optional downloadLyrics( << NC::Format::NoBold << "... "; } } - auto result_ = fetcher_->fetch(s_artist, s_title); + auto result_ = fetcher_->fetch(s_artist, s_title, s); if (result_.first == false) { if (shared_buffer) @@ -361,6 +361,25 @@ void Lyrics::toggleFetcher() Statusbar::print("Using all lyrics fetchers"); } +/* For HTTP(S) streams, fetchInBackground makes ncmpcpp crash: the lyrics_file + * gets set to the stream URL, which is too long, hence + * boost::filesystem::exists() fails. + * Possible solutions: + * - truncate the URL and use that as a filename. Problem: resulting filename + * might not be unique. + * - generate filenames in a different way for streams. Problem: what is a good + * method for this? Perhaps hashing -- but then the lyrics filenames are not + * informative. + * - skip fetching lyrics for streams with URLs that are too long. Problem: + * this leads to inconsistent behavior since lyrics will be fetched for some + * streams but not others. + * - avoid fetching lyrics for streams altogether. + * + * We choose the last solution, and ignore streams when fetching lyrics. This + * is because fetching lyrics for a stream may not be accurate (streams do not + * always provide the necessary metadata to look up lyrics reliably). + * Furthermore, fetching lyrics for streams is not necessarily desirable. + */ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_) { auto consumer_impl = [this] { @@ -377,7 +396,10 @@ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_) break; } lyrics_file = lyricsFilename(consumer->songs.front().song()); - if (!boost::filesystem::exists(lyrics_file)) + + // For long filenames (e.g. http(s) stream URLs), boost::filesystem::exists() fails. + // This if condition is fine, because evaluation order is guaranteed. + if (!consumer->songs.front().song().isStream() && !boost::filesystem::exists(lyrics_file)) { cs = consumer->songs.front(); if (cs.notify()) @@ -389,7 +411,7 @@ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_) } consumer->songs.pop(); } - if (!cs.song().empty()) + if (!cs.song().empty() && !cs.song().isStream()) { auto lyrics = downloadLyrics(cs.song(), nullptr, nullptr, m_fetcher); if (lyrics) diff --git a/src/screens/media_library.cpp b/src/screens/media_library.cpp index df2b58662..2cdfceef1 100644 --- a/src/screens/media_library.cpp +++ b/src/screens/media_library.cpp @@ -98,7 +98,7 @@ bool MoveToAlbum(NC::Menu &albums, const std::string &primary_tag, c struct SortSongs { typedef NC::Menu::Item SongItem; - static const std::array GetFuns; + static const std::array GetFuns; LocaleStringComparison m_cmp; @@ -117,26 +117,16 @@ struct SortSongs { return ret < 0; } - // Sort by track numbers. - try { - ret = boost::lexical_cast(a.getTags(&MPD::Song::getTrackNumber)) - - boost::lexical_cast(b.getTags(&MPD::Song::getTrackNumber)); - } catch (boost::bad_lexical_cast &) { - ret = a.getTrackNumber().compare(b.getTrackNumber()); - } - if (ret != 0) - return ret < 0; - - // If track numbers are equal, sort by the display format. return Format::stringify(Config.song_library_format, &a) < Format::stringify(Config.song_library_format, &b); } }; -const std::array SortSongs::GetFuns = {{ +const std::array SortSongs::GetFuns = {{ &MPD::Song::getDate, &MPD::Song::getAlbum, - &MPD::Song::getDisc + &MPD::Song::getDisc, + &MPD::Song::getTrackNumber, }}; class SortAlbumEntries { @@ -346,9 +336,9 @@ void MediaLibrary::update() for (const auto &album : albums) { auto entry = AlbumEntry( - Album(std::move(std::get<0>(album.first)), - std::move(std::get<1>(album.first)), - std::move(std::get<2>(album.first)), + Album(std::get<0>(album.first), + std::get<1>(album.first), + std::get<2>(album.first), album.second)); if (idx < Albums.size()) Albums[idx].value() = std::move(entry); @@ -445,8 +435,8 @@ void MediaLibrary::update() { auto entry = AlbumEntry( Album(primary_tag, - std::move(std::get<0>(album.first)), - std::move(std::get<1>(album.first)), + std::get<0>(album.first), + std::get<1>(album.first), album.second)); if (idx < Albums.size()) { diff --git a/src/screens/tag_editor.cpp b/src/screens/tag_editor.cpp index d754f4990..5ad68382b 100644 --- a/src/screens/tag_editor.cpp +++ b/src/screens/tag_editor.cpp @@ -798,13 +798,16 @@ void TagEditor::runAction() Statusbar::print("Writing changes..."); for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it) { - Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName()); - if (!Tags::write(**it)) + if ((*it)->isModified()) { - Statusbar::printf("Error while writing tags to \"%1%\": %2%", - (*it)->getName(), strerror(errno)); - success = 0; - break; + Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName()); + if (!Tags::write(**it)) + { + Statusbar::printf("Error while writing tags to \"%1%\": %2%", + (*it)->getName(), strerror(errno)); + success = 0; + break; + } } } if (success) diff --git a/src/screens/visualizer.cpp b/src/screens/visualizer.cpp index 26eac7bfb..66fc546d1 100644 --- a/src/screens/visualizer.cpp +++ b/src/screens/visualizer.cpp @@ -83,7 +83,8 @@ Visualizer::Visualizer() HZ_MIN(Config.visualizer_spectrum_hz_min), HZ_MAX(Config.visualizer_spectrum_hz_max), GAIN(Config.visualizer_spectrum_gain), - SMOOTH_CHARS(ToWString("▁▂▃▄▅▆▇█")) + SMOOTH_CHARS(ToWString("▁▂▃▄▅▆▇█")), + SMOOTH_CHARS_FLIPPED(ToWString("▔🮂🮃🮄🬎🮅🮆█")) // https://unicode.org/charts/PDF/U1FB00.pdf #endif { InitDataSource(); @@ -95,7 +96,7 @@ Visualizer::Visualizer() memset(m_fftw_input, 0, sizeof(double)*DFT_TOTAL_SIZE); m_fftw_output = static_cast(fftw_malloc(sizeof(fftw_complex)*m_fftw_results)); m_fftw_plan = fftw_plan_dft_r2c_1d(DFT_TOTAL_SIZE, m_fftw_input, m_fftw_output, FFTW_ESTIMATE); - m_dft_logspace.reserve(500); + m_dft_freqspace.reserve(500); m_bar_heights.reserve(100); # endif // HAVE_FFTW3_H } @@ -107,7 +108,7 @@ void Visualizer::switchTo() m_reset_output = true; drawHeader(); # ifdef HAVE_FFTW3_H - GenLogspace(); + GenFreqSpace(); m_bar_heights.reserve(w.getWidth()); # endif // HAVE_FFTW3_H } @@ -121,7 +122,7 @@ void Visualizer::resize() hasToBeResized = 0; InitVisualization(); # ifdef HAVE_FFTW3_H - GenLogspace(); + GenFreqSpace(); m_bar_heights.reserve(w.getWidth()); # endif // HAVE_FFTW3_H } @@ -420,7 +421,7 @@ void Visualizer::DrawSoundEllipseStereo(const int16_t *buf_left, const int16_t * auto c = toColor(sqrt(x*x + 4*y*y), radius, true); w << NC::XY(left_half_width + x, top_half_height + y) << c - << Config.visualizer_chars[1] + << Config.visualizer_chars[0] << NC::FormattedColor::End<>(c); } } @@ -449,7 +450,7 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size const size_t win_width = w.getWidth(); size_t cur_bin = 0; - while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_logspace[0]) + while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_freqspace[0]) ++cur_bin; for (size_t x = 0; x < win_width; ++x) { @@ -458,10 +459,10 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size // accumulate bins size_t count = 0; // check right bound - while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_logspace[x]) + while (cur_bin < m_fftw_results && Bin2Hz(cur_bin) < m_dft_freqspace[x]) { // check left bound if not first index - if (x == 0 || Bin2Hz(cur_bin) >= m_dft_logspace[x-1]) + if (x == 0 || Bin2Hz(cur_bin) >= m_dft_freqspace[x-1]) { bar_height += m_freq_magnitudes[cur_bin]; ++count; @@ -475,8 +476,19 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size // average bins bar_height /= count; - // log scale bar heights - bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE; + // apply scaling to bar heights + if (Config.visualizer_spectrum_log_scale_y) { + bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE; + } else { + // apply gain + bar_height *= pow(10, 1.8 + GAIN / 20); + // buff higher frequencies + bar_height *= log2(2 + x) * 80.0/win_width; + // moderately normalize the heights + bar_height = pow(bar_height, 0.65); + + //bar_height = pow(10, 1 + GAIN / 20) * bar_height; + } // Scale bar height between 0 and height bar_height = bar_height > 0 ? bar_height * height : 0; bar_height = bar_height > height ? height : bar_height; @@ -498,7 +510,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size ++h_idx; } else { // data point does not exist, need to interpolate - h = Interpolate(x, h_idx); + if (Config.visualizer_spectrum_log_scale_x) { + h = InterpolateCubic(x, h_idx); + } else { + h = std::min(InterpolateLinear(x, h_idx), height / 1.0); + //h = 0; + } } for (size_t j = 0; j < h; ++j) @@ -518,8 +535,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size } else { // fractional height if (flipped) { - ch = SMOOTH_CHARS[size-idx-2]; - color = NC::FormattedColor(color.color(), {NC::Format::Reverse}); + if (Config.visualizer_spectrum_smooth_look_legacy_chars) { + ch = SMOOTH_CHARS_FLIPPED[idx]; + } else { + ch = SMOOTH_CHARS[size-idx-2]; + color = NC::FormattedColor(color.color(), {NC::Format::Reverse}); + } } else { ch = SMOOTH_CHARS[idx]; } @@ -544,7 +565,7 @@ void Visualizer::DrawFrequencySpectrumStereo(const int16_t *buf_left, const int1 DrawFrequencySpectrum(buf_right, samples, height, w.getHeight() - height); } -double Visualizer::Interpolate(size_t x, size_t h_idx) +double Visualizer::InterpolateCubic(size_t x, size_t h_idx) { const double x_next = m_bar_heights[h_idx].first; const double h_next = m_bar_heights[h_idx].second; @@ -589,6 +610,33 @@ double Visualizer::Interpolate(size_t x, size_t h_idx) return h_next; } +double Visualizer::InterpolateLinear(size_t x, size_t h_idx) +{ + const double x_next = m_bar_heights[h_idx].first; + const double h_next = m_bar_heights[h_idx].second; + + double dh = 0; + if (h_idx == 0) { + // no data points on the left, linear extrapolation + if (h_idx < m_bar_heights.size()) { + const double x_next2 = m_bar_heights[h_idx+1].first; + const double h_next2 = m_bar_heights[h_idx+1].second; + dh = (h_next2 - h_next) / (x_next2 - x_next); + } + return h_next - dh * (x_next - x); + } else if (h_idx < m_bar_heights.size()) { + // simple linear interpolation + const double x_prev = m_bar_heights[h_idx-1].first; + const double h_prev = m_bar_heights[h_idx-1].second; + + const double m = (h_next - h_prev) / (x_next - x_prev); + return h_prev + m * (x - x_prev); + } + + // no data points on the right: don't interpolate + return h_next; +} + void Visualizer::ApplyWindow(double *output, const int16_t *input, ssize_t samples) { // Use Blackman window for low sidelobes and fast sidelobe rolloff @@ -617,10 +665,36 @@ void Visualizer::GenLogspace() const size_t win_width = w.getWidth(); const size_t left_bins = (log10(HZ_MIN) - win_width*log10(HZ_MIN)) / (log10(HZ_MIN) - log10(HZ_MAX)); // Generate logspaced frequencies - m_dft_logspace.resize(win_width); - const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_logspace.size() - 1); - for (size_t i = left_bins; i < m_dft_logspace.size() + left_bins; ++i) { - m_dft_logspace[i - left_bins] = pow(10, i * log_scale); + m_dft_freqspace.resize(win_width); + const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_freqspace.size() - 1); + for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) { + m_dft_freqspace[i - left_bins] = pow(10, i * log_scale); + } +} + +// Generate vector of linearly-spaced frequencies from HZ_MIN to HZ_MAX +void Visualizer::GenLinspace() +{ + // Calculate number of extra bins needed between 0 HZ and HZ_MIN + const size_t win_width = w.getWidth(); + const size_t left_bins = (HZ_MIN - win_width * HZ_MIN) / (HZ_MIN - HZ_MAX); + // Generate linspaced frequencies + m_dft_freqspace.resize(win_width); + const double lin_scale = HZ_MAX / (left_bins + m_dft_freqspace.size() - 1); + for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) { + m_dft_freqspace[i - left_bins] = i * lin_scale; + } +} + +// Generate vector of spectrum frequencies from HZ_MIN to HZ_MAX +// Frequencies are (not) log-scaled depending on +// Config.visualizer_spectrum_log_scale_x +void Visualizer::GenFreqSpace() +{ + if (Config.visualizer_spectrum_log_scale_x) { + GenLogspace(); + } else { + GenLinspace(); } } #endif // HAVE_FFTW3_H diff --git a/src/screens/visualizer.h b/src/screens/visualizer.h index 170a1ed7b..3a7a65ba7 100644 --- a/src/screens/visualizer.h +++ b/src/screens/visualizer.h @@ -76,8 +76,11 @@ struct Visualizer: Screen, Tabbable void DrawFrequencySpectrumStereo(const int16_t *, const int16_t *, ssize_t, size_t); void ApplyWindow(double *, const int16_t *, ssize_t); void GenLogspace(); + void GenLinspace(); + void GenFreqSpace(); double Bin2Hz(size_t); - double Interpolate(size_t, size_t); + double InterpolateCubic(size_t, size_t); + double InterpolateLinear(size_t, size_t); # endif // HAVE_FFTW3_H void InitDataSource(); @@ -113,7 +116,8 @@ struct Visualizer: Screen, Tabbable const double HZ_MAX; const double GAIN; const std::wstring SMOOTH_CHARS; - std::vector m_dft_logspace; + const std::wstring SMOOTH_CHARS_FLIPPED; + std::vector m_dft_freqspace; std::vector> m_bar_heights; std::vector m_freq_magnitudes; diff --git a/src/settings.cpp b/src/settings.cpp index 7b9ebf9f5..ddde73e07 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -253,6 +253,7 @@ bool Configuration::read(const std::vector &config_paths, bool igno }); p.add("visualizer_autoscale", &visualizer_autoscale, "no", yes_no); p.add("visualizer_spectrum_smooth_look", &visualizer_spectrum_smooth_look, "yes", yes_no); + p.add("visualizer_spectrum_smooth_look_legacy_chars", &visualizer_spectrum_smooth_look_legacy_chars, "yes", yes_no); p.add("visualizer_spectrum_dft_size", &visualizer_spectrum_dft_size, "2", [](std::string v) { auto result = verbose_lexical_cast(v); @@ -277,6 +278,8 @@ bool Configuration::read(const std::vector &config_paths, bool igno lowerBoundCheck(result, Config.visualizer_spectrum_hz_min+1); return result; }); + p.add("visualizer_spectrum_log_scale_x", &visualizer_spectrum_log_scale_x, "yes", yes_no); + p.add("visualizer_spectrum_log_scale_y", &visualizer_spectrum_log_scale_y, "yes", yes_no); p.add("visualizer_color", &visualizer_colors, "blue, cyan, green, yellow, magenta, red", list_of); p.add("system_encoding", &system_encoding, "", [](std::string encoding) { @@ -458,9 +461,27 @@ bool Configuration::read(const std::vector &config_paths, bool igno p.add("titles_visibility", &titles_visibility, "yes", yes_no); p.add("header_text_scrolling", &header_text_scrolling, "yes", yes_no); p.add("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no); - p.add("lyrics_fetchers", &lyrics_fetchers, - "azlyrics, genius, musixmatch, sing365, metrolyrics, justsomelyrics, jahlyrics, plyrics, tekstowo, zeneszoveg, internet", - list_of); + p.add("lyrics_fetchers", nullptr, +#ifdef HAVE_TAGLIB_H + "tags, " +#endif + "genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) { + lyrics_fetchers = list_of(v, [](std::string s) { + LyricsFetcher_ fetcher; + try { + fetcher = boost::lexical_cast(s); + } catch (boost::bad_lexical_cast &) { + std::clog << "Unknown lyrics fetcher: " << s << "\n"; + } + return fetcher; + }); + auto last = std::remove_if( + lyrics_fetchers.begin(), lyrics_fetchers.end(), + [](const auto &f) { return f.get() == nullptr; }); + lyrics_fetchers.erase(last, lyrics_fetchers.end()); + if (lyrics_fetchers.empty()) + invalid_value(v); + }); p.add("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no); p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background, "no", yes_no); diff --git a/src/settings.h b/src/settings.h index bc22b5887..e9a8af087 100644 --- a/src/settings.h +++ b/src/settings.h @@ -86,10 +86,13 @@ struct Configuration size_t visualizer_fps; bool visualizer_autoscale; bool visualizer_spectrum_smooth_look; + bool visualizer_spectrum_smooth_look_legacy_chars; uint32_t visualizer_spectrum_dft_size; double visualizer_spectrum_gain; double visualizer_spectrum_hz_min; double visualizer_spectrum_hz_max; + bool visualizer_spectrum_log_scale_x; + bool visualizer_spectrum_log_scale_y; std::string pattern; diff --git a/src/song.cpp b/src/song.cpp index 70822d109..c274d43f1 100644 --- a/src/song.cpp +++ b/src/song.cpp @@ -293,7 +293,9 @@ bool Song::isFromDatabase() const bool Song::isStream() const { assert(m_song); - return !strncmp(mpd_song_get_uri(m_song.get()), "http://", 7); + const char *song_uri = mpd_song_get_uri(m_song.get()); + // Stream schemas: http, https + return !strncmp(song_uri, "http://", 7) || !strncmp(song_uri, "https://", 8); } bool Song::empty() const @@ -308,7 +310,7 @@ std::string Song::ShowTime(unsigned length) int minutes = length/60; length -= minutes*60; int seconds = length; - + std::string result; if (hours > 0) result = (boost::format("%d:%02d:%02d") % hours % minutes % seconds).str(); diff --git a/src/statusbar.h b/src/statusbar.h index 0cadaf566..b14c21376 100644 --- a/src/statusbar.h +++ b/src/statusbar.h @@ -24,7 +24,6 @@ #include #include "curses/window.h" #include "settings.h" -#include "gcc.h" #include "interfaces.h" namespace Progressbar { diff --git a/src/tags.cpp b/src/tags.cpp index 6cd6a8c27..d74f56c75 100644 --- a/src/tags.cpp +++ b/src/tags.cpp @@ -123,12 +123,12 @@ void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag) tag->setArtist(ToWString(s.getArtist())); tag->setAlbum(ToWString(s.getAlbum())); try { - tag->setYear(boost::lexical_cast(s.getDate())); + tag->setYear(boost::lexical_cast(s.getDate())); } catch (boost::bad_lexical_cast &) { std::cerr << "writeCommonTags: couldn't write 'year' tag to '" << s.getURI() << "' as it's not a positive integer\n"; } try { - tag->setTrack(boost::lexical_cast(s.getTrack())); + tag->setTrack(boost::lexical_cast(s.getTrack())); } catch (boost::bad_lexical_cast &) { std::cerr << "writeCommonTags: couldn't write 'track' tag to '" << s.getURI() << "' as it's not a positive integer\n"; } @@ -174,6 +174,7 @@ void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag) void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag) { auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) { + tag->removeFields(type); for (auto it = list.begin(); it != list.end(); ++it) tag->addField(type, *it, it == list.begin()); }; @@ -261,7 +262,7 @@ void read(mpd_song *s) if (f.isNull()) return; - setAttribute(s, "Time", boost::lexical_cast(f.audioProperties()->length())); + setAttribute(s, "Time", boost::lexical_cast(f.audioProperties()->lengthInSeconds())); if (auto mpeg_file = dynamic_cast(f.file())) { @@ -301,15 +302,9 @@ bool write(MPD::MutableSong &s) if (f.isNull()) return false; - bool saved = false; if (auto mpeg_file = dynamic_cast(f.file())) { writeID3v2Tags(s, mpeg_file->ID3v2Tag(true)); - // write id3v2.4 tags only - if (!mpeg_file->save(TagLib::MPEG::File::ID3v2, true, 4, false)) - return false; - // do not call generic save() as it will duplicate tags - saved = true; } else if (auto vorbis_file = dynamic_cast(f.file())) { @@ -326,7 +321,7 @@ bool write(MPD::MutableSong &s) else writeCommonTags(s, f.tag()); - if (!saved && !f.save()) + if (!f.save()) return false; // TODO: move this somewhere else diff --git a/src/utility/comparators.cpp b/src/utility/comparators.cpp index a9c0d3394..3959962b2 100644 --- a/src/utility/comparators.cpp +++ b/src/utility/comparators.cpp @@ -34,10 +34,23 @@ bool hasTheWord(const std::string &s) && (s[3] == ' '); } +long long strToLL(const char *s, size_t len) +{ + long long n = 0; + while (len--) + n = 10*n + *s++ - '0'; + return n; +} + } int LocaleStringComparison::compare(const char *a, size_t a_len, const char *b, size_t b_len) const { + // If both strings are numbers, compare them as such. + if ( a_len > 0 && std::all_of(a, a + a_len, isdigit) + && b_len > 0 && std::all_of(b, b + b_len, isdigit)) + return strToLL(a, a_len) - strToLL(b, b_len); + size_t ac_off = 0, bc_off = 0; if (m_ignore_the) { diff --git a/src/utility/conversion.h b/src/utility/conversion.h index 80d0a8391..109d161ee 100644 --- a/src/utility/conversion.h +++ b/src/utility/conversion.h @@ -26,7 +26,6 @@ #include #include "config.h" -#include "gcc.h" struct ConversionError { @@ -44,21 +43,21 @@ struct OutOfBounds : std::exception const std::string &errorMessage() { return m_error_message; } template - GNUC_NORETURN static void raise(const Type &value, const Type &lbound, const Type &ubound) + [[noreturn]] static void raise(const Type &value, const Type &lbound, const Type &ubound) { throw OutOfBounds((boost::format( "value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str()); } template - GNUC_NORETURN static void raiseLower(const Type &value, const Type &lbound) + [[noreturn]] static void raiseLower(const Type &value, const Type &lbound) { throw OutOfBounds((boost::format( "value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str()); } template - GNUC_NORETURN static void raiseUpper(const Type &value, const Type &ubound) + [[noreturn]] static void raiseUpper(const Type &value, const Type &ubound) { throw OutOfBounds((boost::format( "value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str()); diff --git a/src/utility/html.cpp b/src/utility/html.cpp index 9ef71becd..725538450 100644 --- a/src/utility/html.cpp +++ b/src/utility/html.cpp @@ -20,16 +20,29 @@ #include #include +#include #include "utility/html.h" std::string unescapeHtmlUtf8(const std::string &data) { + int base; + size_t offset; std::string result; for (size_t i = 0, j; i < data.length(); ++i) { if (data[i] == '&' && data[i+1] == '#' && (j = data.find(';', i)) != std::string::npos) { - int n = atoi(&data.c_str()[i+2]); + if (data[i+2] == 'x') + { + offset = 3; + base = 16; + } + else + { + offset = 2; + base = 10; + } + int n = strtol(&data.c_str()[i+offset], nullptr, base); if (n >= 0x800) { result += (0xe0 | ((n >> 12) & 0x0f)); diff --git a/src/utility/string.h b/src/utility/string.h index e86ddeb8a..75d657678 100644 --- a/src/utility/string.h +++ b/src/utility/string.h @@ -25,7 +25,6 @@ #include #include #include -#include "gcc.h" template size_t const_strlen(const char (&)[N]) { return N-1; diff --git a/src/utility/type_conversions.cpp b/src/utility/type_conversions.cpp index d4bfb8cdc..b546fa4a1 100644 --- a/src/utility/type_conversions.cpp +++ b/src/utility/type_conversions.cpp @@ -168,6 +168,8 @@ MPD::Song::GetFunction charToGetFunction(char c) return &MPD::Song::getDirectory; case 'f': return &MPD::Song::getName; + case 'F': + return &MPD::Song::getURI; case 'a': return &MPD::Song::getArtist; case 'A':