Compare commits

..

No commits in common. "master" and "v2.0.0" have entirely different histories.

15 changed files with 1853 additions and 1237 deletions

View File

@ -1,5 +1,5 @@
cmake_minimum_required (VERSION 3.0...3.27)
project (nncmpp VERSION 2.1.1 LANGUAGES C)
cmake_minimum_required (VERSION 3.0)
project (nncmpp VERSION 2.0.0 LANGUAGES C)
# Moar warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
@ -10,14 +10,6 @@ endif ()
# For custom modules
set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/liberty/cmake)
# Collect important build toggles for our simple preprocessor
# (cpp(1) isn't part of POSIX, otherwise we could reuse config.h)
set (options)
macro (add_option variable help value)
option (${ARGV})
list (APPEND options "${variable}=$<BOOL:${${variable}}>")
endmacro ()
# Dependencies
find_package (Ncursesw REQUIRED)
find_package (PkgConfig REQUIRED)
@ -27,8 +19,8 @@ pkg_check_modules (curl REQUIRED libcurl)
include (AddThreads)
find_package (Termo QUIET NO_MODULE)
add_option (USE_SYSTEM_TERMO
"Don't compile our own termo library, use the system one" "${Termo_FOUND}")
option (USE_SYSTEM_TERMO
"Don't compile our own termo library, use the system one" ${Termo_FOUND})
if (USE_SYSTEM_TERMO)
if (NOT Termo_FOUND)
message (FATAL_ERROR "System termo library not found")
@ -49,8 +41,7 @@ else ()
endif ()
pkg_check_modules (fftw fftw3 fftw3f)
add_option (WITH_FFTW
"Use FFTW to enable spectrum visualisation" "${fftw_FOUND}")
option (WITH_FFTW "Use FFTW to enable spectrum visualisation" ${fftw_FOUND})
if (WITH_FFTW)
if (NOT fftw_FOUND)
message (FATAL_ERROR "FFTW not found")
@ -59,8 +50,7 @@ if (WITH_FFTW)
endif ()
pkg_check_modules (libpulse libpulse)
add_option (WITH_PULSE
"Enable PulseAudio sink volume control" "${libpulse_FOUND}")
option (WITH_PULSE "Enable control of PulseAudio sink volume" ${libpulse_FOUND})
if (WITH_PULSE)
if (NOT libpulse_FOUND)
message (FATAL_ERROR "libpulse not found")
@ -68,8 +58,8 @@ if (WITH_PULSE)
list (APPEND extra_libraries ${libpulse_LIBRARIES})
endif ()
pkg_check_modules (x11 x11 xrender xft fontconfig libpng)
add_option (WITH_X11 "Build with X11 support" "${x11_FOUND}")
pkg_check_modules (x11 x11 xrender xft fontconfig)
option (WITH_X11 "Use FFTW to enable spectrum visualisation" ${x11_FOUND})
if (WITH_X11)
if (NOT x11_FOUND)
message (FATAL_ERROR "Some X11 libraries were not found")
@ -105,59 +95,42 @@ foreach (extra m)
endforeach ()
# Generate a configuration file
include (GNUInstallDirs)
configure_file (${PROJECT_SOURCE_DIR}/config.h.in
${PROJECT_BINARY_DIR}/config.h)
include_directories (${PROJECT_SOURCE_DIR} ${PROJECT_BINARY_DIR})
# Assuming a Unix-compatible system with a standalone preprocessor
set (actions_list ${PROJECT_SOURCE_DIR}/nncmpp.actions)
set (actions_awk ${PROJECT_SOURCE_DIR}/nncmpp.actions.awk)
set (actions ${PROJECT_BINARY_DIR}/nncmpp-actions.h)
add_custom_command (OUTPUT ${actions}
COMMAND env LC_ALL=C ${options}
awk -f ${actions_awk} ${actions_list} > ${actions}
DEPENDS ${actions_awk} ${actions_list} VERBATIM)
COMMAND cpp -I${PROJECT_BINARY_DIR} -P ${actions_list}
| grep . | tr [[\n]] ^ | sed -ne [[h; s/,[^^]*/,/g]] -e [[s/$/COUNT/]]
-e [[s/[^^]*/\tACTION_&/g]] -e [[s/.*/enum action {\n&\n};\n/p]]
-e [[g; s/,[^^]*//g; y/_/-/]] -e [[s/[^^]\{1,\}/\t"&",/g]]
-e [[s/.*/static const char *g_action_names[] = {\n&};\n/p]]
-e [[g; s/[^^]*, *//g;]] -e [[s/[^^]\{1,\}/\t"&",/g]]
-e [[s/.*/static const char *g_action_descriptions[] = {\n&};/p]]
| tr ^ [[\n]] > ${actions}
COMMAND test -s ${actions}
DEPENDS ${actions_list} ${PROJECT_BINARY_DIR}/config.h VERBATIM)
# Build the main executable and link it
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c ${actions})
target_link_libraries (${PROJECT_NAME} ${Unistring_LIBRARIES}
${Ncursesw_LIBRARIES} ${Termo_LIBRARIES} ${curl_LIBRARIES}
${extra_libraries})
${Ncursesw_LIBRARIES} termo-static ${curl_LIBRARIES} ${extra_libraries})
add_threads (${PROJECT_NAME})
# Installation
include (GNUInstallDirs)
install (TARGETS ${PROJECT_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
install (DIRECTORY contrib DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME})
install (DIRECTORY info DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}
USE_SOURCE_PERMISSIONS)
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
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/scalable/apps)
install (DIRECTORY ${icon_base}
DESTINATION ${CMAKE_INSTALL_DATADIR})
install (FILES ${PROJECT_NAME}.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications)
endif ()
# Generate documentation from text markup
find_program (ASCIIDOCTOR_EXECUTABLE asciidoctor)
find_program (A2X_EXECUTABLE a2x)
if (NOT ASCIIDOCTOR_EXECUTABLE AND NOT A2X_EXECUTABLE)
message (WARNING "Neither asciidoctor nor a2x were found, "
"falling back to a substandard manual page generator")
message (FATAL_ERROR "Neither asciidoctor nor a2x were found")
endif ()
foreach (page ${PROJECT_NAME})
@ -179,14 +152,6 @@ foreach (page ${PROJECT_NAME})
"${PROJECT_SOURCE_DIR}/${page}.adoc"
DEPENDS ${page}.adoc
COMMENT "Generating man page for ${page}" VERBATIM)
else ()
set (ASCIIMAN ${PROJECT_SOURCE_DIR}/liberty/tools/asciiman.awk)
add_custom_command (OUTPUT ${page_output}
COMMAND env LC_ALL=C asciidoc-release-version=${PROJECT_VERSION}
awk -f ${ASCIIMAN} "${PROJECT_SOURCE_DIR}/${page}.adoc"
> ${page_output}
DEPENDS ${page}.adoc ${ASCIIMAN}
COMMENT "Generating man page for ${page}" VERBATIM)
endif ()
endforeach ()
@ -198,33 +163,6 @@ foreach (page ${project_MAN_PAGES})
DESTINATION "${CMAKE_INSTALL_MANDIR}/man${CMAKE_MATCH_1}")
endforeach ()
# Testing
option (BUILD_TESTING "Build tests" OFF)
if (BUILD_TESTING)
enable_testing ()
find_program (xmlwf_EXECUTABLE xmlwf)
find_program (xmllint_EXECUTABLE xmllint)
foreach (xml ${PROJECT_NAME}.svg)
if (xmlwf_EXECUTABLE)
add_test (test-xmlwf-${xml} ${xmlwf_EXECUTABLE}
${PROJECT_SOURCE_DIR}/${xml})
endif ()
if (xmllint_EXECUTABLE)
add_test (test-xmllint-${xml} ${xmllint_EXECUTABLE} --noout
${PROJECT_SOURCE_DIR}/${xml})
endif ()
endforeach ()
find_program (dfv_EXECUTABLE desktop-file-validate)
if (dfv_EXECUTABLE)
foreach (df ${PROJECT_NAME}.desktop)
add_test (test-dfv-${df} ${dfv_EXECUTABLE}
${PROJECT_SOURCE_DIR}/${df})
endforeach ()
endif ()
endif ()
# CPack
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Terminal/X11 MPD client")
set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")

