64 Commits

Author SHA1 Message Date
Salastil
5e31b41f1f Update README with yt-dlp fork details
All checks were successful
CI / build ubuntu-22.04 (push) Successful in 16m17s
CI / build ubuntu-24.04 (push) Successful in 16m2s
Added information about yt-dlp fork and project status.
2025-11-30 22:18:53 -05:00
Salastil
b36a41d4e9 Align yt-dlp to the boost removal changes
Some checks failed
CI / build ubuntu-24.04 (push) Has been cancelled
CI / build ubuntu-22.04 (push) Has been cancelled
2025-11-30 22:13:06 -05:00
Salastil
900939f4ad Merge branch 'ncmpcpp:master' into master 2025-09-11 13:46:06 -04:00
Michael Cho
2b4bd89e2e Update boost.m4 to fix build with Boost >= 1.89 (#636) 2025-09-11 16:36:41 +02:00
Salastil
d885583d6e Merge branch 'ncmpcpp:master' into master 2025-04-21 18:56:55 -04:00
Andrzej Rybczak
e8f1c4bd48 Add support for hexadecimal HTML escape codes 2025-02-26 02:35:09 +01:00
Andrzej Rybczak
2f9a65e177 Update lyrics fetchers 2025-02-24 01:13:48 +01:00
Salastil
27b107ee37 Merge branch 'ncmpcpp:master' into master 2025-02-22 21:19:27 -05:00
Andrzej Rybczak
3e6d992b93 Release 0.10.1 (#617) 2024-10-24 14:28:08 +02:00
Andrzej Rybczak
4fdafc5320 Do not crash when trying to reverse an empty playlist
Fixes https://github.com/ncmpcpp/ncmpcpp/issues/616.
2024-10-24 01:18:16 +02:00
Andrzej Rybczak
440e9a5768 Remove autogen.sh in favour of autoreconf 2024-10-05 09:23:49 +02:00
Andrzej Rybczak
4ac9a0f0dd Add CI (#610)
Adaptation of 2148c716c7/.github/workflows/build.yml
2024-10-05 09:07:38 +02:00
Andrzej Rybczak
92b478ef31 Fix compilation with libc++ 2024-10-05 08:42:08 +02:00
fkobi
72d990fe25 fix stray + in LDFLAGS assignment (#608)
Signed-off-by: Filip Kobierski <fkobi@pm.me>
2024-09-12 21:02:27 +02:00
Andrzej Rybczak
f9d0fb608c Put project status in the README
Fixes https://github.com/ncmpcpp/ncmpcpp/issues/607.
2024-09-09 12:29:26 +02:00
Andrzej Rybczak
1d7d5152c6 Release 0.10 2024-09-03 03:34:57 +02:00
Andrzej Rybczak
9c27eec846 Add support for fetching lyrics from tags
Adaptation of https://github.com/ncmpcpp/ncmpcpp/pull/482.
2024-09-03 03:15:09 +02:00
Salastil
4b9eb741ea Merge branch 'ncmpcpp:master' into master 2024-08-25 21:35:43 -04:00
Andrzej Rybczak
510e28c65a Remove link to project page, it's deprecated 2024-08-21 19:18:21 +02:00
Andrzej Rybczak
37aea91be1 Don't throw when window doesn't fit, ncurses can deal with it 2024-08-21 19:13:21 +02:00
Andrzej Rybczak
377994daca Remove Larson from assignes in issue templates 2024-08-21 18:36:13 +02:00
Andrzej Rybczak
33145940c8 curl is not optional 2024-08-21 18:29:36 +02:00
Andrzej Rybczak
cd9ae93c3e Fix boost::regex ICU support 2024-08-21 18:19:02 +02:00
Andrzej Rybczak
ba484cff1e Use C++20 2024-08-21 18:09:24 +02:00
Andrzej Rybczak
603735d80e Clear warnings 2024-08-21 17:42:30 +02:00
o0nd7ots
5238d0f6ed Update README.md (#602)
updated links
2024-08-21 17:13:22 +02:00
Will Huie
7078ed7f55 Tweaks to frequency spectrum and sound ellipse in visualizer (#464)
* use visualizer_spectrum_log_scale_{x,y} to control log scaling

* change sound ellipse character to the circle instead of the bar

* oops, should be determined by x-axis scaling

* rebase to upstream

* use values from Config directly

* fix that weird first-column screen-wrapping issue when drawing

* rebase to upstream

---------

Co-authored-by: whooie <wchooie@gmail.com>
Co-authored-by: Andrzej Rybczak <arybczak@users.noreply.github.com>
2024-08-21 17:12:45 +02:00
Andrzej Rybczak
f51968b98b Rename the new option, slightly reword the description, add changelog 2024-08-21 16:45:37 +02:00
TilCreator
f83428a665 Improve specrum visulaizer for transparent backgrounds (#467)
* Replace "hack" with hacky chars

* Remove unnecessary spaces

* Add config option visualizer_spectrum_use_legacy_chars
2024-08-21 16:34:12 +02:00
Andrzej Rybczak
1c77138f51 Update changelog 2024-08-21 16:26:07 +02:00
Andrzej Rybczak
838f600b57 Fix compilation with taglib 1.11 2024-08-21 16:14:32 +02:00
Andrzej Rybczak
68daf44032 Don't treat unknown lyrics fetchers as errors 2024-07-24 21:39:11 +02:00
Andrzej Rybczak
985bc09b5c Update lyrics fetchers
Removes azlyrics, musixmatch, sing365/lyrics007 and metrolyrics.
2024-07-24 20:36:11 +02:00
Andrzej Rybczak
b2c8ca91c4 Don't treat empty strings as numbers 2024-07-24 19:38:30 +02:00
Andrzej Rybczak
6f8d12da8c If both strings are numbers, compare them as such 2024-07-24 19:18:00 +02:00
Andrzej Rybczak
af7550a69f Adjust the new regex to require 2 minute digits 2024-07-24 18:33:49 +02:00
djvs
8ecbb8b49b Expose filepath in columns view (#530)
* Expose filepath in columns view

* update
2024-07-24 18:15:02 +02:00
James Le Cuirot
b218d90193 Check for taglib with pkg-config before trying taglib-config (#558)
Programs like taglib-config are not good when cross-compiling.
2024-07-24 18:07:34 +02:00
gardockt
95bca25cb6 Tag editor: only write files with modified tags (#536) 2024-07-24 18:04:51 +02:00
George Abbott
8746ded880 Added hh:mm:ss to g command (#550)
* Added hh:mm:ss to g command

* Updated statusbar prompt with h:m:ss as well

* corrected spaces to tabs
2024-07-24 18:01:07 +02:00
Alex Balgavy
f76dd085b2 Fix crash when background-fetching lyrics for streams (#561)
When `fetch_lyrics_for_current_song_in_background` is set to `yes`, and
an HTTP(S) stream is loaded with a long URL, ncmpcpp crashes due to the
length of the filename generated to store lyrics. This commit makes the
background lyrics fetcher ignore such streams, preventing the crash.
Addresses issue #380.
2024-07-24 17:57:38 +02:00
Denys Tynok
e87c104dd6 fix: update obsolete macros in autoconf (#582)
* fix: update obsolete macros in autoconf

* fix: update obsolete macro 'AC_PROG_LIBTOOL' in autoconf
2024-07-24 17:56:14 +02:00
Andrzej Rybczak
afb150138d Change visibility of orig_termios 2024-07-24 17:55:35 +02:00
Drew Marino
51ebe9e52d Fixed bug related to readline.h (#592)
Terminals that don't restore the terminal state when a program exits display stdin incorrectly after quitting. This is not a bug in readline.h as its examples use termios in the same way.
2024-07-24 17:54:12 +02:00
Andrzej Rybczak
9c396d7ef0 Fix removal of XiphComments 2024-07-24 17:25:03 +02:00
Andrzej Rybczak
81e5cf58b4 extras: fix compilation with taglib 2.0 2024-05-30 14:58:44 +02:00
Andrzej Rybczak
dc46f7a49b Fix compilation with taglib 2.0 2024-02-07 14:29:09 +01:00
Salastil
c7593a2087 Missing quote fix 2023-09-21 21:41:43 -04:00
Salastil
73f5de7511 Merge branch 'lenerd/youtube-dl' 2023-08-27 03:25:28 -04:00
Larson Carter
9f44edf0b1 Merge pull request #560 from zaidhaan/fix-mpd-block
Fix unresponsive stall when server error occurs
2022-12-31 12:52:39 -06:00
Zaidhaan Hussain
d96e3e514d Fix MPD blocking when server error occurs by handling directly without blocking further execution by throwing 2022-11-21 23:05:31 +08:00
Robert Kaspar
417d7172e5 Read Performer from TPE4, not TPE3 (#497) 2022-02-14 00:48:11 +01:00
leg7
727ca95ca5 Increase the framerate limit for the visualizer. (#490)
* Update settings.cpp

* Update settings.cpp
2022-02-14 00:47:32 +01:00
squigz
26ee24a938 Add italic text support (#527) 2022-02-14 00:42:13 +01:00
Jeffrey Paul
02754534fd update homepage link to https (#529)
modern browsers don't support http
2022-02-08 15:50:38 +01:00
Andrzej Rybczak
40e4f98ee8 Support sockets without a socket inode 2021-08-25 02:37:54 +02:00
Andrzej Rybczak
b023c737e3 Remove options deprecated in 0.9 2021-07-29 13:19:54 +02:00
Andrzej Rybczak
70ca94f739 Curl: stop setting user agent to ncmpcpp 2021-07-27 00:47:46 +02:00
Andrzej Rybczak
df1c4d7d7d Fix genius lyrics fetcher 2021-07-26 23:44:36 +02:00
Andrzej Rybczak
35f9262a6e Fix compilation 2021-04-25 23:57:56 +02:00
Jinwoo Park
f47cf7f37c Configurable Column Widths (#360)
* add configurable column widths

* reorder to match config file

* fix possibly misleading documentation

* fix crash when integer out of bounds

* parse config string during initial processing

* use std::bind with length of list

* fix division by zero error

* use list_of

* change escaped_list_separator variable names
2021-04-25 19:42:56 +02:00
Jonathan Neidel
22fd919ce4 Fix separator between albums with the same name, to check for album artist instead of artist (#472) 2021-04-25 19:41:07 +02:00
John Ferguson
71970fa73c Fix overflow errors and allow small terminal sizes (#473)
* In #259 users request the ability to have ncmpcpp remain open when the
  terminal has a small size. Previously ncmpcpp would exit when there
  were less than 30 columns or 5 rows.
* After removing this constraint, ncmpcpp would freeze when terminal
  sizes went below those thresholds.
* Upon debugging, it became clear that ncmpcpp wasn't frozen, it was
  stuck in a very long for loop because it thought the display was
  unreasonably large (resulting from integer overflow).
* Preventing that overflow allows ncmpcpp to handle small sizes
  gracefully. No other changes to the rendering code were necessary.

Fixes #259
2021-04-25 19:39:52 +02:00
John Ferguson
295663fc1c update .gitignore to ignore more build artifacts (#475) 2021-04-25 19:37:27 +02:00
47 changed files with 1218 additions and 619 deletions

View File

@@ -3,7 +3,7 @@ name: Bug report
about: Create a report to help us improve about: Create a report to help us improve
title: "[BUG]" title: "[BUG]"
labels: bug labels: bug
assignees: larson-carter assignees:
--- ---

View File

@@ -3,7 +3,7 @@ name: Feature request
about: Suggest an idea for this project about: Suggest an idea for this project
title: "[FEATURE]" title: "[FEATURE]"
labels: enhancement labels: enhancement
assignees: larson-carter assignees:
--- ---

79
.github/workflows/build.yml vendored Normal file
View File

@@ -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

5
.gitignore vendored
View File

@@ -6,13 +6,16 @@ Makefile
Makefile.in Makefile.in
aclocal.m4 aclocal.m4
autom4te.cache/ autom4te.cache/
compile
config.* config.*
configure configure
depcomp depcomp
**/.dirstamp
install-sh install-sh
libtool libtool
ltmain.sh ltmain.sh
missing missing
src/.deps/ **.m4
**/.deps/
src/ncmpcpp src/ncmpcpp
stamp-h1 stamp-h1

View File

@@ -1,6 +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`. * Add the configuration option `mpd_password`.
* Separate chunks of lyrics with a double newline. * 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) # ncmpcpp-0.9.2 (2021-01-24)
* Revert suppression of output of all external commands as that makes e.g album * Revert suppression of output of all external commands as that makes e.g album

View File

@@ -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 For the next two commands, `csh' users will need to prefix them with
`sh '. `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 3. Run `./configure' to configure the package for your system. This
will take a while. While running, it prints some messages will take a while. While running, it prints some messages

View File

@@ -1,11 +1,22 @@
# NCurses Music Player Client (Plus Plus) # NCurses Music Player Client (Plus Plus) (Plus yt-dlp)
Project page - http://rybczak.net/ncmpcpp/
## ncmpcpp featureful ncurses based MPD client inspired by ncmpc ## ncmpcpp featureful ncurses based MPD client inspired by ncmpc
### Main features: ### yt-dlp fork
This fork allows adding items with yt-dlp via shift+d at the playlist screen, add a single item or a playlist (with a cap of 100 items)
## 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 * tag editor
* playlist editor * playlist editor
* easy to use search engine * easy to use search engine
@@ -18,19 +29,18 @@ Project page - http://rybczak.net/ncmpcpp/
…and a lot more minor functions. …and a lot more minor functions.
### Dependencies: ### Dependencies:
* [boost](https://www.boost.org/)
* boost library [https://www.boost.org/] * [ncurses](https://invisible-island.net/ncurses/announce.html)
* ncurses library [http://www.gnu.org/software/ncurses/ncurses.html] * [readline](https://tiswww.case.edu/php/chet/readline/rltop.html)
* readline library [https://tiswww.case.edu/php/chet/readline/rltop.html] * [curl](https://curl.se), for fetching lyrics and last.fm data
* curl library (optional, required for fetching lyrics and last.fm data) [https://curl.haxx.se/] #### Optional libraries
* fftw library (optional, required for frequency spectrum music visualization mode) [http://www.fftw.org/] * [fftw](http://www.fftw.org), for frequency spectrum music visualization mode
* tag library (optional, required for tag editing) [https://taglib.org/] * [taglib](https://taglib.org/), for tag editing
### Known issues: ### Known issues:
* No full support for handling encodings other than UTF-8. * No full support for handling encodings other than UTF-8.
### Installation: ### Installation:
The simplest way to compile this package is: The simplest way to compile this package is:
1. `cd` to the directory containing the package's source code. 1. `cd` to the directory containing the package's source code.
@@ -38,7 +48,7 @@ The simplest way to compile this package is:
For the next two commands, `csh` users will need to prefix them with For the next two commands, `csh` users will need to prefix them with
`sh `. `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 3. Run `./configure` to configure the package for your system. This
will take a while. While running, it prints some messages will take a while. While running, it prints some messages
@@ -55,7 +65,6 @@ The simplest way to compile this package is:
Detailed intallation instructions can be found in the `INSTALL` file. Detailed intallation instructions can be found in the `INSTALL` file.
### Optional features: ### Optional features:
Optional features can be enable by specifying them during configure. For Optional features can be enable by specifying them during configure. For
example, to enable visualizer run `./configure --enable-visualizer`. example, to enable visualizer run `./configure --enable-visualizer`.

View File

@@ -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

View File

@@ -1,15 +1,15 @@
AC_INIT([ncmpcpp], [0.10_dev]) AC_INIT([ncmpcpp],[0.10.2_dev])
AC_CONFIG_SRCDIR([configure.ac]) AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_HEADERS(config.h) AC_CONFIG_HEADERS(config.h)
AM_INIT_AUTOMAKE([subdir-objects]) AM_INIT_AUTOMAKE([subdir-objects])
AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_MACRO_DIR([m4])
AC_PREREQ(2.59) AC_PREREQ([2.71])
AC_LANG_CPLUSPLUS AC_LANG([C++])
AC_PROG_CXX 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(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]) 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" CXXFLAGS="$old_CXXFLAGS $fast_math"
# -std=c++14 # -std=c++20
AC_MSG_CHECKING([whether compiler supports -std=c++14]) AC_MSG_CHECKING([whether compiler supports -std=c++20])
old_CXXFLAGS="$CXXFLAGS" old_CXXFLAGS="$CXXFLAGS"
CXXFLAGS="-std=c++14" CXXFLAGS="-std=c++20"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]])], AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]])],
AC_MSG_RESULT([yes]) AC_MSG_RESULT([yes])
std_cpp14="-std=c++14", std_cpp14="-std=c++20",
AC_MSG_RESULT([no]) 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" 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)]]) 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
BOOST_REQUIRE([1.60]) BOOST_REQUIRE([1.60])
AC_SUBST(BOOST_CPPFLAGS) AC_SUBST(BOOST_CPPFLAGS)
@@ -139,7 +142,7 @@ LIBS="$LIBS $BOOST_REGEX_LIBS"
BOOST_THREAD BOOST_THREAD
AC_SUBST(BOOST_THREAD_LDFLAGS) AC_SUBST(BOOST_THREAD_LDFLAGS)
AC_SUBST(BOOST_THREAD_LIBS) AC_SUBST(BOOST_THREAD_LIBS)
LDFLAGS+="$LDFLAGS $BOOST_THREAD_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS"
LIBS="$LIBS $BOOST_THREAD_LIBS" LIBS="$LIBS $BOOST_THREAD_LIBS"
# icu # icu
@@ -149,7 +152,7 @@ PKG_CHECK_MODULES([ICU], [icu-i18n icu-uc], [
old_LIBS="$LIBS" old_LIBS="$LIBS"
AC_SUBST(ICU_CFLAGS) AC_SUBST(ICU_CFLAGS)
AC_SUBST(ICU_LIBS) AC_SUBST(ICU_LIBS)
CPPFLAGS="$CPPFLAGS $ICU_CFLAGS -DU_USING_ICU_NAMESPACE=0" CPPFLAGS="$CPPFLAGS $ICU_CFLAGS"
LIBS="$LIBS $ICU_LIBS" LIBS="$LIBS $ICU_LIBS"
AC_MSG_CHECKING([whether boost.regex was compiled with ICU support]) AC_MSG_CHECKING([whether boost.regex was compiled with ICU support])
AC_LINK_IFELSE([AC_LANG_PROGRAM([ AC_LINK_IFELSE([AC_LANG_PROGRAM([
@@ -257,19 +260,30 @@ PKG_CHECK_MODULES([libcurl], [libcurl], [
# taglib # taglib
if test "$taglib" != "no" ; then if test "$taglib" != "no" ; then
AC_PATH_PROG(TAGLIB_CONFIG, taglib-config) PKG_CHECK_MODULES([taglib], [taglib], [
if test "$TAGLIB_CONFIG" != "" ; then AC_SUBST(taglib_CFLAGS)
CPPFLAGS="$CPPFLAGS `$TAGLIB_CONFIG --cflags`" AC_SUBST(taglib_LIBS)
LIBS="$LIBS `$TAGLIB_CONFIG --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], , AC_CHECK_HEADERS([taglib.h], ,
if test "$taglib" = "yes" ; then if test "$taglib" = "yes" ; then
AC_MSG_ERROR([missing taglib.h header]) AC_MSG_ERROR([missing taglib.h header])
fi fi
) )
else
if test "$taglib" = "yes" ; then
AC_MSG_ERROR([taglib-config executable is missing])
fi
fi fi
fi fi

View File

@@ -125,6 +125,14 @@
# #
#visualizer_spectrum_smooth_look = yes #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 ## 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 look at a larger slice of time, which results in less jumpy
## visualizer output. ## visualizer output.
@@ -141,6 +149,11 @@
# #
#visualizer_spectrum_hz_max = 20000 #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 ##### ##### system encoding #####
## ##
## ncmpcpp should detect your charset encoding but if it failed to do so, you ## ncmpcpp should detect your charset encoding but if it failed to do so, you
@@ -172,6 +185,7 @@
## ##
## %l - length ## %l - length
## %f - filename ## %f - filename
## %F - full filepath
## %D - directory ## %D - directory
## %a - artist ## %a - artist
## %A - album artist ## %A - album artist
@@ -215,6 +229,7 @@
## - 9 - end of current color ## - 9 - end of current color
## - b - bold text ## - b - bold text
## - u - underline text ## - u - underline text
## - i - italic text
## - r - reverse colors ## - r - reverse colors
## - a - use alternative character set ## - a - use alternative character set
## ##
@@ -407,7 +422,7 @@
# #
#cyclic_scrolling = no #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 #follow_now_playing_lyrics = no
# #
@@ -465,6 +480,20 @@
# #
#ask_for_locked_screen_width_part = yes #ask_for_locked_screen_width_part = yes
# #
##
## Width of media_library screen columns
##
#
#media_library_column_width_ratio_two = 1:1
#
#media_library_column_width_ratio_three = 1:1:1
#
##
## Width of playlist_editor screen columns
##
#
#playlist_editor_column_width_ratio = 1:2
#
#jump_to_now_playing_song_at_start = yes #jump_to_now_playing_song_at_start = yes
# #
#ask_before_clearing_playlists = yes #ask_before_clearing_playlists = yes

View File

@@ -114,6 +114,13 @@ Automatically scale visualizer size.
.B visualizer_spectrum_smooth_look = yes/no .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. 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 .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 .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. 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 .TP
@@ -335,6 +342,15 @@ If you want to lock a screen, ncmpcpp asks for % of locked screen's width to be
.B ask_for_locked_screen_width_part = yes/no .B ask_for_locked_screen_width_part = yes/no
If enabled, ncmpcpp will ask for % of locked screen's width each time you want to lock a screen. If you disable that, it'll silently attempt to use default value. If enabled, ncmpcpp will ask for % of locked screen's width each time you want to lock a screen. If you disable that, it'll silently attempt to use default value.
.TP .TP
.B media_library_column_width_ratio_two = a:b
The ratio of the column widths in the media library, when there are two columns.
.TP
.B media_library_column_width_ratio_three = a:b:c
The ratio of the column widths in the media library, when there are three columns.
.TP
.B playlist_editor_column_width_ratio = a:b
The ratio of the column widths in the playlist editor.
.TP
.B jump_to_now_playing_song_at_start = yes/no .B jump_to_now_playing_song_at_start = yes/no
If enabled, ncmpcpp will jump at start to now playing song if mpd is playing or paused. If enabled, ncmpcpp will jump at start to now playing song if mpd is playing or paused.
.TP .TP
@@ -463,6 +479,7 @@ For song format you can use:
%l - length %l - length
%f - filename %f - filename
%F - full filepath
%D - directory %D - directory
%a - artist %a - artist
%A - album artist %A - album artist

View File

@@ -34,7 +34,7 @@ enum class CopyResult { Success, NoArtist, AlbumArtistAlreadyInPlace };
bool is_framelist_empty(const TagLib::ID3v2::FrameList &list) bool is_framelist_empty(const TagLib::ID3v2::FrameList &list)
{ {
for (auto it = list.begin(); it != list.end(); ++it) for (auto it = list.begin(); it != list.end(); ++it)
if ((*it)->toString() != TagLib::String::null) if (!(*it)->toString().isEmpty())
return false; return false;
return true; return true;
} }

View File

@@ -22,7 +22,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
m4_define([_BOOST_SERIAL], [m4_translit([ 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], AC_CACHE_CHECK([for Boost's header version],
[boost_cv_lib_version], [boost_cv_lib_version],
[m4_pattern_allow([^BOOST_LIB_VERSION$])dnl [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/version.hpp> [#include <boost/version.hpp>
boost-lib-version = BOOST_LIB_VERSION], boost-lib-version = BOOST_LIB_VERSION],
[boost_cv_lib_version=`cat conftest.i`])]) [boost_cv_lib_version=`cat conftest.i`])])
@@ -288,14 +288,17 @@ fi
# BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES], # BOOST_FIND_LIBS([COMPONENT-NAME], [CANDIDATE-LIB-NAMES],
# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [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 # Look for the Boost library COMPONENT-NAME (e.g., `thread', for
# libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g., # libboost_thread) under the possible CANDIDATE-LIB-NAMES (e.g.,
# "thread_win32 thread"). Check that HEADER-NAME works and check that # "thread_win32 thread"). Check that HEADER-NAME works and check that
# libboost_LIB-NAME can link with the code CXX-TEST. The optional # libboost_LIB-NAME can link with the code CXX-TEST. The optional
# argument CXX-PROLOGUE can be used to include some C++ code before # 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 <HEADER-NAME>'.
# #
# Invokes BOOST_FIND_HEADER([HEADER-NAME]) (see above). # 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 # 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 # ... If you want to make sure you have a specific version of Boost
# (eg, >= 1.33) you *must* invoke BOOST_REQUIRE before this macro. # (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_DEFUN([BOOST_FIND_LIBS],
[AC_REQUIRE([BOOST_REQUIRE])dnl [AC_REQUIRE([BOOST_REQUIRE])dnl
AC_REQUIRE([_BOOST_FIND_COMPILER_TAG])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 if test x"$boost_cv_inc_path" = xno; then
AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library]) AC_MSG_NOTICE([Boost not available, not searching for the Boost $1 library])
else 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 AC_LANG_PUSH([C++])dnl
AS_VAR_PUSHDEF([Boost_lib], [boost_cv_lib_$1])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_LDFLAGS], [boost_cv_lib_$1_LDFLAGS])dnl
AS_VAR_PUSHDEF([Boost_lib_LDPATH], [boost_cv_lib_$1_LDPATH])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 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 boost_save_CPPFLAGS=$CPPFLAGS
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS" CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib], AC_CACHE_CHECK([for the Boost $1 library], [Boost_lib],
[_BOOST_FIND_LIBS($@)]) [_BOOST_FIND_LIBS($@)])
case $Boost_lib in #( 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 (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 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 CPPFLAGS=$boost_save_CPPFLAGS
AS_VAR_POPDEF([Boost_lib])dnl AS_VAR_POPDEF([Boost_lib])dnl
AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl AS_VAR_POPDEF([Boost_lib_LDFLAGS])dnl
@@ -349,16 +361,20 @@ fi
# BOOST_FIND_LIB([LIB-NAME], # BOOST_FIND_LIB([LIB-NAME],
# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [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. # 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], AC_DEFUN([BOOST_FIND_LIB],
[BOOST_FIND_LIBS([$1], $@)]) [BOOST_FIND_LIBS([$1], $@)])
# _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES], # _BOOST_FIND_LIBS([LIB-NAME], [CANDIDATE-LIB-NAMES],
# [PREFERRED-RT-OPT], [HEADER-NAME], [CXX-TEST], # [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: # Real implementation of BOOST_FIND_LIBS: rely on these local macros:
# Boost_lib, Boost_lib_LDFLAGS, Boost_lib_LDPATH, Boost_lib_LIBS # 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 # usually installed. If we can't find the standard variants, we try
# to enforce -mt (for instance on MacOSX, libboost_thread.dylib # to enforce -mt (for instance on MacOSX, libboost_thread.dylib
# doesn't exist but there's -obviously- libboost_thread-mt.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], AC_DEFUN([_BOOST_FIND_LIBS],
[Boost_lib=no [Boost_lib=no
case "$3" in #( case "$3" in #(
@@ -396,7 +415,8 @@ AC_DEFUN([_BOOST_FIND_LIBS],
AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?]) AC_MSG_ERROR([the libext variable is empty, did you invoke Libtool?])
boost_save_ac_objext=$ac_objext boost_save_ac_objext=$ac_objext
# Generate the test file. # Generate the test file.
AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <$4> AC_LANG_CONFTEST([AC_LANG_PROGRAM([$7
#include <$4>
$6], [$5])]) $6], [$5])])
dnl Optimization hacks: compiling C++ is slow, especially with Boost. What 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 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). dnl start the for loops).
AC_COMPILE_IFELSE([], AC_COMPILE_IFELSE([],
[ac_objext=do_not_rm_me_plz], [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 ac_objext=$boost_save_ac_objext
boost_failed_libs= boost_failed_libs=
# Don't bother to ident the following nested for loops, only the 2 # 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_ver_ in -$boost_cv_lib_version ''; do
for boost_mt_ in $boost_mt -mt ''; do for boost_mt_ in $boost_mt -mt ''; do
for boost_rtopt_ in $boost_rtopt '' -d; do for boost_rtopt_ in $boost_rtopt '' -d; do
for boost_lib in \ for boost_full_suffix in \
boost_$boost_lib_$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \ $boost_last_suffix \
boost_$boost_lib_$boost_tag_$boost_rtopt_$boost_ver_ \ x$boost_tag_$boost_mt_$boost_rtopt_$boost_ver_ \
boost_$boost_lib_$boost_tag_$boost_mt_$boost_ver_ \ x$boost_tag_$boost_rtopt_$boost_ver_ \
boost_$boost_lib_$boost_tag_$boost_ver_ x$boost_tag_$boost_mt_$boost_ver_ \
x$boost_tag_$boost_ver_
do 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 # Avoid testing twice the same lib
case $boost_failed_libs in #( case $boost_failed_libs in #(
(*@$boost_lib@*) continue;; (*@$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 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" 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_AC_LINK_IFELSE([],
[boost_rpath_link_ldflag_found=yes [boost_rpath_link_ldflag_found=yes
break], break],
@@ -496,6 +522,7 @@ dnl generated only once above (before we start the for loops).
test x"$boost_ldpath" != x && test x"$boost_ldpath" != x &&
Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath" Boost_lib_LDFLAGS="-L$boost_ldpath $boost_cv_rpath_link_ldflag$boost_ldpath"
Boost_lib_LDPATH="$boost_ldpath" Boost_lib_LDPATH="$boost_ldpath"
boost_last_suffix="$boost_full_suffix"
break 7 break 7
else else
boost_failed_libs="$boost_failed_libs@$boost_lib@" 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() # BOOST_ARRAY()
# ------------- # -------------
# Look for Boost.Array # Look for Boost.Array
@@ -548,6 +583,12 @@ BOOST_DEFUN([Asio],
[AC_REQUIRE([BOOST_SYSTEM])dnl [AC_REQUIRE([BOOST_SYSTEM])dnl
BOOST_FIND_HEADER([boost/asio.hpp])]) BOOST_FIND_HEADER([boost/asio.hpp])])
# BOOST_BIMAP()
# ------------
# Look for Boost.Bimap
BOOST_DEFUN([Bimap],
[BOOST_FIND_HEADER([boost/bimap.hpp])])
# BOOST_ASSIGN() # BOOST_ASSIGN()
# ------------- # -------------
@@ -556,6 +597,24 @@ BOOST_DEFUN([Assign],
[BOOST_FIND_HEADER([boost/assign.hpp])]) [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<int> a;],
[ ],
[#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif], [$2])
])# BOOST_ATOMIC
# BOOST_BIND() # BOOST_BIND()
# ------------ # ------------
# Look for Boost.Bind. # Look for Boost.Bind.
@@ -563,7 +622,14 @@ BOOST_DEFUN([Bind],
[BOOST_FIND_HEADER([boost/bind.hpp])]) [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. # Look for Boost.Chrono.
BOOST_DEFUN([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 # 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. # find Boost.System as it didn't exist by then.
if test $boost_major_version -ge 135; then if test $boost_major_version -ge 135; then
BOOST_SYSTEM([$1]) BOOST_SYSTEM([$1], [$2])
fi # end of the Boost.System check. fi # end of the Boost.System check.
boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LIBS=$LIBS
boost_filesystem_save_LDFLAGS=$LDFLAGS boost_filesystem_save_LDFLAGS=$LDFLAGS
@@ -580,7 +646,7 @@ LIBS="$LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS"
BOOST_FIND_LIB([chrono], [$1], BOOST_FIND_LIB([chrono], [$1],
[boost/chrono.hpp], [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 if test $enable_static_boost = yes && test $boost_major_version -ge 135; then
BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS" BOOST_CHRONO_LIBS="$BOOST_CHRONO_LIBS $BOOST_SYSTEM_LIBS"
fi fi
@@ -589,7 +655,7 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS
])# BOOST_CHRONO ])# 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 # Look for Boost.Context. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
@@ -597,17 +663,76 @@ LDFLAGS=$boost_filesystem_save_LDFLAGS
# * This library was introduced in Boost 1.51.0 # * This library was introduced in Boost 1.51.0
# * The signatures of make_fcontext() and jump_fcontext were changed in 1.56.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 # * 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_DEFUN([Context],
[boost_context_save_LIBS=$LIBS [boost_context_save_LIBS=$LIBS
boost_context_save_LDFLAGS=$LDFLAGS boost_context_save_LDFLAGS=$LDFLAGS
if test $boost_major_version -ge 157; then if test $boost_major_version -ge 157; then
BOOST_THREAD([$1]) BOOST_THREAD([$1], [$2])
m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl m4_pattern_allow([^BOOST_THREAD_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_THREAD_LIBS" LIBS="$LIBS $BOOST_THREAD_LIBS"
LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_THREAD_LDFLAGS"
fi fi
if test $boost_major_version -ge 169; then
BOOST_FIND_LIB([context], [$1], 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<int> source(
[n, &res](ctx::execution_context<int> 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 // creates a stack
void * stack_pointer = new void*[4096]; void * stack_pointer = new void*[4096];
@@ -662,7 +787,10 @@ static void f(intptr_t i) {
ctx::jump_fcontext(&fc, fcm, i * 2); ctx::jump_fcontext(&fc, fcm, i * 2);
} }
#endif #endif
]) ], [], [], [$2])
fi
LIBS=$boost_context_save_LIBS LIBS=$boost_context_save_LIBS
LDFLAGS=$boost_context_save_LDFLAGS LDFLAGS=$boost_context_save_LDFLAGS
])# BOOST_CONTEXT ])# BOOST_CONTEXT
@@ -677,7 +805,7 @@ BOOST_FIND_HEADER([boost/lexical_cast.hpp])
])# BOOST_CONVERSION ])# 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 # 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 # 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_LIBS=$LIBS
boost_coroutine_save_LDFLAGS=$LDFLAGS boost_coroutine_save_LDFLAGS=$LDFLAGS
# Link-time dependency from coroutine to context # 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 # Starting from Boost 1.55 a dependency on Boost.System is added
if test $boost_major_version -ge 155; then if test $boost_major_version -ge 155; then
BOOST_SYSTEM([$1]) BOOST_SYSTEM([$1], [$2])
fi fi
m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)]) m4_pattern_allow([^BOOST_(CONTEXT|SYSTEM)_(LIBS|LDFLAGS)])
LIBS="$LIBS $BOOST_CONTEXT_LIBS $BOOST_SYSTEM_LIBS" 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 # in 1.53 coroutine was a header only library
if test $boost_major_version -eq 153; then 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 else
BOOST_FIND_LIB([coroutine], [$1], BOOST_FIND_LIB([coroutine], [$1],
[boost/coroutine/coroutine.hpp], [boost/coroutine/coroutine.hpp],
@@ -709,7 +838,7 @@ else
#else #else
boost::coroutines::asymmetric_coroutine<int>::pull_type coro; coro.get(); boost::coroutines::asymmetric_coroutine<int>::pull_type coro; coro.get();
#endif #endif
]) ], [], [], [$2])
fi fi
# Link-time dependency from coroutine to context, existed only in 1.53, in 1.54 # 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. # coroutine doesn't use context from its headers but from its library.
@@ -734,18 +863,25 @@ BOOST_DEFUN([CRC],
])# BOOST_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 # Look for Boost.Date_Time. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Date_Time], BOOST_DEFUN([Date_Time],
[BOOST_FIND_LIB([date_time], [$1], [BOOST_FIND_LIB([date_time], [$1],
[boost/date_time/posix_time/posix_time.hpp], [boost/date_time/posix_time/posix_time.hpp],
[boost::posix_time::ptime t;]) [boost::posix_time::ptime t;], [], [], [$2])
])# BOOST_DATE_TIME ])# 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 # Look for Boost.Filesystem. For the documentation of PREFERRED-RT-OPT, see
# the documentation of BOOST_FIND_LIB above. # 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 # 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. # find Boost.System as it didn't exist by then.
if test $boost_major_version -ge 135; then if test $boost_major_version -ge 135; then
BOOST_SYSTEM([$1]) BOOST_SYSTEM([$1], [$2])
fi # end of the Boost.System check. fi # end of the Boost.System check.
boost_filesystem_save_LIBS=$LIBS boost_filesystem_save_LIBS=$LIBS
boost_filesystem_save_LDFLAGS=$LDFLAGS boost_filesystem_save_LDFLAGS=$LDFLAGS
@@ -764,7 +900,8 @@ m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_SYSTEM_LIBS" LIBS="$LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS"
BOOST_FIND_LIB([filesystem], [$1], 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 if test $enable_static_boost = yes && test $boost_major_version -ge 135; then
BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" BOOST_FILESYSTEM_LIBS="$BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS"
fi fi
@@ -809,6 +946,13 @@ BOOST_DEFUN([Function],
[BOOST_FIND_HEADER([boost/function.hpp])]) [BOOST_FIND_HEADER([boost/function.hpp])])
# BOOST_FUSION()
# -----------------
# Look for Boost.Fusion
BOOST_DEFUN([Fusion],
[BOOST_FIND_HEADER([boost/fusion/sequence.hpp])])
# BOOST_GEOMETRY() # BOOST_GEOMETRY()
# ---------------- # ----------------
# Look for Boost.Geometry (new since 1.47.0). # Look for Boost.Geometry (new since 1.47.0).
@@ -817,7 +961,7 @@ BOOST_DEFUN([Geometry],
])# BOOST_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 # Look for Boost.Graphs. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
@@ -826,29 +970,19 @@ BOOST_DEFUN([Graph],
boost_graph_save_LDFLAGS=$LDFLAGS boost_graph_save_LDFLAGS=$LDFLAGS
# Link-time dependency from graph to regex was added as of 1.40.0. # Link-time dependency from graph to regex was added as of 1.40.0.
if test $boost_major_version -ge 140; then if test $boost_major_version -ge 140; then
BOOST_REGEX([$1]) BOOST_REGEX([$1], [$2])
m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl m4_pattern_allow([^BOOST_REGEX_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_REGEX_LIBS" LIBS="$LIBS $BOOST_REGEX_LIBS"
LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_REGEX_LDFLAGS"
fi fi
BOOST_FIND_LIB([graph], [$1], 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 LIBS=$boost_graph_save_LIBS
LDFLAGS=$boost_graph_save_LDFLAGS LDFLAGS=$boost_graph_save_LDFLAGS
])# BOOST_GRAPH ])# BOOST_GRAPH
# BOOST_IOSTREAMS([PREFERRED-RT-OPT])
# -----------------------------------
# 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
# BOOST_HASH() # BOOST_HASH()
# ------------ # ------------
# Look for Boost.Functional/Hash # Look for Boost.Functional/Hash
@@ -856,6 +990,25 @@ BOOST_DEFUN([Hash],
[BOOST_FIND_HEADER([boost/functional/hash.hpp])]) [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();],
[], [], [$2])
])# BOOST_IOSTREAMS
# BOOST_ITERATOR()
# ------------
# Look for Boost.Iterator
BOOST_DEFUN([Iterator],
[BOOST_FIND_HEADER([boost/iterator/iterator_adaptor.hpp])])
# BOOST_LAMBDA() # BOOST_LAMBDA()
# -------------- # --------------
# Look for Boost.Lambda # Look for Boost.Lambda
@@ -863,7 +1016,7 @@ BOOST_DEFUN([Lambda],
[BOOST_FIND_HEADER([boost/lambda/lambda.hpp])]) [BOOST_FIND_HEADER([boost/lambda/lambda.hpp])])
# BOOST_LOCALE() # BOOST_LOCALE([PREFERRED-RT-OPT], [ERROR_ON_UNUSABLE])
# -------------- # --------------
# Look for Boost.Locale # Look for Boost.Locale
BOOST_DEFUN([Locale], BOOST_DEFUN([Locale],
@@ -872,40 +1025,40 @@ boost_locale_save_LIBS=$LIBS
boost_locale_save_LDFLAGS=$LDFLAGS boost_locale_save_LDFLAGS=$LDFLAGS
# require SYSTEM for boost-1.50.0 and up # require SYSTEM for boost-1.50.0 and up
if test $boost_major_version -ge 150; then if test $boost_major_version -ge 150; then
BOOST_SYSTEM([$1]) BOOST_SYSTEM([$1], [$2])
m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_SYSTEM_LIBS" LIBS="$LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS"
fi # end of the Boost.System check. fi # end of the Boost.System check.
BOOST_FIND_LIB([locale], [$1], BOOST_FIND_LIB([locale], [$1],
[boost/locale.hpp], [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 LIBS=$boost_locale_save_LIBS
LDFLAGS=$boost_locale_save_LDFLAGS LDFLAGS=$boost_locale_save_LDFLAGS
])# BOOST_LOCALE ])# 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 # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Log], BOOST_DEFUN([Log],
[boost_log_save_LIBS=$LIBS [boost_log_save_LIBS=$LIBS
boost_log_save_LDFLAGS=$LDFLAGS boost_log_save_LDFLAGS=$LDFLAGS
BOOST_SYSTEM([$1]) BOOST_SYSTEM([$1], [$2])
BOOST_FILESYSTEM([$1]) BOOST_FILESYSTEM([$1], [$2])
BOOST_DATE_TIME([$1]) BOOST_DATE_TIME([$1], [$2])
m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl m4_pattern_allow([^BOOST_(SYSTEM|FILESYSTEM|DATE_TIME)_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS" LIBS="$LIBS $BOOST_DATE_TIME_LIBS $BOOST_FILESYSTEM_LIBS $BOOST_SYSTEM_LIBS"
LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_DATE_TIME_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS $BOOST_SYSTEM_LDFLAGS"
BOOST_FIND_LIB([log], [$1], BOOST_FIND_LIB([log], [$1],
[boost/log/core/core.hpp], [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 LIBS=$boost_log_save_LIBS
LDFLAGS=$boost_log_save_LDFLAGS LDFLAGS=$boost_log_save_LDFLAGS
])# BOOST_LOG ])# 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 # Look for Boost.Log. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
@@ -918,7 +1071,7 @@ LIBS="$LIBS $BOOST_LOG_LIBS"
LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS" LDFLAGS="$LDFLAGS $BOOST_LOG_LDFLAGS"
BOOST_FIND_LIB([log_setup], [$1], BOOST_FIND_LIB([log_setup], [$1],
[boost/log/utility/setup/from_settings.hpp], [boost/log/utility/setup/from_settings.hpp],
[boost::log::basic_settings<char> bs; bs.empty();]) [boost::log::basic_settings<char> bs; bs.empty();], [], [], [$2])
LIBS=$boost_log_setup_save_LIBS LIBS=$boost_log_setup_save_LIBS
LDFLAGS=$boost_log_setup_save_LDFLAGS LDFLAGS=$boost_log_setup_save_LDFLAGS
])# BOOST_LOG_SETUP ])# BOOST_LOG_SETUP
@@ -936,7 +1089,7 @@ BOOST_DEFUN([Math],
[BOOST_FIND_HEADER([boost/math/special_functions.hpp])]) [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 # 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 # documentation of BOOST_FIND_LIB above. Uses MPICXX variable if it is
@@ -953,12 +1106,20 @@ BOOST_FIND_LIB([mpi], [$1],
[boost/mpi.hpp], [boost/mpi.hpp],
[int argc = 0; [int argc = 0;
char **argv = 0; char **argv = 0;
boost::mpi::environment env(argc,argv);]) boost::mpi::environment env(argc,argv);],
[], [], [$2])
CXX=${boost_save_CXX} CXX=${boost_save_CXX}
CXXCPP=${boost_save_CXXCPP} CXXCPP=${boost_save_CXXCPP}
])# BOOST_MPI ])# BOOST_MPI
# BOOST_MPL()
# ------------------
# Look for Boost.MPL
BOOST_DEFUN([MPL],
[BOOST_FIND_HEADER([boost/mpl/for_each.hpp])])
# BOOST_MULTIARRAY() # BOOST_MULTIARRAY()
# ------------------ # ------------------
# Look for Boost.MultiArray # Look for Boost.MultiArray
@@ -966,6 +1127,13 @@ BOOST_DEFUN([MultiArray],
[BOOST_FIND_HEADER([boost/multi_array.hpp])]) [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() # BOOST_NUMERIC_UBLAS()
# -------------------------- # --------------------------
# Look for Boost.NumericUblas (Basic Linear Algebra) # Look for Boost.NumericUblas (Basic Linear Algebra)
@@ -996,6 +1164,25 @@ BOOST_DEFUN([Preprocessor],
[BOOST_FIND_HEADER([boost/preprocessor/repeat.hpp])]) [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() # BOOST_RANGE()
# -------------------- # --------------------
# Look for Boost.Range # Look for Boost.Range
@@ -1016,14 +1203,15 @@ BOOST_DEFUN([Uuid],
[BOOST_FIND_HEADER([boost/uuid/uuid.hpp])]) [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, # Look for Boost.Program_options. For the documentation of PREFERRED-RT-OPT,
# see the documentation of BOOST_FIND_LIB above. # see the documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Program_Options], BOOST_DEFUN([Program_Options],
[BOOST_FIND_LIB([program_options], [$1], [BOOST_FIND_LIB([program_options], [$1],
[boost/program_options.hpp], [boost/program_options.hpp],
[boost::program_options::options_description d("test");]) [boost::program_options::options_description d("test");],
[], [], [$2])
])# BOOST_PROGRAM_OPTIONS ])# BOOST_PROGRAM_OPTIONS
@@ -1039,7 +1227,7 @@ boost_python_save_$1=$$1
$1="$$1 $BOOST_PYTHON_$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, # Look for Boost.Python. For the documentation of PREFERRED-RT-OPT,
# see the documentation of BOOST_FIND_LIB above. # see the documentation of BOOST_FIND_LIB above.
@@ -1050,7 +1238,7 @@ _BOOST_PYTHON_CONFIG([LIBS], [libs])
m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl m4_pattern_allow([^BOOST_PYTHON_MODULE$])dnl
BOOST_FIND_LIBS([python], [python python3], [$1], BOOST_FIND_LIBS([python], [python python3], [$1],
[boost/python.hpp], [boost/python.hpp],
[], [BOOST_PYTHON_MODULE(empty) {}]) [], [BOOST_PYTHON_MODULE(empty) {}], [], [$2])
CPPFLAGS=$boost_python_save_CPPFLAGS CPPFLAGS=$boost_python_save_CPPFLAGS
LDFLAGS=$boost_python_save_LDFLAGS LDFLAGS=$boost_python_save_LDFLAGS
LIBS=$boost_python_save_LIBS LIBS=$boost_python_save_LIBS
@@ -1064,18 +1252,26 @@ BOOST_DEFUN([Ref],
[BOOST_FIND_HEADER([boost/ref.hpp])]) [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 # Look for Boost.Regex. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Regex], BOOST_DEFUN([Regex],
[BOOST_FIND_LIB([regex], [$1], [BOOST_FIND_LIB([regex], [$1],
[boost/regex.hpp], [boost/regex.hpp],
[boost::regex exp("*"); boost::regex_match("foo", exp);]) [boost::regex exp("*"); boost::regex_match("foo", exp);],
[], [], [$2])
])# BOOST_REGEX ])# 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 # Look for Boost.Serialization. For the documentation of PREFERRED-RT-OPT, see
# the documentation of BOOST_FIND_LIB above. # the documentation of BOOST_FIND_LIB above.
@@ -1083,18 +1279,20 @@ BOOST_DEFUN([Serialization],
[BOOST_FIND_LIB([serialization], [$1], [BOOST_FIND_LIB([serialization], [$1],
[boost/archive/text_oarchive.hpp], [boost/archive/text_oarchive.hpp],
[std::ostream* o = 0; // Cheap way to get an ostream... [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_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 # Look for Boost.Signals. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
BOOST_DEFUN([Signals], BOOST_DEFUN([Signals],
[BOOST_FIND_LIB([signals], [$1], [BOOST_FIND_LIB([signals], [$1],
[boost/signal.hpp], [boost/signal.hpp],
[boost::signal<void ()> s;]) [boost::signal<void ()> s;],
[], [], [$2])
])# BOOST_SIGNALS ])# 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 # 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 # 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_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.hpp],
[boost::system::error_code e; e.clear();]) [boost::system::error_code e; e.clear();], [], [], [$2])
fi
])# BOOST_SYSTEM ])# 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 # Look for Boost.Test. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # 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);], [boost/test/unit_test.hpp], [BOOST_CHECK(2 == 2);],
[using boost::unit_test::test_suite; [using boost::unit_test::test_suite;
test_suite* init_unit_test_suite(int argc, char ** argv) test_suite* init_unit_test_suite(int argc, char ** argv)
{ return NULL; }]) { return NULL; }], [], [$2])
])# BOOST_TEST ])# 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 # Look for Boost.Thread. For the documentation of PREFERRED-RT-OPT, see the
# documentation of BOOST_FIND_LIB above. # documentation of BOOST_FIND_LIB above.
@@ -1170,7 +1373,7 @@ boost_thread_save_LDFLAGS=$LDFLAGS
boost_thread_save_CPPFLAGS=$CPPFLAGS boost_thread_save_CPPFLAGS=$CPPFLAGS
# Link-time dependency from thread to system was added as of 1.49.0. # Link-time dependency from thread to system was added as of 1.49.0.
if test $boost_major_version -ge 149; then if test $boost_major_version -ge 149; then
BOOST_SYSTEM([$1]) BOOST_SYSTEM([$1], [$2])
fi # end of the Boost.System check. fi # end of the Boost.System check.
m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl m4_pattern_allow([^BOOST_SYSTEM_(LIBS|LDFLAGS)$])dnl
LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag" LIBS="$LIBS $BOOST_SYSTEM_LIBS $boost_cv_pthread_flag"
@@ -1189,7 +1392,7 @@ if test $boost_major_version -lt 148; then
fi fi
BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext], BOOST_FIND_LIBS([thread], [thread$boost_thread_lib_ext],
[$1], [$1],
[boost/thread.hpp], [boost::thread t; boost::mutex m;]) [boost/thread.hpp], [boost::thread t; boost::mutex m;], [], [], [$2])
case $host_os in case $host_os in
(*mingw*) boost_thread_w32_socket_link=-lws2_32;; (*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_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 # NOTE: If you intend to use Wave/Spirit with thread support, make sure you
# call BOOST_THREAD first. # call BOOST_THREAD first.
@@ -1283,7 +1486,7 @@ LDFLAGS="$LDFLAGS $BOOST_SYSTEM_LDFLAGS $BOOST_FILESYSTEM_LDFLAGS \
$BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS" $BOOST_DATE_TIME_LDFLAGS $BOOST_THREAD_LDFLAGS"
BOOST_FIND_LIB([wave], [$1], BOOST_FIND_LIB([wave], [$1],
[boost/wave.hpp], [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 LIBS=$boost_wave_save_LIBS
LDFLAGS=$boost_wave_save_LDFLAGS LDFLAGS=$boost_wave_save_LDFLAGS
])# BOOST_WAVE ])# 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";; -pthreads -mthreads -lpthread --thread-safe -mt";;
esac esac
# Generate the test file. # Generate the test file.
AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <pthread.h>], AC_LANG_CONFTEST([AC_LANG_PROGRAM([#include <pthread.h>
[pthread_t th; pthread_join(th, 0); void *f(void*){ return 0; }],
pthread_attr_init(0); pthread_cleanup_push(0, 0); [pthread_t th; pthread_create(&th,0,f,0); pthread_join(th,0);
pthread_create(0,0,0,0); pthread_cleanup_pop(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 for boost_pthread_flag in '' $boost_pthread_flags; do
boost_pthread_ok=false boost_pthread_ok=false
dnl Re-use the test file already generated. 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 # I'm not sure about my test for `il' (be careful: Intel's ICC pre-defines
# the same defines as GCC's). # the same defines as GCC's).
for i in \ 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_mingw_test(6, 2) \
_BOOST_gcc_test(6, 2) \ _BOOST_gcc_test(6, 2) \
_BOOST_mingw_test(6, 1) \ _BOOST_mingw_test(6, 1) \
_BOOST_gcc_test(6, 1) \ _BOOST_gcc_test(6, 1) \
_BOOST_mingw_test(6, 0) \ _BOOST_mingw_test(6, 0) \
_BOOST_gcc_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_mingw_test(5, 3) \
_BOOST_gcc_test(5, 3) \ _BOOST_gcc_test(5, 3) \
_BOOST_mingw_test(5, 2) \ _BOOST_mingw_test(5, 2) \

View File

@@ -20,22 +20,16 @@
#include <cassert> #include <cassert>
#include <cerrno> #include <cerrno>
#include <cstdio>
#include <cstring> #include <cstring>
#include <boost/date_time/posix_time/posix_time.hpp> #include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/filesystem/operations.hpp> #include <boost/filesystem/operations.hpp>
#include <boost/locale/conversion.hpp> #include <boost/locale/conversion.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include <boost/process/child.hpp>
#include <boost/process/io.hpp>
#include <boost/process/pipe.hpp>
#include <boost/process/search_path.hpp>
#include <boost/property_tree/exceptions.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/ptree_fwd.hpp>
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <mpd/tag.h> #include <mpd/tag.h>
#include <sys/wait.h>
#include "actions.h" #include "actions.h"
#include "charset.h" #include "charset.h"
@@ -133,18 +127,6 @@ size_t HeaderHeight;
size_t FooterHeight; size_t FooterHeight;
size_t FooterStartY; size_t FooterStartY;
void validateScreenSize()
{
using Global::MainHeight;
if (COLS < 30 || MainHeight < 5)
{
NC::destroyScreen();
std::cout << "Screen is too small to handle ncmpcpp correctly\n";
exit(1);
}
}
void initializeScreens() void initializeScreens()
{ {
myHelp = new Help; myHelp = new Help;
@@ -230,8 +212,6 @@ void resizeScreen(bool reload_main_window)
MainHeight = std::max(LINES-(Config.design == Design::Alternative ? 7 : 4), 0); MainHeight = std::max(LINES-(Config.design == Design::Alternative ? 7 : 4), 0);
validateScreenSize();
if (!Config.header_visibility) if (!Config.header_visibility)
MainHeight += 2; MainHeight += 2;
if (!Config.statusbar_visibility) if (!Config.statusbar_visibility)
@@ -1769,33 +1749,46 @@ void JumpToPositionInSong::run()
std::string spos; std::string spos;
{ {
Statusbar::ScopedLock slock; 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(); spos = wFooter->prompt();
} }
boost::regex rx; boost::regex rx;
boost::smatch what; 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<unsigned>(what[1]); auto mins = fromString<unsigned>(what[1]);
auto secs = fromString<unsigned>(what[2]); auto secs = fromString<unsigned>(what[2]);
boundsCheck(secs, 0u, 60u); boundsCheck(secs, 0u, 60u);
Mpd.Seek(s.getPosition(), mins * 60 + secs); 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<unsigned>(what[1]); auto secs = fromString<unsigned>(what[1]);
Mpd.Seek(s.getPosition(), secs); 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<unsigned>(what[1]); auto percent = fromString<unsigned>(what[1]);
boundsCheck(percent, 0u, 100u); boundsCheck(percent, 0u, 100u);
int secs = (percent * s.getDuration()) / 100.0; int secs = (percent * s.getDuration()) / 100.0;
Mpd.Seek(s.getPosition(), secs); 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<unsigned>(what[1]);
auto mins = fromString<unsigned>(what[2]);
auto secs = fromString<unsigned>(what[3]);
boundsCheck(mins, 0u, 60u);
boundsCheck(secs, 0u, 60u);
Mpd.Seek(s.getPosition(), hours * 3600 + mins * 60 + secs);
}
else 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() bool SelectItem::canBeRun()
@@ -2008,7 +2001,10 @@ bool ReversePlaylist::canBeRun()
return false; return false;
m_begin = myPlaylist->main().begin(); m_begin = myPlaylist->main().begin();
m_end = myPlaylist->main().end(); 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() void ReversePlaylist::run()
@@ -2763,13 +2759,11 @@ bool AddYoutubeDLItem::canBeRun()
void AddYoutubeDLItem::run() void AddYoutubeDLItem::run()
{ {
using Global::wFooter; using Global::wFooter;
namespace bp = boost::process;
namespace pt = boost::property_tree;
std::string url; std::string url;
{ {
Statusbar::ScopedLock slock; Statusbar::ScopedLock slock;
Statusbar::put() << "Add via youtube-dl: "; Statusbar::put() << "Add via youtube-dl/yt-dlp: ";
url = wFooter->prompt(); url = wFooter->prompt();
} }
@@ -2777,74 +2771,192 @@ void AddYoutubeDLItem::run()
if (url.empty()) if (url.empty())
return; return;
// search the youtube-dl executable in the PATH // Try to find yt-dlp first, fall back to youtube-dl
auto ydl_path = bp::search_path("youtube-dl"); std::string ydl_executable;
if (ydl_path.empty()) { {
Statusbar::print("youtube-dl was not found in PATH"); FILE *which_pipe = popen("which yt-dlp 2>/dev/null", "r");
if (which_pipe)
{
char path[256];
if (fgets(path, sizeof(path), which_pipe) != nullptr)
{
// Remove trailing newline
size_t len = strlen(path);
if (len > 0 && path[len-1] == '\n')
path[len-1] = '\0';
ydl_executable = path;
}
pclose(which_pipe);
}
}
// Fall back to youtube-dl if yt-dlp not found
if (ydl_executable.empty())
{
FILE *which_pipe = popen("which youtube-dl 2>/dev/null", "r");
if (which_pipe)
{
char path[256];
if (fgets(path, sizeof(path), which_pipe) != nullptr)
{
size_t len = strlen(path);
if (len > 0 && path[len-1] == '\n')
path[len-1] = '\0';
ydl_executable = path;
}
pclose(which_pipe);
}
}
if (ydl_executable.empty())
{
Statusbar::print("Neither yt-dlp nor youtube-dl was found in PATH");
return; return;
} }
Statusbar::printf("Calling youtube-dl with '%1%' ...", url); Statusbar::printf("Fetching from '%s'...", url.c_str());
// start youtube-dl in a child process // Build command with proper shell escaping
// -j: output as JSON, each playlist item on a separate line std::string escaped_url = url;
// -f bestaudio/best: selects the best available audio-only stream, or // Simple shell escape - replace single quotes with '\''
// alternatively the best audio+video stream size_t pos = 0;
bp::ipstream output; while ((pos = escaped_url.find("'", pos)) != std::string::npos)
bp::child child_process(ydl_path, url, "-j", "-f", "bestaudio/best", "--playlist-end", "100, bp::std_out > output, {
bp::std_err > bp::null); escaped_url.replace(pos, 1, "'\\''");
pos += 4;
}
// extract the URL and metadata from a ptree object and add // Use --flat-playlist to get all items, then process each one
auto add_song = [] (const pt::ptree& ptree) { // First get list of video IDs/URLs from playlist
auto download_url = ptree.get<std::string>("url"); std::string command = ydl_executable + " --flat-playlist --get-id " +
auto title = ptree.get_optional<std::string>("title"); "--playlist-end 100 " +
auto artist = ptree.get_optional<std::string>("creator"); "'" + escaped_url + "' 2>/dev/null";
if (!artist.has_value()) {
artist = ptree.get_optional<std::string>("uploader"); FILE *pipe = popen(command.c_str(), "r");
} if (!pipe)
auto album = ptree.get_optional<std::string>("album"); {
auto id = Mpd.AddSong(download_url); Statusbar::print("Failed to execute downloader");
if (id == -1) { return;
return 0; }
}
if (title.has_value()) { std::vector<std::string> video_ids;
Mpd.AddTag(id, MPD_TAG_TITLE, *title); char line[4096];
}
if (artist.has_value()) { // Collect all video IDs
Mpd.AddTag(id, MPD_TAG_ARTIST, *artist); while (fgets(line, sizeof(line), pipe) != nullptr)
} {
if (album.has_value()) { size_t len = strlen(line);
Mpd.AddTag(id, MPD_TAG_ALBUM, *album); while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
} {
return 1; line[--len] = '\0';
}; }
if (len > 0)
video_ids.push_back(line);
}
pclose(pipe);
if (video_ids.empty())
{
Statusbar::print("No items found in playlist");
return;
}
Statusbar::printf("Found %u items, fetching URLs...",
static_cast<unsigned>(video_ids.size()));
std::string line;
pt::ptree ptree;
unsigned num_songs_added = 0; unsigned num_songs_added = 0;
while (std::getline(output, line)) { // Now process each video to get its title and stream URL
try { for (const auto &video_id : video_ids)
std::istringstream line_stream(line); {
pt::read_json(line_stream, ptree); // Build URL for this specific video
num_songs_added += add_song(ptree); std::string video_url;
} catch (pt::ptree_error &e) { if (video_id.find("http") == 0)
Statusbar::print("An error occurred while parsing the output of youtube-dl"); {
continue; video_url = video_id;
}
else
{
// Assume it's a YouTube ID
video_url = "https://www.youtube.com/watch?v=" + video_id;
}
// Escape the video URL
std::string escaped_video_url = video_url;
pos = 0;
while ((pos = escaped_video_url.find("'", pos)) != std::string::npos)
{
escaped_video_url.replace(pos, 1, "'\\''");
pos += 4;
}
// Get title and URL for this specific video
std::string video_command = ydl_executable + " --get-title --get-url " +
"-f bestaudio/best " +
"'" + escaped_video_url + "' 2>/dev/null";
FILE *video_pipe = popen(video_command.c_str(), "r");
if (!video_pipe)
continue;
std::string title;
std::string stream_url;
int line_num = 0;
while (fgets(line, sizeof(line), video_pipe) != nullptr)
{
size_t len = strlen(line);
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r'))
{
line[--len] = '\0';
}
if (len == 0)
continue;
if (line_num == 0)
title = line;
else if (line_num == 1)
stream_url = line;
++line_num;
}
pclose(video_pipe);
// Add to MPD if we got both title and URL
if (!stream_url.empty() &&
(stream_url.find("http://") == 0 || stream_url.find("https://") == 0))
{
int id = Mpd.AddSong(stream_url);
if (id != -1)
{
if (!title.empty())
Mpd.AddTag(id, MPD_TAG_TITLE, title);
++num_songs_added;
std::string display = title.empty() ? video_id : title;
if (display.length() > 50)
display = display.substr(0, 47) + "...";
Statusbar::printf("Added: %s (%u/%u)",
display.c_str(),
num_songs_added,
static_cast<unsigned>(video_ids.size()));
}
} }
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
} }
if (child_process.running()) { if (num_songs_added > 0)
child_process.terminate(); {
Statusbar::printf("Successfully added %u item(s) to playlist", num_songs_added);
} }
child_process.wait(); else
{
auto ec = child_process.exit_code(); Statusbar::print("Failed to add any items");
if (ec == 0) {
Statusbar::printf("Added %1% item(s) to playlist", num_songs_added);
} else {
Statusbar::printf("Added %1% item(s) to playlist (youtube-dl exited with exit code %2%)", num_songs_added, ec);
} }
} }

View File

@@ -156,14 +156,10 @@ bool configure(int argc, char **argv)
if (vm.count("test-lyrics-fetchers")) if (vm.count("test-lyrics-fetchers"))
{ {
std::vector<std::tuple<std::string, std::string, std::string>> fetcher_data = { std::vector<std::tuple<std::string, std::string, std::string>> fetcher_data = {
std::make_tuple("azlyrics", "rihanna", "umbrella"),
std::make_tuple("genius", "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("justsomelyrics", "rihanna", "umbrella"),
std::make_tuple("jahlyrics", "sean kingston", "dry your eyes"), 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("tekstowo", "rihanna", "umbrella"),
std::make_tuple("zeneszoveg", "rihanna", "umbrella"), std::make_tuple("zeneszoveg", "rihanna", "umbrella"),
}; };
@@ -175,7 +171,7 @@ bool configure(int argc, char **argv)
<< fetcher->name() << fetcher->name()
<< " : " << " : "
<< std::flush; << 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") std::cout << (result.first ? "ok" : "failed")
<< "\n"; << "\n";
} }

View File

@@ -41,7 +41,6 @@ CURLcode Curl::perform(std::string &data, const std::string &URL, const std::str
curl_easy_setopt(c, CURLOPT_WRITEDATA, &data); curl_easy_setopt(c, CURLOPT_WRITEDATA, &data);
curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, timeout); curl_easy_setopt(c, CURLOPT_CONNECTTIMEOUT, timeout);
curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1); curl_easy_setopt(c, CURLOPT_NOSIGNAL, 1);
curl_easy_setopt(c, CURLOPT_USERAGENT, "ncmpcpp " VERSION);
if (follow_redirect) if (follow_redirect)
curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1L);
if (!referer.empty()) if (!referer.empty())

View File

@@ -30,7 +30,8 @@ void verifyFormats(const NC::FormattedColor::Formats &formats)
if (fmt == NC::Format::NoBold if (fmt == NC::Format::NoBold
|| fmt == NC::Format::NoUnderline || fmt == NC::Format::NoUnderline
|| fmt == NC::Format::NoReverse || fmt == NC::Format::NoReverse
|| fmt == NC::Format::NoAltCharset) || fmt == NC::Format::NoAltCharset
|| fmt == NC::Format::NoItalic)
throw std::logic_error("FormattedColor can't hold disabling formats"); throw std::logic_error("FormattedColor can't hold disabling formats");
} }
} }
@@ -71,6 +72,9 @@ std::istream &NC::operator>>(std::istream &is, NC::FormattedColor &fc)
case 'a': case 'a':
formats.push_back(NC::Format::AltCharset); formats.push_back(NC::Format::AltCharset);
break; break;
case 'i':
formats.push_back(NC::Format::Italic);
break;
default: default:
is.setstate(std::ios::failbit); is.setstate(std::ios::failbit);
break; break;

View File

@@ -24,6 +24,7 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <sys/select.h> #include <sys/select.h>
#include <termios.h>
#include <unistd.h> #include <unistd.h>
#include "utility/readline.h" #include "utility/readline.h"
@@ -198,6 +199,8 @@ int add_base()
int color_pair_counter; int color_pair_counter;
std::vector<int> color_pair_map; std::vector<int> color_pair_map;
termios orig_termios;
} }
namespace NC { namespace NC {
@@ -346,6 +349,10 @@ NC::Format reverseFormat(NC::Format fmt)
return NC::Format::NoAltCharset; return NC::Format::NoAltCharset;
case NC::Format::NoAltCharset: case NC::Format::NoAltCharset:
return NC::Format::AltCharset; return NC::Format::AltCharset;
case NC::Format::Italic:
return NC::Format::NoItalic;
case NC::Format::NoItalic:
return NC::Format::Italic;
} }
// Unreachable, silence GCC. // Unreachable, silence GCC.
return fmt; return fmt;
@@ -396,6 +403,7 @@ int colorCount()
void initScreen(bool enable_colors, bool enable_mouse) void initScreen(bool enable_colors, bool enable_mouse)
{ {
tcgetattr(STDIN_FILENO, &orig_termios);
initscr(); initscr();
if (has_colors() && enable_colors) if (has_colors() && enable_colors)
{ {
@@ -477,6 +485,7 @@ void destroyScreen()
Mouse::disable(); Mouse::disable();
curs_set(1); curs_set(1);
endwin(); endwin();
tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios);
} }
Window::Window(size_t startx, size_t starty, size_t width, size_t height, Window::Window(size_t startx, size_t starty, size_t width, size_t height,
@@ -494,14 +503,9 @@ Window::Window(size_t startx, size_t starty, size_t width, size_t height,
m_bold_counter(0), m_bold_counter(0),
m_underline_counter(0), m_underline_counter(0),
m_reverse_counter(0), m_reverse_counter(0),
m_alt_charset_counter(0) 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) if (m_border)
{ {
++m_start_x; ++m_start_x;
@@ -542,6 +546,7 @@ Window::Window(const Window &rhs)
, m_underline_counter(rhs.m_underline_counter) , m_underline_counter(rhs.m_underline_counter)
, m_reverse_counter(rhs.m_reverse_counter) , m_reverse_counter(rhs.m_reverse_counter)
, m_alt_charset_counter(rhs.m_alt_charset_counter) , m_alt_charset_counter(rhs.m_alt_charset_counter)
, m_italic_counter(rhs.m_italic_counter)
{ {
setColor(m_color); setColor(m_color);
} }
@@ -566,6 +571,7 @@ Window::Window(Window &&rhs)
, m_underline_counter(rhs.m_underline_counter) , m_underline_counter(rhs.m_underline_counter)
, m_reverse_counter(rhs.m_reverse_counter) , m_reverse_counter(rhs.m_reverse_counter)
, m_alt_charset_counter(rhs.m_alt_charset_counter) , m_alt_charset_counter(rhs.m_alt_charset_counter)
, m_italic_counter(rhs.m_italic_counter)
{ {
rhs.m_window = nullptr; rhs.m_window = nullptr;
} }
@@ -591,6 +597,7 @@ Window &Window::operator=(Window rhs)
std::swap(m_underline_counter, rhs.m_underline_counter); std::swap(m_underline_counter, rhs.m_underline_counter);
std::swap(m_reverse_counter, rhs.m_reverse_counter); std::swap(m_reverse_counter, rhs.m_reverse_counter);
std::swap(m_alt_charset_counter, rhs.m_alt_charset_counter); std::swap(m_alt_charset_counter, rhs.m_alt_charset_counter);
std::swap(m_italic_counter, rhs.m_italic_counter);
return *this; return *this;
} }
@@ -682,13 +689,16 @@ void Window::moveTo(size_t new_x, size_t new_y)
void Window::adjustDimensions(size_t width, size_t height) void Window::adjustDimensions(size_t width, size_t height)
{ {
// NOTE: when dimensions get small, integer overflow will cause calls to
// `Menu<T>::refresh()` to run for a very long time.
if (m_border) if (m_border)
{ {
width -= 2; width -= width >= 2 ? 2 : 0;
height -= 2; height -= height >= 2 ? 2 : 0;
} }
if (!m_title.empty()) if (!m_title.empty())
height -= 2; height -= height >= 2 ? 2 : 0;
m_height = height; m_height = height;
m_width = width; m_width = width;
} }
@@ -777,6 +787,11 @@ void Window::altCharset(bool altcharset_state) const
(altcharset_state ? wattron : wattroff)(m_window, A_ALTCHARSET); (altcharset_state ? wattron : wattroff)(m_window, A_ALTCHARSET);
} }
void Window::italic(bool italic_state) const
{
(italic_state ? wattron : wattroff)(m_window, A_ITALIC);
}
void Window::setTimeout(int timeout) void Window::setTimeout(int timeout)
{ {
m_window_timeout = timeout; m_window_timeout = timeout;
@@ -1410,6 +1425,12 @@ Window &Window::operator<<(Format format)
case Format::NoAltCharset: case Format::NoAltCharset:
decrease_flag(*this, m_alt_charset_counter, &Window::altCharset); decrease_flag(*this, m_alt_charset_counter, &Window::altCharset);
break; break;
case Format::Italic:
increase_flag(*this, m_italic_counter, &Window::italic);
break;
case Format::NoItalic:
decrease_flag(*this, m_italic_counter, &Window::italic);
break;
} }
return *this; return *this;
} }

View File

@@ -26,7 +26,6 @@
#include "config.h" #include "config.h"
#include "curses.h" #include "curses.h"
#include "gcc.h"
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <functional> #include <functional>
@@ -200,7 +199,8 @@ enum class Format {
Bold, NoBold, Bold, NoBold,
Underline, NoUnderline, Underline, NoUnderline,
Reverse, NoReverse, Reverse, NoReverse,
AltCharset, NoAltCharset AltCharset, NoAltCharset,
Italic, NoItalic
}; };
NC::Format reverseFormat(NC::Format fmt); NC::Format reverseFormat(NC::Format fmt);
@@ -531,6 +531,11 @@ private:
/// @param altcharset_state state of altcharset attribute /// @param altcharset_state state of altcharset attribute
/// ///
void altCharset(bool altcharset_state) const; void altCharset(bool altcharset_state) const;
/// Sets state of italic attribute (internal use only)
/// @param italic_state state of italic attribute
///
void italic(bool italic_state) const;
/// pointer to helper function used by getString() /// pointer to helper function used by getString()
/// @see getString() /// @see getString()
@@ -562,6 +567,7 @@ private:
int m_underline_counter; int m_underline_counter;
int m_reverse_counter; int m_reverse_counter;
int m_alt_charset_counter; int m_alt_charset_counter;
int m_italic_counter;
}; };
} }

View File

@@ -47,6 +47,8 @@ const wchar_t *toColumnName(char c)
return L"Filename"; return L"Filename";
case 'D': case 'D':
return L"Directory"; return L"Directory";
case 'F':
return L"Filepath";
case 'a': case 'a':
return L"Artist"; return L"Artist";
case 'A': case 'A':
@@ -92,9 +94,9 @@ void setProperties(NC::Menu<T> &menu, const MPD::Song &s, const SongList &list,
{ {
// Draw a separator when the next album is different than the current // Draw a separator when the next album is different than the current
// one. In case there are two albums with the same name, but a different // one. In case there are two albums with the same name, but a different
// artist, compare also artists. // album artist, compare also album artists.
separate_albums = next->song()->getAlbum() != s.getAlbum() separate_albums = next->song()->getAlbum() != s.getAlbum()
|| next->song()->getArtist() != s.getArtist(); || next->song()->getAlbumArtist() != s.getAlbumArtist();
} }
} }
} }

View File

@@ -192,6 +192,8 @@ expressions<CharT> parseBracket(const string<CharT> &s,
result.push_back(NC::Format::AltCharset); result.push_back(NC::Format::AltCharset);
else if (flags & Format::Flags::Format && *it == 'r') else if (flags & Format::Flags::Format && *it == 'r')
result.push_back(NC::Format::Reverse); result.push_back(NC::Format::Reverse);
else if (flags & Format::Flags::Format && *it == 'i')
result.push_back(NC::Format::Italic);
else if (flags & Format::Flags::Format && *it == '/') else if (flags & Format::Flags::Format && *it == '/')
{ {
++it; ++it;
@@ -204,6 +206,8 @@ expressions<CharT> parseBracket(const string<CharT> &s,
result.push_back(NC::Format::NoAltCharset); result.push_back(NC::Format::NoAltCharset);
else if (*it == 'r') else if (*it == 'r')
result.push_back(NC::Format::NoReverse); result.push_back(NC::Format::NoReverse);
else if (*it == 'i')
result.push_back(NC::Format::NoItalic);
else else
throwError(s, it, invalidCharacter(*it)); throwError(s, it, invalidCharacter(*it));
} }

View File

@@ -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

View File

@@ -46,8 +46,8 @@ SongIterator makeSongIterator(IteratorT it)
> Extractor; > Extractor;
static_assert( static_assert(
std::is_convertible< std::is_convertible<
typename std::result_of<Extractor(typename IteratorT::reference)>::type, std::invoke_result_t<Extractor, typename IteratorT::reference>,
SongProperties & SongProperties &
>::value, "invalid result type of SongPropertiesExtractor"); >::value, "invalid result type of SongPropertiesExtractor");
return SongIterator(boost::make_transform_iterator(it, Extractor{})); return SongIterator(boost::make_transform_iterator(it, Extractor{}));
} }
@@ -60,8 +60,8 @@ ConstSongIterator makeConstSongIterator(ConstIteratorT it)
> Extractor; > Extractor;
static_assert( static_assert(
std::is_convertible< std::is_convertible<
typename std::result_of<Extractor(typename ConstIteratorT::reference)>::type, std::invoke_result_t<Extractor, typename ConstIteratorT::reference>,
const SongProperties & const SongProperties &
>::value, "invalid result type of SongPropertiesExtractor"); >::value, "invalid result type of SongPropertiesExtractor");
return ConstSongIterator(boost::make_transform_iterator(it, Extractor{})); return ConstSongIterator(boost::make_transform_iterator(it, Extractor{}));
} }

View File

@@ -25,7 +25,6 @@
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#include <string> #include <string>
#include "enums.h" #include "enums.h"
#include "gcc.h"
#include "screens/screen.h" #include "screens/screen.h"
#include "song.h" #include "song.h"

View File

@@ -19,7 +19,6 @@
***************************************************************************/ ***************************************************************************/
#include "config.h" #include "config.h"
#include "curl_handle.h"
#include <cstdlib> #include <cstdlib>
#include <cstring> #include <cstring>
@@ -29,8 +28,15 @@
#include <boost/algorithm/string/trim.hpp> #include <boost/algorithm/string/trim.hpp>
#include <boost/regex.hpp> #include <boost/regex.hpp>
#ifdef HAVE_TAGLIB_H
#include <fileref.h>
#include <tpropertymap.h>
#endif // HAVE_TAGLIB_H
#include "charset.h" #include "charset.h"
#include "curl_handle.h"
#include "lyrics_fetcher.h" #include "lyrics_fetcher.h"
#include "settings.h"
#include "utility/html.h" #include "utility/html.h"
#include "utility/string.h" #include "utility/string.h"
@@ -38,16 +44,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
{ {
std::string s; std::string s;
is >> s; is >> s;
if (s == "azlyrics") if (s == "genius")
fetcher = std::make_unique<AzLyricsFetcher>();
else if (s == "genius")
fetcher = std::make_unique<GeniusFetcher>(); fetcher = std::make_unique<GeniusFetcher>();
else if (s == "musixmatch")
fetcher = std::make_unique<MusixmatchFetcher>();
else if (s == "sing365")
fetcher = std::make_unique<Sing365Fetcher>();
else if (s == "metrolyrics")
fetcher = std::make_unique<MetrolyricsFetcher>();
else if (s == "justsomelyrics") else if (s == "justsomelyrics")
fetcher = std::make_unique<JustSomeLyricsFetcher>(); fetcher = std::make_unique<JustSomeLyricsFetcher>();
else if (s == "jahlyrics") else if (s == "jahlyrics")
@@ -60,6 +58,10 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
fetcher = std::make_unique<ZeneszovegFetcher>(); fetcher = std::make_unique<ZeneszovegFetcher>();
else if (s == "internet") else if (s == "internet")
fetcher = std::make_unique<InternetLyricsFetcher>(); fetcher = std::make_unique<InternetLyricsFetcher>();
#ifdef HAVE_TAGLIB_H
else if (s == "tags")
fetcher = std::make_unique<TagsLyricsFetcher>();
#endif // HAVE_TAGLIB_H
else else
is.setstate(std::ios::failbit); is.setstate(std::ios::failbit);
return is; return is;
@@ -68,7 +70,8 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher)
const char LyricsFetcher::msgNotFound[] = "Not found"; const char LyricsFetcher::msgNotFound[] = "Not found";
LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist, 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 result;
result.first = false; result.first = false;
@@ -76,7 +79,7 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist,
std::string url = urlTemplate(); std::string url = urlTemplate();
boost::replace_all(url, "%artist%", Curl::escape(artist)); boost::replace_all(url, "%artist%", Curl::escape(artist));
boost::replace_all(url, "%title%", Curl::escape(title)); boost::replace_all(url, "%title%", Curl::escape(title));
std::string data; std::string data;
CURLcode code = Curl::perform(data, url, "", true); CURLcode code = Curl::perform(data, url, "", true);
@@ -88,9 +91,11 @@ LyricsFetcher::Result LyricsFetcher::fetch(const std::string &artist,
auto lyrics = getContent(regex(), data); auto lyrics = getContent(regex(), data);
//std::cerr << "URL: " << url << "\n";
//std::cerr << "Data: " << data << "\n";
if (lyrics.empty() || notLyrics(data)) if (lyrics.empty() || notLyrics(data))
{ {
//std::cerr << "Data: " << data << "\n";
//std::cerr << "Empty: " << lyrics.empty() << "\n"; //std::cerr << "Empty: " << lyrics.empty() << "\n";
//std::cerr << "Not Lyrics: " << notLyrics(data) << "\n"; //std::cerr << "Not Lyrics: " << notLyrics(data) << "\n";
result.second = msgNotFound; result.second = msgNotFound;
@@ -137,12 +142,14 @@ void LyricsFetcher::postProcess(std::string &data) const
stripHtmlTags(data); stripHtmlTags(data);
// Remove indentation from each line and collapse multiple newlines into one. // Remove indentation from each line and collapse multiple newlines into one.
std::vector<std::string> lines; std::vector<std::string> lines;
boost::split(lines, data, boost::is_any_of("\n")); boost::split(lines, data, boost::is_any_of("\r\n"));
for (auto &line : lines) for (auto &line : lines)
boost::trim(line); boost::trim(line);
std::unique(lines.begin(), lines.end(), [](std::string &a, std::string &b) { auto last = std::unique(
return a.empty() && b.empty(); 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"); data = boost::algorithm::join(lines, "\n");
boost::trim(data); boost::trim(data);
} }
@@ -150,7 +157,8 @@ void LyricsFetcher::postProcess(std::string &data) const
/**********************************************************************/ /**********************************************************************/
LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist, LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
const std::string &title) const std::string &title,
const MPD::Song &song)
{ {
Result result; Result result;
result.first = false; result.first = false;
@@ -192,7 +200,7 @@ LyricsFetcher::Result GoogleLyricsFetcher::fetch(const std::string &artist,
data = unescapeHtmlUtf8(urls[0]); data = unescapeHtmlUtf8(urls[0]);
URL = data.c_str(); URL = data.c_str();
return LyricsFetcher::fetch("", ""); return LyricsFetcher::fetch("", "", song);
} }
bool GoogleLyricsFetcher::isURLOk(const std::string &url) 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, 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; LyricsFetcher::Result result;
result.first = false; result.first = false;
result.second = "The following site may contain lyrics for this song: "; result.second = "The following site may contain lyrics for this song: ";
@@ -226,3 +227,42 @@ bool InternetLyricsFetcher::isURLOk(const std::string &url)
URL = url; URL = url;
return false; 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

View File

@@ -26,6 +26,8 @@
#include <memory> #include <memory>
#include <string> #include <string>
#include "song.h"
struct LyricsFetcher struct LyricsFetcher
{ {
typedef std::pair<bool, std::string> Result; typedef std::pair<bool, std::string> Result;
@@ -33,7 +35,7 @@ struct LyricsFetcher
virtual ~LyricsFetcher() { } virtual ~LyricsFetcher() { }
virtual const char *name() const = 0; 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: protected:
virtual const char *urlTemplate() const = 0; virtual const char *urlTemplate() const = 0;
@@ -57,7 +59,7 @@ std::istream &operator>>(std::istream &is, LyricsFetcher_ &fetcher);
struct GoogleLyricsFetcher : public LyricsFetcher 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: protected:
virtual const char *urlTemplate() const { return URL; } virtual const char *urlTemplate() const { return URL; }
@@ -69,34 +71,6 @@ private:
const char *URL; const char *URL;
}; };
struct MusixmatchFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "musixmatch.com"; }
protected:
virtual const char *regex() const override { return "<span class=\"lyrics__content__.*?>(.*?)</span>"; }
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 "<div class=\"lyrics-body\">(.*?)<!--WIDGET.*?<!-- Second Section -->(.*?)<!--WIDGET.*?<!-- Third Section -->(.*?)</div>"; }
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 "<div class=\"lyrics\">(.*?)</div>"; }
};
struct JustSomeLyricsFetcher : public GoogleLyricsFetcher struct JustSomeLyricsFetcher : public GoogleLyricsFetcher
{ {
virtual const char *name() const override { return "justsomelyrics.com"; } virtual const char *name() const override { return "justsomelyrics.com"; }
@@ -105,20 +79,12 @@ protected:
virtual const char *regex() const override { return "<div class=\"content.*?</div>(.*?)See also"; } virtual const char *regex() const override { return "<div class=\"content.*?</div>(.*?)See also"; }
}; };
struct AzLyricsFetcher : public GoogleLyricsFetcher
{
virtual const char *name() const override { return "azlyrics.com"; }
protected:
virtual const char *regex() const override { return "<div class=\"lyricsh\">.*?</h2>.*<div>(.*?)</div>"; }
};
struct GeniusFetcher : public GoogleLyricsFetcher struct GeniusFetcher : public GoogleLyricsFetcher
{ {
virtual const char *name() const override { return "genius.com"; } virtual const char *name() const override { return "genius.com"; }
protected: protected:
virtual const char *regex() const override { return "<div class=\"[Ll]yrics.*?>(.*?)</div>"; } virtual const char *regex() const override { return "<div data-lyrics-container.*?>(.*?)</div>"; }
}; };
struct JahLyricsFetcher : public GoogleLyricsFetcher struct JahLyricsFetcher : public GoogleLyricsFetcher
@@ -142,7 +108,7 @@ struct TekstowoFetcher : public GoogleLyricsFetcher
virtual const char *name() const override { return "tekstowo.pl"; } virtual const char *name() const override { return "tekstowo.pl"; }
protected: protected:
virtual const char *regex() const override { return "<div class=\"song-text\".*?>.*?</h2>(.*?)<a"; } virtual const char *regex() const override { return "<div class=\"inner-text\">(.*?)</div>"; }
}; };
struct ZeneszovegFetcher : public GoogleLyricsFetcher struct ZeneszovegFetcher : public GoogleLyricsFetcher
@@ -150,13 +116,13 @@ struct ZeneszovegFetcher : public GoogleLyricsFetcher
virtual const char *name() const override { return "zeneszoveg.hu"; } virtual const char *name() const override { return "zeneszoveg.hu"; }
protected: protected:
virtual const char *regex() const override { return "<div class=\"lyrics-plain-text.*?\">(.*?)</div>"; } virtual const char *regex() const override { return "<div class=\"lyrics-plain-text trans_original\">(.*?)</div>"; }
}; };
struct InternetLyricsFetcher : public GoogleLyricsFetcher struct InternetLyricsFetcher : public GoogleLyricsFetcher
{ {
virtual const char *name() const override { return "the Internet"; } 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: protected:
virtual const char *siteKeyword() const override { return nullptr; } virtual const char *siteKeyword() const override { return nullptr; }
@@ -168,4 +134,16 @@ private:
std::string URL; 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 #endif // NCMPCPP_LYRICS_FETCHER_H

View File

@@ -147,7 +147,7 @@ unsigned Connection::Version() const
void Connection::SetHostname(const std::string &host) void Connection::SetHostname(const std::string &host)
{ {
size_t at = host.find("@"); size_t at = host.find("@");
if (at != std::string::npos) if (at != 0 && at != std::string::npos)
{ {
m_password = host.substr(0, at); m_password = host.substr(0, at);
m_host = host.substr(at+1); m_host = host.substr(at+1);

View File

@@ -353,8 +353,14 @@ private:
}; };
template <typename ObjectT> template <typename ObjectT>
struct Iterator: std::iterator<std::input_iterator_tag, ObjectT> 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 // shared state of the iterator
struct State struct State
{ {

View File

@@ -121,7 +121,6 @@ int main(int argc, char **argv)
Config.statusbar_visibility = 0; Config.statusbar_visibility = 0;
Actions::setWindowsDimensions(); Actions::setWindowsDimensions();
Actions::validateScreenSize();
Actions::initializeScreens(); Actions::initializeScreens();
wHeader = new NC::Window(0, 0, COLS, Actions::HeaderHeight, "", Config.header_color, NC::Border()); wHeader = new NC::Window(0, 0, COLS, Actions::HeaderHeight, "", Config.header_color, NC::Border());
@@ -220,7 +219,10 @@ int main(int argc, char **argv)
try try
{ {
auto k = Bindings.get(input); 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) catch (ConversionError &e)
{ {

View File

@@ -151,7 +151,7 @@ boost::optional<std::string> downloadLyrics(
<< NC::Format::NoBold << "... "; << 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 (result_.first == false)
{ {
if (shared_buffer) if (shared_buffer)
@@ -361,6 +361,25 @@ void Lyrics::toggleFetcher()
Statusbar::print("Using all lyrics fetchers"); 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_) void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_)
{ {
auto consumer_impl = [this] { auto consumer_impl = [this] {
@@ -377,7 +396,10 @@ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_)
break; break;
} }
lyrics_file = lyricsFilename(consumer->songs.front().song()); 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(); cs = consumer->songs.front();
if (cs.notify()) if (cs.notify())
@@ -389,7 +411,7 @@ void Lyrics::fetchInBackground(const MPD::Song &s, bool notify_)
} }
consumer->songs.pop(); consumer->songs.pop();
} }
if (!cs.song().empty()) if (!cs.song().empty() && !cs.song().isStream())
{ {
auto lyrics = downloadLyrics(cs.song(), nullptr, nullptr, m_fetcher); auto lyrics = downloadLyrics(cs.song(), nullptr, nullptr, m_fetcher);
if (lyrics) if (lyrics)

View File

@@ -98,7 +98,7 @@ bool MoveToAlbum(NC::Menu<AlbumEntry> &albums, const std::string &primary_tag, c
struct SortSongs { struct SortSongs {
typedef NC::Menu<MPD::Song>::Item SongItem; typedef NC::Menu<MPD::Song>::Item SongItem;
static const std::array<MPD::Song::GetFunction, 3> GetFuns; static const std::array<MPD::Song::GetFunction, 4> GetFuns;
LocaleStringComparison m_cmp; LocaleStringComparison m_cmp;
@@ -117,26 +117,16 @@ public:
return ret < 0; return ret < 0;
} }
// Sort by track numbers.
try {
ret = boost::lexical_cast<int>(a.getTags(&MPD::Song::getTrackNumber))
- boost::lexical_cast<int>(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<char>(Config.song_library_format, &a) return Format::stringify<char>(Config.song_library_format, &a)
< Format::stringify<char>(Config.song_library_format, &b); < Format::stringify<char>(Config.song_library_format, &b);
} }
}; };
const std::array<MPD::Song::GetFunction, 3> SortSongs::GetFuns = {{ const std::array<MPD::Song::GetFunction, 4> SortSongs::GetFuns = {{
&MPD::Song::getDate, &MPD::Song::getDate,
&MPD::Song::getAlbum, &MPD::Song::getAlbum,
&MPD::Song::getDisc &MPD::Song::getDisc,
&MPD::Song::getTrackNumber,
}}; }};
class SortAlbumEntries { class SortAlbumEntries {
@@ -191,11 +181,16 @@ MediaLibrary::MediaLibrary()
{ {
hasTwoColumns = 0; hasTwoColumns = 0;
isAlbumOnly = 0; isAlbumOnly = 0;
itsLeftColWidth = COLS/3-1;
itsMiddleColWidth = COLS/3; size_t ra = Config.media_library_column_width_ratio_three[0];
size_t rb = Config.media_library_column_width_ratio_three[1];
size_t rc = Config.media_library_column_width_ratio_three[2];
itsLeftColWidth = COLS*ra/(ra+rb+rc)-1;
itsMiddleColStartX = itsLeftColWidth+1; itsMiddleColStartX = itsLeftColWidth+1;
itsRightColWidth = COLS-COLS/3*2-1; itsMiddleColWidth = COLS*rb/(ra+rb+rc);
itsRightColStartX = itsLeftColWidth+itsMiddleColWidth+2; itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1;
itsRightColWidth = COLS-itsLeftColWidth-itsMiddleColWidth-2;
Tags = NC::Menu<PrimaryTag>(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::Border()); Tags = NC::Menu<PrimaryTag>(0, MainStartY, itsLeftColWidth, MainHeight, Config.titles_visibility ? tagTypeToString(Config.media_lib_primary_tag) + "s" : "", Config.main_color, NC::Border());
setHighlightFixes(Tags); setHighlightFixes(Tags);
@@ -240,18 +235,25 @@ void MediaLibrary::resize()
getWindowResizeParams(x_offset, width); getWindowResizeParams(x_offset, width);
if (!hasTwoColumns) if (!hasTwoColumns)
{ {
size_t ra = Config.media_library_column_width_ratio_three[0];
size_t rb = Config.media_library_column_width_ratio_three[1];
size_t rc = Config.media_library_column_width_ratio_three[2];
itsLeftColStartX = x_offset; itsLeftColStartX = x_offset;
itsLeftColWidth = width/3-1; itsLeftColWidth = width*ra/(ra+rb+rc)-1;
itsMiddleColStartX = itsLeftColStartX+itsLeftColWidth+1; itsMiddleColStartX = itsLeftColStartX+itsLeftColWidth+1;
itsMiddleColWidth = width/3; itsMiddleColWidth = width*rb/(ra+rb+rc);
itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1; itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1;
itsRightColWidth = width-width/3*2-1; itsRightColWidth = width-itsLeftColWidth-itsMiddleColWidth-2;
} }
else else
{ {
size_t ra = Config.media_library_column_width_ratio_two[0];
size_t rb = Config.media_library_column_width_ratio_two[1];
itsMiddleColStartX = x_offset; itsMiddleColStartX = x_offset;
itsMiddleColWidth = width/2; itsMiddleColWidth = width*ra/(ra+rb);
itsRightColStartX = x_offset+itsMiddleColWidth+1; itsRightColStartX = itsMiddleColStartX+itsMiddleColWidth+1;
itsRightColWidth = width-itsMiddleColWidth-1; itsRightColWidth = width-itsMiddleColWidth-1;
} }
@@ -334,9 +336,9 @@ void MediaLibrary::update()
for (const auto &album : albums) for (const auto &album : albums)
{ {
auto entry = AlbumEntry( auto entry = AlbumEntry(
Album(std::move(std::get<0>(album.first)), Album(std::get<0>(album.first),
std::move(std::get<1>(album.first)), std::get<1>(album.first),
std::move(std::get<2>(album.first)), std::get<2>(album.first),
album.second)); album.second));
if (idx < Albums.size()) if (idx < Albums.size())
Albums[idx].value() = std::move(entry); Albums[idx].value() = std::move(entry);
@@ -433,8 +435,8 @@ void MediaLibrary::update()
{ {
auto entry = AlbumEntry( auto entry = AlbumEntry(
Album(primary_tag, Album(primary_tag,
std::move(std::get<0>(album.first)), std::get<0>(album.first),
std::move(std::get<1>(album.first)), std::get<1>(album.first),
album.second)); album.second));
if (idx < Albums.size()) if (idx < Albums.size())
{ {

View File

@@ -66,10 +66,14 @@ PlaylistEditor::PlaylistEditor()
, m_window_timeout(Config.data_fetching_delay ? 250 : BaseScreen::defaultWindowTimeout) , m_window_timeout(Config.data_fetching_delay ? 250 : BaseScreen::defaultWindowTimeout)
, m_fetching_delay(boost::posix_time::milliseconds(Config.data_fetching_delay ? 250 : -1)) , m_fetching_delay(boost::posix_time::milliseconds(Config.data_fetching_delay ? 250 : -1))
{ {
LeftColumnWidth = COLS/3-1; size_t ra = Config.playlist_editor_column_width_ratio[0];
size_t rb = Config.playlist_editor_column_width_ratio[1];
LeftColumnWidth = COLS*ra/(ra+rb)-1;
RightColumnStartX = LeftColumnWidth+1; RightColumnStartX = LeftColumnWidth+1;
RightColumnWidth = COLS-LeftColumnWidth-1; RightColumnWidth = COLS-LeftColumnWidth-1;
Playlists = NC::Menu<MPD::Playlist>(0, MainStartY, LeftColumnWidth, MainHeight, Config.titles_visibility ? "Playlists" : "", Config.main_color, NC::Border()); Playlists = NC::Menu<MPD::Playlist>(0, MainStartY, LeftColumnWidth, MainHeight, Config.titles_visibility ? "Playlists" : "", Config.main_color, NC::Border());
setHighlightFixes(Playlists); setHighlightFixes(Playlists);
Playlists.cyclicScrolling(Config.use_cyclic_scrolling); Playlists.cyclicScrolling(Config.use_cyclic_scrolling);
@@ -108,11 +112,14 @@ void PlaylistEditor::resize()
size_t x_offset, width; size_t x_offset, width;
getWindowResizeParams(x_offset, width); getWindowResizeParams(x_offset, width);
size_t ra = Config.playlist_editor_column_width_ratio[0];
size_t rb = Config.playlist_editor_column_width_ratio[1];
LeftColumnStartX = x_offset; LeftColumnStartX = x_offset;
LeftColumnWidth = width/3-1; LeftColumnWidth = width*ra/(ra+rb)-1;
RightColumnStartX = LeftColumnStartX+LeftColumnWidth+1; RightColumnStartX = LeftColumnStartX+LeftColumnWidth+1;
RightColumnWidth = width-LeftColumnWidth-1; RightColumnWidth = width-LeftColumnWidth-1;
Playlists.resize(LeftColumnWidth, MainHeight); Playlists.resize(LeftColumnWidth, MainHeight);
Content.resize(RightColumnWidth, MainHeight); Content.resize(RightColumnWidth, MainHeight);
@@ -162,10 +169,7 @@ void PlaylistEditor::update()
} }
catch (MPD::ServerError &e) catch (MPD::ServerError &e)
{ {
if (e.code() == MPD_SERVER_ERROR_SYSTEM) // no playlists directory Status::handleServerError(e);
Statusbar::print(e.what());
else
throw;
} }
if (idx < Playlists.size()) if (idx < Playlists.size())
Playlists.resizeList(idx); Playlists.resizeList(idx);

View File

@@ -798,13 +798,16 @@ void TagEditor::runAction()
Statusbar::print("Writing changes..."); Statusbar::print("Writing changes...");
for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it) for (auto it = EditedSongs.begin(); it != EditedSongs.end(); ++it)
{ {
Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName()); if ((*it)->isModified())
if (!Tags::write(**it))
{ {
Statusbar::printf("Error while writing tags to \"%1%\": %2%", Statusbar::printf("Writing tags in \"%1%\"...", (*it)->getName());
(*it)->getName(), strerror(errno)); if (!Tags::write(**it))
success = 0; {
break; Statusbar::printf("Error while writing tags to \"%1%\": %2%",
(*it)->getName(), strerror(errno));
success = 0;
break;
}
} }
} }
if (success) if (success)

View File

@@ -83,7 +83,8 @@ Visualizer::Visualizer()
HZ_MIN(Config.visualizer_spectrum_hz_min), HZ_MIN(Config.visualizer_spectrum_hz_min),
HZ_MAX(Config.visualizer_spectrum_hz_max), HZ_MAX(Config.visualizer_spectrum_hz_max),
GAIN(Config.visualizer_spectrum_gain), GAIN(Config.visualizer_spectrum_gain),
SMOOTH_CHARS(ToWString("▁▂▃▄▅▆▇█")) SMOOTH_CHARS(ToWString("▁▂▃▄▅▆▇█")),
SMOOTH_CHARS_FLIPPED(ToWString("▔🮂🮃🮄🬎🮅🮆█")) // https://unicode.org/charts/PDF/U1FB00.pdf
#endif #endif
{ {
InitDataSource(); InitDataSource();
@@ -95,7 +96,7 @@ Visualizer::Visualizer()
memset(m_fftw_input, 0, sizeof(double)*DFT_TOTAL_SIZE); memset(m_fftw_input, 0, sizeof(double)*DFT_TOTAL_SIZE);
m_fftw_output = static_cast<fftw_complex *>(fftw_malloc(sizeof(fftw_complex)*m_fftw_results)); m_fftw_output = static_cast<fftw_complex *>(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_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); m_bar_heights.reserve(100);
# endif // HAVE_FFTW3_H # endif // HAVE_FFTW3_H
} }
@@ -107,7 +108,7 @@ void Visualizer::switchTo()
m_reset_output = true; m_reset_output = true;
drawHeader(); drawHeader();
# ifdef HAVE_FFTW3_H # ifdef HAVE_FFTW3_H
GenLogspace(); GenFreqSpace();
m_bar_heights.reserve(w.getWidth()); m_bar_heights.reserve(w.getWidth());
# endif // HAVE_FFTW3_H # endif // HAVE_FFTW3_H
} }
@@ -121,7 +122,7 @@ void Visualizer::resize()
hasToBeResized = 0; hasToBeResized = 0;
InitVisualization(); InitVisualization();
# ifdef HAVE_FFTW3_H # ifdef HAVE_FFTW3_H
GenLogspace(); GenFreqSpace();
m_bar_heights.reserve(w.getWidth()); m_bar_heights.reserve(w.getWidth());
# endif // HAVE_FFTW3_H # 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); auto c = toColor(sqrt(x*x + 4*y*y), radius, true);
w << NC::XY(left_half_width + x, top_half_height + y) w << NC::XY(left_half_width + x, top_half_height + y)
<< c << c
<< Config.visualizer_chars[1] << Config.visualizer_chars[0]
<< NC::FormattedColor::End<>(c); << 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(); const size_t win_width = w.getWidth();
size_t cur_bin = 0; 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; ++cur_bin;
for (size_t x = 0; x < win_width; ++x) 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 // accumulate bins
size_t count = 0; size_t count = 0;
// check right bound // 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 // 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]; bar_height += m_freq_magnitudes[cur_bin];
++count; ++count;
@@ -475,8 +476,19 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
// average bins // average bins
bar_height /= count; bar_height /= count;
// log scale bar heights // apply scaling to bar heights
bar_height = (20 * log10(bar_height) + DYNAMIC_RANGE + GAIN) / DYNAMIC_RANGE; 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 // Scale bar height between 0 and height
bar_height = bar_height > 0 ? bar_height * height : 0; bar_height = bar_height > 0 ? bar_height * height : 0;
bar_height = bar_height > height ? height : bar_height; 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; ++h_idx;
} else { } else {
// data point does not exist, need to interpolate // 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) for (size_t j = 0; j < h; ++j)
@@ -518,8 +535,12 @@ void Visualizer::DrawFrequencySpectrum(const int16_t *buf, ssize_t samples, size
} else { } else {
// fractional height // fractional height
if (flipped) { if (flipped) {
ch = SMOOTH_CHARS[size-idx-2]; if (Config.visualizer_spectrum_smooth_look_legacy_chars) {
color = NC::FormattedColor(color.color(), {NC::Format::Reverse}); ch = SMOOTH_CHARS_FLIPPED[idx];
} else {
ch = SMOOTH_CHARS[size-idx-2];
color = NC::FormattedColor(color.color(), {NC::Format::Reverse});
}
} else { } else {
ch = SMOOTH_CHARS[idx]; 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); 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 x_next = m_bar_heights[h_idx].first;
const double h_next = m_bar_heights[h_idx].second; 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; 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) void Visualizer::ApplyWindow(double *output, const int16_t *input, ssize_t samples)
{ {
// Use Blackman window for low sidelobes and fast sidelobe rolloff // 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 win_width = w.getWidth();
const size_t left_bins = (log10(HZ_MIN) - win_width*log10(HZ_MIN)) / (log10(HZ_MIN) - log10(HZ_MAX)); const size_t left_bins = (log10(HZ_MIN) - win_width*log10(HZ_MIN)) / (log10(HZ_MIN) - log10(HZ_MAX));
// Generate logspaced frequencies // Generate logspaced frequencies
m_dft_logspace.resize(win_width); m_dft_freqspace.resize(win_width);
const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_logspace.size() - 1); const double log_scale = log10(HZ_MAX) / (left_bins + m_dft_freqspace.size() - 1);
for (size_t i = left_bins; i < m_dft_logspace.size() + left_bins; ++i) { for (size_t i = left_bins; i < m_dft_freqspace.size() + left_bins; ++i) {
m_dft_logspace[i - left_bins] = pow(10, i * log_scale); 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 #endif // HAVE_FFTW3_H

View File

@@ -76,8 +76,11 @@ private:
void DrawFrequencySpectrumStereo(const int16_t *, const int16_t *, ssize_t, size_t); void DrawFrequencySpectrumStereo(const int16_t *, const int16_t *, ssize_t, size_t);
void ApplyWindow(double *, const int16_t *, ssize_t); void ApplyWindow(double *, const int16_t *, ssize_t);
void GenLogspace(); void GenLogspace();
void GenLinspace();
void GenFreqSpace();
double Bin2Hz(size_t); 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 # endif // HAVE_FFTW3_H
void InitDataSource(); void InitDataSource();
@@ -113,7 +116,8 @@ private:
const double HZ_MAX; const double HZ_MAX;
const double GAIN; const double GAIN;
const std::wstring SMOOTH_CHARS; const std::wstring SMOOTH_CHARS;
std::vector<double> m_dft_logspace; const std::wstring SMOOTH_CHARS_FLIPPED;
std::vector<double> m_dft_freqspace;
std::vector<std::pair<size_t, double>> m_bar_heights; std::vector<std::pair<size_t, double>> m_bar_heights;
std::vector<double> m_freq_magnitudes; std::vector<double> m_freq_magnitudes;

View File

@@ -212,28 +212,6 @@ void deprecated(const char *option, const char *version_removal,
bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors) bool Configuration::read(const std::vector<std::string> &config_paths, bool ignore_errors)
{ {
option_parser p; option_parser p;
// Deprecated options.
p.add("visualizer_fifo_path", &visualizer_fifo_path, "", [](std::string v) {
if (!v.empty())
{
deprecated("visualizer_fifo_path",
"0.10",
"replaced by visualizer_data_source");
}
return adjust_path(v);
});
p.add<void>("visualizer_sync_interval", nullptr, "", [](std::string v) {
if (!v.empty())
{
deprecated("visualizer_sync_interval",
"0.10",
"set 'buffer_time' parameter of your MPD audio output to '100000' "
"(100ms) or lower if you experience synchronization issues "
"between audio and visualization");
}
});
// keep the same order of variables as in configuration file // keep the same order of variables as in configuration file
p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.config/ncmpcpp/", adjust_directory); p.add("ncmpcpp_directory", &ncmpcpp_directory, "~/.config/ncmpcpp/", adjust_directory);
p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory); p.add("lyrics_directory", &lyrics_directory, "~/.lyrics/", adjust_directory);
@@ -270,11 +248,12 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("visualizer_fps", &visualizer_fps, p.add("visualizer_fps", &visualizer_fps,
"60", [](std::string v) { "60", [](std::string v) {
uint32_t result = verbose_lexical_cast<uint32_t>(v); uint32_t result = verbose_lexical_cast<uint32_t>(v);
boundsCheck<uint32_t>(result, 30, 144); boundsCheck<uint32_t>(result, 30, 1000);
return result; return result;
}); });
p.add("visualizer_autoscale", &visualizer_autoscale, "no", yes_no); 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", &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, p.add("visualizer_spectrum_dft_size", &visualizer_spectrum_dft_size,
"2", [](std::string v) { "2", [](std::string v) {
auto result = verbose_lexical_cast<size_t>(v); auto result = verbose_lexical_cast<size_t>(v);
@@ -299,6 +278,8 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
lowerBoundCheck<double>(result, Config.visualizer_spectrum_hz_min+1); lowerBoundCheck<double>(result, Config.visualizer_spectrum_hz_min+1);
return result; 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, p.add("visualizer_color", &visualizer_colors,
"blue, cyan, green, yellow, magenta, red", list_of<NC::FormattedColor>); "blue, cyan, green, yellow, magenta, red", list_of<NC::FormattedColor>);
p.add("system_encoding", &system_encoding, "", [](std::string encoding) { p.add("system_encoding", &system_encoding, "", [](std::string encoding) {
@@ -480,9 +461,27 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
p.add("titles_visibility", &titles_visibility, "yes", yes_no); p.add("titles_visibility", &titles_visibility, "yes", yes_no);
p.add("header_text_scrolling", &header_text_scrolling, "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("cyclic_scrolling", &use_cyclic_scrolling, "no", yes_no);
p.add("lyrics_fetchers", &lyrics_fetchers, p.add<void>("lyrics_fetchers", nullptr,
"azlyrics, genius, musixmatch, sing365, metrolyrics, justsomelyrics, jahlyrics, plyrics, tekstowo, zeneszoveg, internet", #ifdef HAVE_TAGLIB_H
list_of<LyricsFetcher_>); "tags, "
#endif
"genius, tekstowo, plyrics, justsomelyrics, jahlyrics, zeneszoveg, internet", [this](std::string v) {
lyrics_fetchers = list_of<LyricsFetcher_>(v, [](std::string s) {
LyricsFetcher_ fetcher;
try {
fetcher = boost::lexical_cast<LyricsFetcher_>(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("follow_now_playing_lyrics", &now_playing_lyrics, "no", yes_no);
p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background, p.add("fetch_lyrics_for_current_song_in_background", &fetch_lyrics_in_background,
"no", yes_no); "no", yes_no);
@@ -534,6 +533,12 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
}); });
p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part, p.add("ask_for_locked_screen_width_part", &ask_for_locked_screen_width_part,
"yes", yes_no); "yes", yes_no);
p.add("media_library_column_width_ratio_two", &media_library_column_width_ratio_two,
"1:1", std::bind(parse_ratio, ph::_1, 2));
p.add("media_library_column_width_ratio_three", &media_library_column_width_ratio_three,
"1:1:1", std::bind(parse_ratio, ph::_1, 3));
p.add("playlist_editor_column_width_ratio", &playlist_editor_column_width_ratio,
"1:2", std::bind(parse_ratio, ph::_1, 2));
p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start, p.add("jump_to_now_playing_song_at_start", &jump_to_now_playing_song_at_start,
"yes", yes_no); "yes", yes_no);
p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists, p.add("ask_before_clearing_playlists", &ask_before_clearing_playlists,
@@ -549,6 +554,7 @@ bool Configuration::read(const std::vector<std::string> &config_paths, bool igno
return boost::regex::icase | boost::regex::literal; return boost::regex::icase | boost::regex::literal;
else if (v == "basic") else if (v == "basic")
return boost::regex::icase | boost::regex::basic; return boost::regex::icase | boost::regex::basic;
else if (v == "extended") else if (v == "extended")
return boost::regex::icase | boost::regex::extended; return boost::regex::icase | boost::regex::extended;
else if (v == "perl") else if (v == "perl")

View File

@@ -86,13 +86,20 @@ struct Configuration
size_t visualizer_fps; size_t visualizer_fps;
bool visualizer_autoscale; bool visualizer_autoscale;
bool visualizer_spectrum_smooth_look; bool visualizer_spectrum_smooth_look;
bool visualizer_spectrum_smooth_look_legacy_chars;
uint32_t visualizer_spectrum_dft_size; uint32_t visualizer_spectrum_dft_size;
double visualizer_spectrum_gain; double visualizer_spectrum_gain;
double visualizer_spectrum_hz_min; double visualizer_spectrum_hz_min;
double visualizer_spectrum_hz_max; double visualizer_spectrum_hz_max;
bool visualizer_spectrum_log_scale_x;
bool visualizer_spectrum_log_scale_y;
std::string pattern; std::string pattern;
std::vector<size_t> playlist_editor_column_width_ratio;
std::vector<size_t> media_library_column_width_ratio_two;
std::vector<size_t> media_library_column_width_ratio_three;
std::vector<Column> columns; std::vector<Column> columns;
DisplayMode playlist_display_mode; DisplayMode playlist_display_mode;

View File

@@ -293,7 +293,9 @@ bool Song::isFromDatabase() const
bool Song::isStream() const bool Song::isStream() const
{ {
assert(m_song); 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 bool Song::empty() const
@@ -308,7 +310,7 @@ std::string Song::ShowTime(unsigned length)
int minutes = length/60; int minutes = length/60;
length -= minutes*60; length -= minutes*60;
int seconds = length; int seconds = length;
std::string result; std::string result;
if (hours > 0) if (hours > 0)
result = (boost::format("%d:%02d:%02d") % hours % minutes % seconds).str(); result = (boost::format("%d:%02d:%02d") % hours % minutes % seconds).str();

View File

@@ -24,7 +24,6 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include "curses/window.h" #include "curses/window.h"
#include "settings.h" #include "settings.h"
#include "gcc.h"
#include "interfaces.h" #include "interfaces.h"
namespace Progressbar { namespace Progressbar {

View File

@@ -92,7 +92,7 @@ void readID3v2Tags(mpd_song *s, TagLib::ID3v2::Tag *tag)
readFrame(frames["TRCK"], "Track"); readFrame(frames["TRCK"], "Track");
readFrame(frames["TCON"], "Genre"); readFrame(frames["TCON"], "Genre");
readFrame(frames["TCOM"], "Composer"); readFrame(frames["TCOM"], "Composer");
readFrame(frames["TPE3"], "Performer"); readFrame(frames["TPE4"], "Performer");
readFrame(frames["TPOS"], "Disc"); readFrame(frames["TPOS"], "Disc");
readFrame(frames["COMM"], "Comment"); readFrame(frames["COMM"], "Comment");
} }
@@ -123,12 +123,12 @@ void writeCommonTags(const MPD::MutableSong &s, TagLib::Tag *tag)
tag->setArtist(ToWString(s.getArtist())); tag->setArtist(ToWString(s.getArtist()));
tag->setAlbum(ToWString(s.getAlbum())); tag->setAlbum(ToWString(s.getAlbum()));
try { try {
tag->setYear(boost::lexical_cast<TagLib::uint>(s.getDate())); tag->setYear(boost::lexical_cast<unsigned>(s.getDate()));
} catch (boost::bad_lexical_cast &) { } catch (boost::bad_lexical_cast &) {
std::cerr << "writeCommonTags: couldn't write 'year' tag to '" << s.getURI() << "' as it's not a positive integer\n"; std::cerr << "writeCommonTags: couldn't write 'year' tag to '" << s.getURI() << "' as it's not a positive integer\n";
} }
try { try {
tag->setTrack(boost::lexical_cast<TagLib::uint>(s.getTrack())); tag->setTrack(boost::lexical_cast<unsigned>(s.getTrack()));
} catch (boost::bad_lexical_cast &) { } catch (boost::bad_lexical_cast &) {
std::cerr << "writeCommonTags: couldn't write 'track' tag to '" << s.getURI() << "' as it's not a positive integer\n"; std::cerr << "writeCommonTags: couldn't write 'track' tag to '" << s.getURI() << "' as it's not a positive integer\n";
} }
@@ -166,7 +166,7 @@ void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag)
writeID3v2("TRCK", tagList(s, &MPD::Song::getTrack)); writeID3v2("TRCK", tagList(s, &MPD::Song::getTrack));
writeID3v2("TCON", tagList(s, &MPD::Song::getGenre)); writeID3v2("TCON", tagList(s, &MPD::Song::getGenre));
writeID3v2("TCOM", tagList(s, &MPD::Song::getComposer)); writeID3v2("TCOM", tagList(s, &MPD::Song::getComposer));
writeID3v2("TPE3", tagList(s, &MPD::Song::getPerformer)); writeID3v2("TPE4", tagList(s, &MPD::Song::getPerformer));
writeID3v2("TPOS", tagList(s, &MPD::Song::getDisc)); writeID3v2("TPOS", tagList(s, &MPD::Song::getDisc));
writeID3v2("COMM", tagList(s, &MPD::Song::getComment)); writeID3v2("COMM", tagList(s, &MPD::Song::getComment));
} }
@@ -174,6 +174,7 @@ void writeID3v2Tags(const MPD::MutableSong &s, TagLib::ID3v2::Tag *tag)
void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag) void writeXiphComments(const MPD::MutableSong &s, TagLib::Ogg::XiphComment *tag)
{ {
auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) { auto writeXiph = [&](const TagLib::String &type, const TagLib::StringList &list) {
tag->removeFields(type);
for (auto it = list.begin(); it != list.end(); ++it) for (auto it = list.begin(); it != list.end(); ++it)
tag->addField(type, *it, it == list.begin()); tag->addField(type, *it, it == list.begin());
}; };
@@ -261,7 +262,7 @@ void read(mpd_song *s)
if (f.isNull()) if (f.isNull())
return; return;
setAttribute(s, "Time", boost::lexical_cast<std::string>(f.audioProperties()->length())); setAttribute(s, "Time", boost::lexical_cast<std::string>(f.audioProperties()->lengthInSeconds()));
if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file())) if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
{ {
@@ -301,15 +302,9 @@ bool write(MPD::MutableSong &s)
if (f.isNull()) if (f.isNull())
return false; return false;
bool saved = false;
if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file())) if (auto mpeg_file = dynamic_cast<TagLib::MPEG::File *>(f.file()))
{ {
writeID3v2Tags(s, mpeg_file->ID3v2Tag(true)); 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<TagLib::Ogg::Vorbis::File *>(f.file())) else if (auto vorbis_file = dynamic_cast<TagLib::Ogg::Vorbis::File *>(f.file()))
{ {
@@ -326,7 +321,7 @@ bool write(MPD::MutableSong &s)
else else
writeCommonTags(s, f.tag()); writeCommonTags(s, f.tag());
if (!saved && !f.save()) if (!f.save())
return false; return false;
// TODO: move this somewhere else // TODO: move this somewhere else

View File

@@ -34,10 +34,23 @@ bool hasTheWord(const std::string &s)
&& (s[3] == ' '); && (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 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; size_t ac_off = 0, bc_off = 0;
if (m_ignore_the) if (m_ignore_the)
{ {

View File

@@ -26,7 +26,6 @@
#include <boost/type_traits/is_unsigned.hpp> #include <boost/type_traits/is_unsigned.hpp>
#include "config.h" #include "config.h"
#include "gcc.h"
struct ConversionError struct ConversionError
{ {
@@ -44,21 +43,21 @@ struct OutOfBounds : std::exception
const std::string &errorMessage() { return m_error_message; } const std::string &errorMessage() { return m_error_message; }
template <typename Type> template <typename Type>
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( throw OutOfBounds((boost::format(
"value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str()); "value is out of bounds ([%1%, %2%] expected, %3% given)") % lbound % ubound % value).str());
} }
template <typename Type> template <typename Type>
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( throw OutOfBounds((boost::format(
"value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str()); "value is out of bounds ([%1%, ->) expected, %2% given)") % lbound % value).str());
} }
template <typename Type> template <typename Type>
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( throw OutOfBounds((boost::format(
"value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str()); "value is out of bounds ((<-, %1%] expected, %2% given)") % ubound % value).str());

View File

@@ -20,16 +20,29 @@
#include <algorithm> #include <algorithm>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <cstdlib>
#include "utility/html.h" #include "utility/html.h"
std::string unescapeHtmlUtf8(const std::string &data) std::string unescapeHtmlUtf8(const std::string &data)
{ {
int base;
size_t offset;
std::string result; std::string result;
for (size_t i = 0, j; i < data.length(); ++i) for (size_t i = 0, j; i < data.length(); ++i)
{ {
if (data[i] == '&' && data[i+1] == '#' && (j = data.find(';', i)) != std::string::npos) 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) if (n >= 0x800)
{ {
result += (0xe0 | ((n >> 12) & 0x0f)); result += (0xe0 | ((n >> 12) & 0x0f));

View File

@@ -45,6 +45,20 @@ bool yes_no(const std::string &v)
invalid_value(v); invalid_value(v);
} }
std::vector<size_t> parse_ratio(const std::string &v, const std::vector<size_t>::size_type length)
{
std::vector<size_t> ret = list_of<size_t>(v, verbose_lexical_cast<size_t>, length, "", ":", "");
size_t total = 0;
for (auto i : ret)
total += i;
if (total == 0)
invalid_value(v);
return ret;
}
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
bool option_parser::run(std::istream &is, bool ignore_errors) bool option_parser::run(std::istream &is, bool ignore_errors)

View File

@@ -56,17 +56,26 @@ DestT verbose_lexical_cast(const std::string &v)
} }
template <typename ValueT, typename ConvertT> template <typename ValueT, typename ConvertT>
std::vector<ValueT> list_of(const std::string &v, ConvertT convert) std::vector<ValueT> list_of(const std::string &v, ConvertT convert, const typename std::vector<ValueT>::size_type length, const std::string &escape, const std::string &sep, const std::string &quote)
{ {
std::vector<ValueT> result; std::vector<ValueT> result;
boost::tokenizer<boost::escaped_list_separator<char>> elems(v); boost::escaped_list_separator<char> esq(escape, sep, quote);
boost::tokenizer<boost::escaped_list_separator<char>> elems(v, esq);
for (auto &value : elems) for (auto &value : elems)
result.push_back(convert(boost::trim_copy(value))); result.push_back(convert(boost::trim_copy(value)));
if (result.empty()) if (result.empty())
throw std::runtime_error("empty list"); throw std::runtime_error("empty list");
if (length > 0 && result.size() != length)
throw std::runtime_error("invalid list length");
return result; return result;
} }
template <typename ValueT, typename ConvertT>
std::vector<ValueT> list_of(const std::string &v, ConvertT convert)
{
return list_of<ValueT>(v, convert, 0, "\\", ",", "\"");
}
template <typename ValueT> template <typename ValueT>
std::vector<ValueT> list_of(const std::string &v) std::vector<ValueT> list_of(const std::string &v)
{ {
@@ -75,6 +84,8 @@ std::vector<ValueT> list_of(const std::string &v)
bool yes_no(const std::string &v); bool yes_no(const std::string &v);
std::vector<size_t> parse_ratio(const std::string &v, const std::vector<size_t>::size_type length);
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
class option_parser class option_parser

View File

@@ -25,7 +25,6 @@
#include <locale> #include <locale>
#include <string> #include <string>
#include <vector> #include <vector>
#include "gcc.h"
template <size_t N> size_t const_strlen(const char (&)[N]) { template <size_t N> size_t const_strlen(const char (&)[N]) {
return N-1; return N-1;

View File

@@ -168,6 +168,8 @@ MPD::Song::GetFunction charToGetFunction(char c)
return &MPD::Song::getDirectory; return &MPD::Song::getDirectory;
case 'f': case 'f':
return &MPD::Song::getName; return &MPD::Song::getName;
case 'F':
return &MPD::Song::getURI;
case 'a': case 'a':
return &MPD::Song::getArtist; return &MPD::Song::getArtist;
case 'A': case 'A':