Compare commits
17 Commits
b3fe85995f
...
v2.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
c4bce75866
|
|||
|
08b87bccbd
|
|||
|
fa460b97cf
|
|||
|
ef19337bad
|
|||
|
9699b80e9d
|
|||
|
7601a754af
|
|||
|
31d975604b
|
|||
|
59f82b7a72
|
|||
|
8bcdb0afd5
|
|||
|
9dfd89ef06
|
|||
|
4b592ec295
|
|||
|
58eb7edfd5
|
|||
|
48fc9bdb19
|
|||
|
9ab5ab6928
|
|||
|
93e0d7027a
|
|||
|
5900b0708a
|
|||
|
5f97b95026
|
@@ -1,5 +1,5 @@
|
|||||||
cmake_minimum_required (VERSION 3.0)
|
cmake_minimum_required (VERSION 3.0...3.27)
|
||||||
project (nncmpp VERSION 2.0.0 LANGUAGES C)
|
project (nncmpp VERSION 2.1.1 LANGUAGES C)
|
||||||
|
|
||||||
# Moar warnings
|
# Moar warnings
|
||||||
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
|
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
|
||||||
@@ -68,7 +68,7 @@ if (WITH_PULSE)
|
|||||||
list (APPEND extra_libraries ${libpulse_LIBRARIES})
|
list (APPEND extra_libraries ${libpulse_LIBRARIES})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
pkg_check_modules (x11 x11 xrender xft fontconfig)
|
pkg_check_modules (x11 x11 xrender xft fontconfig libpng)
|
||||||
add_option (WITH_X11 "Build with X11 support" "${x11_FOUND}")
|
add_option (WITH_X11 "Build with X11 support" "${x11_FOUND}")
|
||||||
if (WITH_X11)
|
if (WITH_X11)
|
||||||
if (NOT x11_FOUND)
|
if (NOT x11_FOUND)
|
||||||
@@ -128,10 +128,25 @@ add_threads (${PROJECT_NAME})
|
|||||||
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||||
install (DIRECTORY contrib DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})
|
install (DIRECTORY contrib DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})
|
||||||
install (DIRECTORY info DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})
|
install (DIRECTORY info DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}
|
||||||
|
USE_SOURCE_PERMISSIONS)
|
||||||
if (WITH_X11)
|
if (WITH_X11)
|
||||||
|
include (IconUtils)
|
||||||
|
|
||||||
|
set (icon_base ${PROJECT_BINARY_DIR}/icons)
|
||||||
|
set (icon_png_list)
|
||||||
|
foreach (icon_size 16 32 48)
|
||||||
|
icon_to_png (${PROJECT_NAME} ${PROJECT_SOURCE_DIR}/${PROJECT_NAME}.svg
|
||||||
|
${icon_size} ${icon_base} icon_png)
|
||||||
|
list (APPEND icon_png_list ${icon_png})
|
||||||
|
endforeach ()
|
||||||
|
|
||||||
|
add_custom_target (icons ALL DEPENDS ${icon_png_list})
|
||||||
|
|
||||||
install (FILES ${PROJECT_NAME}.svg
|
install (FILES ${PROJECT_NAME}.svg
|
||||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
|
||||||
|
install (DIRECTORY ${icon_base}
|
||||||
|
DESTINATION ${CMAKE_INSTALL_DATADIR})
|
||||||
install (FILES ${PROJECT_NAME}.desktop
|
install (FILES ${PROJECT_NAME}.desktop
|
||||||
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
|
||||||
endif ()
|
endif ()
|
||||||
|
|||||||
17
NEWS
17
NEWS
@@ -1,4 +1,13 @@
|
|||||||
Unreleased
|
2.1.1 (2024-02-27)
|
||||||
|
|
||||||
|
* Fixed installation of Info tab plugins
|
||||||
|
|
||||||
|
* Fixed display of playback mode toggles in the terminal user interface
|
||||||
|
|
||||||
|
* Fixed a dead link in the manual page
|
||||||
|
|
||||||
|
|
||||||
|
2.1.0 (2024-02-11)
|
||||||
|
|
||||||
* Added ability to look up song lyrics,
|
* Added ability to look up song lyrics,
|
||||||
using a new scriptable extension interface for the Info tab
|
using a new scriptable extension interface for the Info tab
|
||||||
@@ -13,12 +22,18 @@ Unreleased
|
|||||||
|
|
||||||
* X11: fixed rendering of overflowing, partially visible list items
|
* X11: fixed rendering of overflowing, partially visible list items
|
||||||
|
|
||||||
|
* X11: fixed a crash when resizing the window to zero dimensions
|
||||||
|
|
||||||
* Added a "o" binding to select the currently playing song
|
* Added a "o" binding to select the currently playing song
|
||||||
|
|
||||||
* Added Readline-like M-u, M-l, M-c editor bindings
|
* Added Readline-like M-u, M-l, M-c editor bindings
|
||||||
|
|
||||||
|
* Made the scroll wheel work on the elapsed time gauge and the volume display
|
||||||
|
|
||||||
* Changed volume adjustment bindings to use +/- keys
|
* Changed volume adjustment bindings to use +/- keys
|
||||||
|
|
||||||
|
* Changed volume adjustment to go in steps of 5 rather than 10 %
|
||||||
|
|
||||||
|
|
||||||
2.0.0 (2022-09-03)
|
2.0.0 (2022-09-03)
|
||||||
|
|
||||||
|
|||||||
14
README.adoc
14
README.adoc
@@ -28,8 +28,10 @@ image::nncmpp.png[align="center"]
|
|||||||
|
|
||||||
Packages
|
Packages
|
||||||
--------
|
--------
|
||||||
Regular releases are sporadic. git master should be stable enough. You can get
|
Regular releases are sporadic. git master should be stable enough.
|
||||||
a package with the latest development version from Archlinux's AUR.
|
You can get a package with the latest development version using Arch Linux's
|
||||||
|
https://aur.archlinux.org/packages/nncmpp-git[AUR],
|
||||||
|
or as a https://git.janouch.name/p/nixexprs[Nix derivation].
|
||||||
|
|
||||||
Documentation
|
Documentation
|
||||||
-------------
|
-------------
|
||||||
@@ -38,10 +40,12 @@ The rest of this README will concern itself with externalities.
|
|||||||
|
|
||||||
Building
|
Building
|
||||||
--------
|
--------
|
||||||
Build dependencies: CMake, pkg-config, awk, liberty (included),
|
Build-only dependencies: CMake, pkg-config, awk, liberty (included),
|
||||||
termo (included), asciidoctor or asciidoc (recommended but optional) +
|
termo (included), asciidoctor or asciidoc (recommended but optional),
|
||||||
|
rsvg-convert (X11) +
|
||||||
Runtime dependencies: ncursesw, libunistring, cURL +
|
Runtime dependencies: ncursesw, libunistring, cURL +
|
||||||
Optional runtime dependencies: fftw3, libpulse, x11, xft, Perl + cURL (lyrics)
|
Optional runtime dependencies: fftw3, libpulse, x11 + xft + libpng (X11),
|
||||||
|
Perl + cURL (lyrics)
|
||||||
|
|
||||||
$ git clone --recursive https://git.janouch.name/p/nncmpp.git
|
$ git clone --recursive https://git.janouch.name/p/nncmpp.git
|
||||||
$ mkdir nncmpp/build
|
$ mkdir nncmpp/build
|
||||||
|
|||||||
2
liberty
2
liberty
Submodule liberty updated: d01a1ff034...969a4cfc3e
@@ -69,7 +69,7 @@ colors = {
|
|||||||
scrollbar = ""
|
scrollbar = ""
|
||||||
}
|
}
|
||||||
streams = {
|
streams = {
|
||||||
"dnbradio.com" = "http://www.dnbradio.com/hi.m3u"
|
"dnbradio.com" = "https://dnbradio.com/hi.pls"
|
||||||
"BassDrive.com" = "http://bassdrive.com/v2/streams/BassDrive.pls"
|
"BassDrive.com" = "http://bassdrive.com/v2/streams/BassDrive.pls"
|
||||||
}
|
}
|
||||||
....
|
....
|
||||||
|
|||||||
80
nncmpp.c
80
nncmpp.c
@@ -1148,8 +1148,8 @@ pulse_volume_status (struct pulse *self, struct str *s)
|
|||||||
// Widget identification, mostly for mouse events.
|
// Widget identification, mostly for mouse events.
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
WIDGET_NONE = 0, WIDGET_BUTTON, WIDGET_GAUGE, WIDGET_TAB, WIDGET_SPECTRUM,
|
WIDGET_NONE = 0, WIDGET_BUTTON, WIDGET_GAUGE, WIDGET_VOLUME,
|
||||||
WIDGET_LIST, WIDGET_SCROLLBAR, WIDGET_MESSAGE,
|
WIDGET_TAB, WIDGET_SPECTRUM, WIDGET_LIST, WIDGET_SCROLLBAR, WIDGET_MESSAGE,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct layout
|
struct layout
|
||||||
@@ -1616,7 +1616,7 @@ app_append_layout (struct layout *l, struct layout *dest)
|
|||||||
{
|
{
|
||||||
// Assuming there is no unclaimed vertical space.
|
// Assuming there is no unclaimed vertical space.
|
||||||
LIST_FOR_EACH (struct widget, w, l->head)
|
LIST_FOR_EACH (struct widget, w, l->head)
|
||||||
widget_move (w, 0, last->y + last->height - w->y);
|
widget_move (w, 0, last->y + last->height);
|
||||||
|
|
||||||
last->next = l->head;
|
last->next = l->head;
|
||||||
l->head->prev = last;
|
l->head->prev = last;
|
||||||
@@ -1866,7 +1866,8 @@ app_layout_status (struct layout *out)
|
|||||||
if (volume.len)
|
if (volume.len)
|
||||||
{
|
{
|
||||||
app_push (&l, g.ui->padding (attrs[0], 1, 1));
|
app_push (&l, g.ui->padding (attrs[0], 1, 1));
|
||||||
app_push (&l, g.ui->label (attrs[0], volume.str));
|
app_push (&l, g.ui->label (attrs[0], volume.str))
|
||||||
|
->id = WIDGET_VOLUME;
|
||||||
}
|
}
|
||||||
str_free (&volume);
|
str_free (&volume);
|
||||||
|
|
||||||
@@ -1913,20 +1914,22 @@ app_layout_tabs (struct layout *out)
|
|||||||
app_flush_layout (&l, out);
|
app_flush_layout (&l, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
app_layout_padding (chtype attrs, struct layout *out)
|
||||||
|
{
|
||||||
|
struct layout l = {};
|
||||||
|
app_push_fill (&l, g.ui->padding (attrs, 0, 0.125));
|
||||||
|
app_flush_layout (&l, out);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
app_layout_header (struct layout *out)
|
app_layout_header (struct layout *out)
|
||||||
{
|
{
|
||||||
if (g.client.state == MPD_CONNECTED)
|
if (g.client.state == MPD_CONNECTED)
|
||||||
{
|
{
|
||||||
struct layout lt = {};
|
app_layout_padding (APP_ATTR (NORMAL), out);
|
||||||
app_push_fill (<, g.ui->padding (APP_ATTR (NORMAL), 0, 0.125));
|
|
||||||
app_flush_layout (<, out);
|
|
||||||
|
|
||||||
app_layout_status (out);
|
app_layout_status (out);
|
||||||
|
app_layout_padding (APP_ATTR (NORMAL), out);
|
||||||
struct layout lb = {};
|
|
||||||
app_push_fill (&lb, g.ui->padding (APP_ATTR (NORMAL), 0, 0.125));
|
|
||||||
app_flush_layout (&lb, out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app_layout_tabs (out);
|
app_layout_tabs (out);
|
||||||
@@ -2136,8 +2139,10 @@ app_layout_mpd_status (struct layout *out)
|
|||||||
static void
|
static void
|
||||||
app_layout_statusbar (struct layout *out)
|
app_layout_statusbar (struct layout *out)
|
||||||
{
|
{
|
||||||
struct layout l = {};
|
|
||||||
chtype attrs[2] = { APP_ATTR (NORMAL), APP_ATTR (HIGHLIGHT) };
|
chtype attrs[2] = { APP_ATTR (NORMAL), APP_ATTR (HIGHLIGHT) };
|
||||||
|
app_layout_padding (attrs[0], out);
|
||||||
|
|
||||||
|
struct layout l = {};
|
||||||
if (g.message)
|
if (g.message)
|
||||||
{
|
{
|
||||||
app_push (&l, g.ui->padding (attrs[0], 0.25, 1));
|
app_push (&l, g.ui->padding (attrs[0], 0.25, 1));
|
||||||
@@ -2167,6 +2172,8 @@ app_layout_statusbar (struct layout *out)
|
|||||||
app_layout_text ("Connecting to MPD...", attrs[0], out);
|
app_layout_text ("Connecting to MPD...", attrs[0], out);
|
||||||
else if (g.client.state == MPD_DISCONNECTED)
|
else if (g.client.state == MPD_DISCONNECTED)
|
||||||
app_layout_text ("Disconnected", attrs[0], out);
|
app_layout_text ("Disconnected", attrs[0], out);
|
||||||
|
|
||||||
|
app_layout_padding (attrs[0], out);
|
||||||
}
|
}
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@@ -2619,12 +2626,12 @@ app_process_action (enum action action)
|
|||||||
case ACTION_MPD_CONSUME: return app_mpd_toggle ("consume");
|
case ACTION_MPD_CONSUME: return app_mpd_toggle ("consume");
|
||||||
case ACTION_MPD_UPDATE_DB: return MPD_SIMPLE ("update");
|
case ACTION_MPD_UPDATE_DB: return MPD_SIMPLE ("update");
|
||||||
|
|
||||||
case ACTION_MPD_VOLUME_UP: return app_setvol (g.volume + 10);
|
case ACTION_MPD_VOLUME_UP: return app_setvol (g.volume + 5);
|
||||||
case ACTION_MPD_VOLUME_DOWN: return app_setvol (g.volume - 10);
|
case ACTION_MPD_VOLUME_DOWN: return app_setvol (g.volume - 5);
|
||||||
|
|
||||||
#ifdef WITH_PULSE
|
#ifdef WITH_PULSE
|
||||||
case ACTION_PULSE_VOLUME_UP: return pulse_volume_set (&g.pulse, +10);
|
case ACTION_PULSE_VOLUME_UP: return pulse_volume_set (&g.pulse, +5);
|
||||||
case ACTION_PULSE_VOLUME_DOWN: return pulse_volume_set (&g.pulse, -10);
|
case ACTION_PULSE_VOLUME_DOWN: return pulse_volume_set (&g.pulse, -5);
|
||||||
case ACTION_PULSE_MUTE: return pulse_volume_mute (&g.pulse);
|
case ACTION_PULSE_MUTE: return pulse_volume_mute (&g.pulse);
|
||||||
#endif // WITH_PULSE
|
#endif // WITH_PULSE
|
||||||
|
|
||||||
@@ -2848,12 +2855,34 @@ app_process_mouse (termo_mouse_event_t type, int x, int y, int button,
|
|||||||
g.ui_dragging = target->id;
|
g.ui_dragging = target->id;
|
||||||
return app_process_left_mouse_click (target, x, y, modifiers);
|
return app_process_left_mouse_click (target, x, y, modifiers);
|
||||||
case 4:
|
case 4:
|
||||||
if (target->id == WIDGET_LIST)
|
switch (target->id)
|
||||||
|
{
|
||||||
|
case WIDGET_LIST:
|
||||||
return app_process_action (ACTION_SCROLL_UP);
|
return app_process_action (ACTION_SCROLL_UP);
|
||||||
|
case WIDGET_VOLUME:
|
||||||
|
return app_process_action (
|
||||||
|
#ifdef WITH_PULSE
|
||||||
|
g.pulse_control_requested ? ACTION_PULSE_VOLUME_UP :
|
||||||
|
#endif // WITH_PULSE
|
||||||
|
ACTION_MPD_VOLUME_UP);
|
||||||
|
case WIDGET_GAUGE:
|
||||||
|
return app_process_action (ACTION_MPD_FORWARD);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
if (target->id == WIDGET_LIST)
|
switch (target->id)
|
||||||
|
{
|
||||||
|
case WIDGET_LIST:
|
||||||
return app_process_action (ACTION_SCROLL_DOWN);
|
return app_process_action (ACTION_SCROLL_DOWN);
|
||||||
|
case WIDGET_VOLUME:
|
||||||
|
return app_process_action (
|
||||||
|
#ifdef WITH_PULSE
|
||||||
|
g.pulse_control_requested ? ACTION_PULSE_VOLUME_DOWN :
|
||||||
|
#endif // WITH_PULSE
|
||||||
|
ACTION_MPD_VOLUME_DOWN);
|
||||||
|
case WIDGET_GAUGE:
|
||||||
|
return app_process_action (ACTION_MPD_BACKWARD);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -3814,8 +3843,13 @@ streams_tab_parse_playlist (const char *playlist, const char *content_type,
|
|||||||
|| (content_type && is_content_type (content_type, "audio", "x-scpls")))
|
|| (content_type && is_content_type (content_type, "audio", "x-scpls")))
|
||||||
extract_re = "^File[^=]*=(.+)";
|
extract_re = "^File[^=]*=(.+)";
|
||||||
else if ((lines.len && !strcasecmp_ascii (lines.vector[0], "#EXTM3U"))
|
else if ((lines.len && !strcasecmp_ascii (lines.vector[0], "#EXTM3U"))
|
||||||
|
|| (content_type && is_content_type (content_type, "audio", "mpegurl"))
|
||||||
|| (content_type && is_content_type (content_type, "audio", "x-mpegurl")))
|
|| (content_type && is_content_type (content_type, "audio", "x-mpegurl")))
|
||||||
extract_re = "^([^#].*)";
|
// This could be "^([^#].*)", however 1. we would need to resolve
|
||||||
|
// relative URIs, and 2. relative URIs probably mean a Media Playlist,
|
||||||
|
// which must be passed to MPD. The better thing to do here would be to
|
||||||
|
// reject anything with EXT-X-TARGETDURATION, and to resolve the URIs.
|
||||||
|
extract_re = "^(https?://.+)";
|
||||||
|
|
||||||
regex_t *re = regex_compile (extract_re, REG_EXTENDED, NULL);
|
regex_t *re = regex_compile (extract_re, REG_EXTENDED, NULL);
|
||||||
hard_assert (re != NULL);
|
hard_assert (re != NULL);
|
||||||
@@ -3838,7 +3872,7 @@ streams_tab_extract_links (struct str *data, const char *content_type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
streams_tab_parse_playlist (data->str, content_type, out);
|
streams_tab_parse_playlist (data->str, content_type, out);
|
||||||
return true;
|
return out->len != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -4194,7 +4228,11 @@ info_tab_format_decode_toggle (char c)
|
|||||||
case '\x01':
|
case '\x01':
|
||||||
return A_BOLD;
|
return A_BOLD;
|
||||||
case '\x02':
|
case '\x02':
|
||||||
|
#ifdef A_ITALIC
|
||||||
return A_ITALIC;
|
return A_ITALIC;
|
||||||
|
#else
|
||||||
|
return A_UNDERLINE;
|
||||||
|
#endif
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user