View File

@ -1,4 +1,4 @@
Copyright (c) 2016 - 2023, Přemysl Eric Janouch <p@janouch.name>
Copyright (c) 2016 - 2022, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.

37
NEWS
View File

@ -1,40 +1,3 @@
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,
using a new scriptable extension interface for the Info tab
* Improved song information shown in the window header
* Escape no longer quits the program
* X11: added an icon and a desktop entry file
* X11: added support for font fallbacks and italic fonts
* 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 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 to go in steps of 5 rather than 10 %
2.0.0 (2022-09-03)
* Added an optional X11 user interface

View File

@ -18,7 +18,6 @@ Features
Most stuff is there. I've been using the program exclusively for many years.
Among other things, it can display and change PulseAudio volume directly
to cover the use case of remote control, it has a fast spectrum visualiser,
it can be extended with plugins to fetch lyrics or other song-related info,
and both its appearance and key bindings can be customized.
Note that currently only the filesystem browsing mode is implemented,
@ -28,10 +27,8 @@ image::nncmpp.png[align="center"]
Packages
--------
Regular releases are sporadic. git master should be stable enough.
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].
Regular releases are sporadic. git master should be stable enough. You can get
a package with the latest development version from Archlinux's AUR.
Documentation
-------------
@ -40,12 +37,10 @@ The rest of this README will concern itself with externalities.
Building
--------
Build-only dependencies: CMake, pkg-config, awk, liberty (included),
termo (included), asciidoctor or asciidoc (recommended but optional),
rsvg-convert (X11) +
Build dependencies: CMake, pkg-config, asciidoctor or asciidoc,
liberty (included), termo (included) +
Runtime dependencies: ncursesw, libunistring, cURL +
Optional runtime dependencies: fftw3, libpulse, x11 + xft + libpng (X11),
Perl + cURL (lyrics)
Optional runtime dependencies: fftw3, libpulse, x11, xft
$ git clone --recursive https://git.janouch.name/p/nncmpp.git
$ mkdir nncmpp/build

View File

@ -4,9 +4,6 @@
#define PROGRAM_NAME "${PROJECT_NAME}"
#define PROGRAM_VERSION "${PROJECT_VERSION}"
// We use the XDG Base Directory Specification, but may be installed anywhere.
#define PROJECT_DATADIR "${CMAKE_INSTALL_FULL_DATADIR}"
#cmakedefine HAVE_RESIZETERM
#cmakedefine WITH_FFTW
#cmakedefine WITH_PULSE

View File

@ -1,43 +0,0 @@
#!/usr/bin/env perl
# 10-azlyrics.pl: nncmpp info plugin to fetch song lyrics on AZLyrics
#
# Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
# SPDX-License-Identifier: 0BSD
#
# Inspired by a similar ncmpc plugin.
use warnings;
use strict;
use utf8;
use open ':std', ':utf8';
unless (@ARGV) {
print "Look up on AZLyrics\n";
exit;
}
use Encode;
my ($title, $artist, $album) = map {decode_utf8($_)} @ARGV;
# TODO: An upgrade would be transliteration with, e.g., Text::Unidecode.
use Unicode::Normalize;
$artist = lc(NFD($artist)) =~ s/^the\s+//ir =~ s/[^a-z0-9]//gr;
$title = lc(NFD($title)) =~ s/\(.*?\)//gr =~ s/[^a-z0-9]//gr;
# TODO: Consider caching the results in a location like
# $XDG_CACHE_HOME/nncmpp/info/azlyrics/$artist-$title
my $found = 0;
if ($title ne '') {
open(my $curl, '-|', 'curl', '-sA', 'nncmpp/2.0',
"https://www.azlyrics.com/lyrics/$artist/$title.html") or die $!;
while (<$curl>) {
next unless /^<div>/ .. /^<\/div>/; s/<!--.*?-->//g; s/\s+$//gs;
$found = 1;
s/<\/?b>/\x01/g; s/<\/?i>/\x02/g; s/<br>/\n/; s/<.+?>//g;
s/&lt;/</g; s/&gt;/>/g; s/&quot;/"/g; s/&apos;/'/g; s/&amp;/&/g;
print;
}
close($curl) or die $?;
}
print "No lyrics have been found.\n" unless $found;

@ -1 +1 @@
Subproject commit 969a4cfc3ea1c4d7c0327907385fc64906ed5d4c
Subproject commit 63aed8f0fd61e097ae9eea43977cac4af595ca43

288
line-editor.c Normal file
View File

@ -0,0 +1,288 @@
/*
* line-editor.c: a line editor component for the TUI part of liberty
*
* Copyright (c) 2017 - 2018, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
// This is here just for IDE code model reasons
#ifndef HAVE_LIBERTY
#include "liberty/liberty.c"
#include "liberty/liberty-tui.c"
#endif
static void
row_buffer_append_c (struct row_buffer *self, ucs4_t c, chtype attrs)
{
struct row_char current = { .attrs = attrs, .c = c };
struct row_char invalid = { .attrs = attrs, .c = '?', .width = 1 };
current.width = uc_width (current.c, locale_charset ());
if (current.width < 0 || !app_is_character_in_locale (current.c))
current = invalid;
ARRAY_RESERVE (self->chars, 1);
self->chars[self->chars_len++] = current;
self->total_width += current.width;
}
// --- Line editor -------------------------------------------------------------
enum line_editor_action
{
LINE_EDITOR_B_CHAR, ///< Go back a character
LINE_EDITOR_F_CHAR, ///< Go forward a character
LINE_EDITOR_B_WORD, ///< Go back a word
LINE_EDITOR_F_WORD, ///< Go forward a word
LINE_EDITOR_HOME, ///< Go to start of line
LINE_EDITOR_END, ///< Go to end of line
LINE_EDITOR_B_DELETE, ///< Delete last character
LINE_EDITOR_F_DELETE, ///< Delete next character
LINE_EDITOR_B_KILL_WORD, ///< Delete last word
LINE_EDITOR_B_KILL_LINE, ///< Delete everything up to BOL
LINE_EDITOR_F_KILL_LINE, ///< Delete everything up to EOL
};
struct line_editor
{
int point; ///< Caret index into line data
ucs4_t *line; ///< Line data, 0-terminated
int *w; ///< Codepoint widths, 0-terminated
size_t len; ///< Editor length
size_t alloc; ///< Editor allocated
char prompt; ///< Prompt character
void (*on_changed) (void); ///< Callback on text change
void (*on_end) (bool); ///< Callback on abort
};
static void
line_editor_free (struct line_editor *self)
{
free (self->line);
free (self->w);
}
/// Notify whomever invoked the editor that it's been either confirmed or
/// cancelled and clean up editor state
static void
line_editor_abort (struct line_editor *self, bool status)
{
self->on_end (status);
self->on_changed = NULL;
free (self->line);
self->line = NULL;
free (self->w);
self->w = NULL;
self->alloc = 0;
self->len = 0;
self->point = 0;
self->prompt = 0;
}
/// Start the line editor; remember to fill in "change" and "end" callbacks
static void
line_editor_start (struct line_editor *self, char prompt)
{
self->alloc = 16;
self->line = xcalloc (sizeof *self->line, self->alloc);
self->w = xcalloc (sizeof *self->w, self->alloc);
self->len = 0;
self->point = 0;
self->prompt = prompt;
}
static void
line_editor_changed (struct line_editor *self)
{
self->line[self->len] = 0;
self->w[self->len] = 0;
if (self->on_changed)
self->on_changed ();
}
static void
line_editor_move (struct line_editor *self, int to, int from, int len)
{
memmove (self->line + to, self->line + from,
sizeof *self->line * len);
memmove (self->w + to, self->w + from,
sizeof *self->w * len);
}
static void
line_editor_insert (struct line_editor *self, ucs4_t codepoint)
{
while (self->alloc - self->len < 2 /* inserted + sentinel */)
{
self->alloc <<= 1;
self->line = xreallocarray
(self->line, sizeof *self->line, self->alloc);
self->w = xreallocarray
(self->w, sizeof *self->w, self->alloc);
}
line_editor_move (self, self->point + 1, self->point,
self->len - self->point);
self->line[self->point] = codepoint;
self->w[self->point] = app_is_character_in_locale (codepoint)
? uc_width (codepoint, locale_charset ())
: 1 /* the replacement question mark */;
self->point++;
self->len++;
line_editor_changed (self);
}
static bool
line_editor_action (struct line_editor *self, enum line_editor_action action)
{
switch (action)
{
default:
return soft_assert (!"unknown line editor action");
case LINE_EDITOR_B_CHAR:
if (self->point < 1)
return false;
do self->point--;
while (self->point > 0
&& !self->w[self->point]);
return true;
case LINE_EDITOR_F_CHAR:
if (self->point + 1 > (int) self->len)
return false;
do self->point++;
while (self->point < (int) self->len
&& !self->w[self->point]);
return true;
case LINE_EDITOR_B_WORD:
{
if (self->point < 1)
return false;
int i = self->point;
while (i && self->line[--i] == ' ');
while (i-- && self->line[i] != ' ');
self->point = ++i;
return true;
}
case LINE_EDITOR_F_WORD:
{
if (self->point + 1 > (int) self->len)
return false;
int i = self->point;
while (i < (int) self->len && self->line[i] != ' ') i++;
while (i < (int) self->len && self->line[i] == ' ') i++;
self->point = i;
return true;
}
case LINE_EDITOR_HOME:
self->point = 0;
return true;
case LINE_EDITOR_END:
self->point = self->len;
return true;
case LINE_EDITOR_B_DELETE:
{
if (self->point < 1)
return false;
int len = 1;
while (self->point - len > 0
&& !self->w[self->point - len])
len++;
line_editor_move (self, self->point - len, self->point,
self->len - self->point);
self->len -= len;
self->point -= len;
line_editor_changed (self);
return true;
}
case LINE_EDITOR_F_DELETE:
{
if (self->point + 1 > (int) self->len)
return false;
int len = 1;
while (self->point + len < (int) self->len
&& !self->w[self->point + len])
len++;
self->len -= len;
line_editor_move (self, self->point, self->point + len,
self->len - self->point);
line_editor_changed (self);
return true;
}
case LINE_EDITOR_B_KILL_WORD:
{
if (self->point < 1)
return false;
int i = self->point;
while (i && self->line[--i] == ' ');
while (i-- && self->line[i] != ' ');
i++;
line_editor_move (self, i, self->point, (self->len - self->point));
self->len -= self->point - i;
self->point = i;
line_editor_changed (self);
return true;
}
case LINE_EDITOR_B_KILL_LINE:
self->len -= self->point;
line_editor_move (self, 0, self->point, self->len);
self->point = 0;
line_editor_changed (self);
return true;
case LINE_EDITOR_F_KILL_LINE:
self->len = self->point;
line_editor_changed (self);
return true;
}
}
static int
line_editor_write (const struct line_editor *self, struct row_buffer *row,
int width, chtype attrs)
{
if (self->prompt)
{
hard_assert (self->prompt < 127);
row_buffer_append_c (row, self->prompt, attrs);
width--;
}
int following = 0;
for (size_t i = self->point; i < self->len; i++)
following += self->w[i];
int preceding = 0;
size_t start = self->point;
while (start && preceding < width / 2)
preceding += self->w[--start];
// There can be one extra space at the end of the line but this way we
// don't need to care about non-spacing marks following full-width chars
while (start && width - preceding - following > 2 /* widest char */)
preceding += self->w[--start];
// XXX: we should also show < > indicators for overflow but it'd probably
// considerably complicate this algorithm
for (; start < self->len; start++)
row_buffer_append_c (row, self->line[start], attrs);
return !!self->prompt + preceding;
}

View File

@ -1,8 +1,9 @@
#include "config.h"
NONE, Do nothing
QUIT, Quit
REDRAW, Redraw screen
ABORT, Abort
TAB_HELP, Switch to help tab
TAB_LAST, Switch to last tab
TAB_PREVIOUS, Switch to previous tab
@ -27,11 +28,11 @@ MPD_CONSUME, Toggle consume
MPD_UPDATE_DB, Update MPD database
MPD_COMMAND, Send raw command to MPD
.ifdef WITH_PULSE
#ifdef WITH_PULSE
PULSE_VOLUME_UP, Increase PulseAudio volume
PULSE_VOLUME_DOWN, Decrease PulseAudio volume
PULSE_MUTE, Toggle PulseAudio sink mute
.endif
#endif
CHOOSE, Choose item
DELETE, Delete item
@ -46,8 +47,6 @@ CENTER_CURSOR, Center the cursor
MOVE_UP, Move selection up
MOVE_DOWN, Move selection down
GOTO_PLAYING, Go to playing song
GOTO_TOP, Go to top
GOTO_BOTTOM, Go to bottom
GOTO_ITEM_PREVIOUS, Go to previous item
@ -68,10 +67,6 @@ EDITOR_F_WORD, Go forward a word
EDITOR_HOME, Go to start of line
EDITOR_END, Go to end of line
EDITOR_UPCASE_WORD, Convert word to uppercase
EDITOR_DOWNCASE_WORD, Convert word to lowercase
EDITOR_CAPITALIZE_WORD, Capitalize word
EDITOR_B_DELETE, Delete last character
EDITOR_F_DELETE, Delete next character
EDITOR_B_KILL_WORD, Delete last word

View File

@ -1,106 +0,0 @@
# nncmpp.actions.awk: produce C code for a list of user actions
#
# Copyright (c) 2022, Přemysl Eric Janouch <p@janouch.name>
# SPDX-License-Identifier: 0BSD
#
# Usage: env LC_ALL=C A=0 B=1 awk -f nncmpp.actions.awk \
# nncmpp.actions > nncmpp-actions.h
# --- Preprocessor -------------------------------------------------------------
function fatal(message) {
print "// " FILENAME ":" FNR ": fatal error: " message
print FILENAME ":" FNR ": fatal error: " message > "/dev/stderr"
exit 1
}
function condition(pass, passing, a, i) {
split(substr($0, RSTART + RLENGTH), a, /[[:space:]]+/)
if (!(1 in a))
fatal("missing condition")
passing = 0
for (i in a)
if (a[i] && !pass == !ENVIRON[a[i]])
passing = 1
while (getline > 0) {
if (match($0, /^[[:space:]]*[.]endif[[:space:]]*$/))
return 1
if (match($0, /^[[:space:]]*[.]else[[:space:]]*$/))
passing = !passing
else if (!directive() && passing)
process()
}
fatal("unterminated condition body")
}
# Multiple arguments mean logical OR, multiple directives mean logical AND.
# Similar syntax is also used by Exim, BSD make, or various assemblers.
#
# Looking at what others have picked for their preprocessor syntax:
# {OpenGL, FreeBASIC} reuse #ifdef, which would be confusing with C code around,
# {Mental Ray, RapidQ and UniVerse BASIC} use $ifdef, NSIS has !ifdef,
# and Verilog went for `ifdef. Not much more can be easily found.
function directive() {
sub(/#.*/, "")
if (match($0, /^[[:space:]]*[.]ifdef[[:space:]]+/))
return condition(1)
if (match($0, /^[[:space:]]*[.]ifndef[[:space:]]+/))
return condition(0)
if (/^[[:space:]]*[.]/)
fatal("unexpected or unsupported directive")
return 0
}
!directive() {
process()
}
# --- Postprocessor ------------------------------------------------------------
function strip(string) {
gsub(/^[[:space:]]*|[[:space:]]*$/, "", string)
return string
}
function process( constant, name, description) {
if (match($0, /,/)) {
constant = name = strip(substr($0, 1, RSTART - 1))
description = strip(substr($0, RSTART + RLENGTH))
gsub(/_/, "-", name)
N++
Constants[N] = constant
Names[N] = tolower(name)
Descriptions[N] = description
} else if (/[^[:space:]]/) {
fatal("invalid action definition syntax")
}
}
function tocstring(string) {
gsub(/\\/, "\\\\", string)
gsub(/"/, "\\\"", string)
return "\"" string "\""
}
END {
print "enum action {"
for (i in Constants)
print "\t" "ACTION_" Constants[i] ","
print "\t" "ACTION_COUNT"
print "};"
print ""
print "static const char *g_action_names[] = {"
for (i in Names)
print "\t" tocstring(Names[i]) ","
print "};"
print ""
print "static const char *g_action_descriptions[] = {"
for (i in Descriptions)
print "\t" tocstring(Descriptions[i]) ","
print "};"
}

View File

@ -69,7 +69,7 @@ colors = {
scrollbar = ""
}
streams = {
"dnbradio.com" = "https://dnbradio.com/hi.pls"
"dnbradio.com" = "http://www.dnbradio.com/hi.m3u"
"BassDrive.com" = "http://bassdrive.com/v2/streams/BassDrive.pls"
}
....
@ -81,10 +81,6 @@ The distribution contains example colour schemes in the _contrib_ directory.
// TODO: it seems like liberty should contain an includable snippet about
// the format, which could form a part of nncmpp.conf(5).
To adjust key bindings, put them within a *normal* or *editor* object.
Run *nncmpp* with the *--debug* option to find out key combinations names.
Press *?* in the help tab to learn the action identifiers to use.
Spectrum visualiser
-------------------
When built against the FFTW library, *nncmpp* can make use of MPD's "fifo"
@ -117,8 +113,8 @@ as in the snippet above. To replace the default volume control bindings, use:
....
normal = {
"+" = "pulse-volume-up"
"-" = "pulse-volume-down"
"M-PageUp" = "pulse-volume-up"
"M-PageDown" = "pulse-volume-down"
}
....
@ -128,19 +124,6 @@ For this to work, *nncmpp* needs to access the right PulseAudio daemon--in case
your setup is unusual, consult the list of environment variables in
*pulseaudio*(1). MPD-compatibles are currently unsupported.
Info plugins
------------
You can invoke various plugins from the Info tab, for example to look up
song lyrics.
Plugins can be arbitrary scripts or binaries. When run without command line
arguments, a plugin outputs a user interface description of what it does.
When invoked by a user, it receives the following self-explanatory arguments:
_TITLE_ _ARTIST_ [_ALBUM_], and anything it writes to its standard output
or standard error stream is presented back to the user. Here, bold and italic
formatting can be toggled with ASCII control characters 1 (SOH) and 2 (STX),
respectively. Otherwise, all input and output makes use of the UTF-8 encoding.
Files
-----
*nncmpp* follows the XDG Base Directory Specification.
@ -148,14 +131,6 @@ Files
_~/.config/nncmpp/nncmpp.conf_::
The configuration file.
_~/.local/share/nncmpp/info/_::
_/usr/local/share/nncmpp/info/_::
_/usr/share/nncmpp/info/_::
Info plugins are loaded from these directories, in order,
then listed lexicographically.
Only the first occurence of a particular filename is used,
and empty files act as silent disablers.
Reporting bugs
--------------
Use https://git.janouch.name/p/nncmpp to report bugs, request features,

2424
nncmpp.c

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +0,0 @@
[Desktop Entry]
Type=Application
Name=nncmpp
GenericName=MPD client
Icon=nncmpp
Exec=nncmpp %U
StartupNotify=false
# Not registering a MimeType, because that depends on MPD.
Categories=AudioVideo;Audio;Player;

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg version="1.0" width="48" height="48" viewBox="0 0 48 48"
xmlns="http://www.w3.org/2000/svg">
<g transform="translate(5 4) scale(2 2)">
<!-- From x11_icon_play, with a stroke for contrast. -->
<path d="M 0 0 20 10 0 20 Z" stroke="#eee" stroke-width="2" fill="#000"
stroke-linejoin="round" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 385 B

2
termo

@ -1 +1 @@
Subproject commit 2518b53e5ae4579bf84ed58fa7a62806f64e861c
Subproject commit 8265f075b176b33680012094aa1ced5721e55ac9