Compare commits

...

128 Commits

Author SHA1 Message Date
Přemysl Eric Janouch e6de4f11e7
Be actually able to use a system Termo library
Alpine 3.19 Success Details
Arch Linux AUR Success Details
OpenBSD 7.3 Success Details
2024-04-10 17:31:40 +02:00
Přemysl Eric Janouch c4bce75866
Bump version, update NEWS
Arch Linux AUR Success Details
Alpine 3.19 Success Details
2024-02-27 00:39:23 +01:00
Přemysl Eric Janouch 08b87bccbd
Bump liberty, fix a dead link 2024-02-27 00:30:39 +01:00
Přemysl Eric Janouch fa460b97cf
Fix info plugins installation
They could never become executable.
2024-02-25 05:15:00 +01:00
Přemysl Eric Janouch ef19337bad
Bump version, update NEWS 2024-02-10 18:39:57 +01:00
Přemysl Eric Janouch 9699b80e9d
Very mildly improve stream handling 2024-02-10 18:35:10 +01:00
Přemysl Eric Janouch 7601a754af
Bump liberty 2024-02-10 12:50:39 +01:00
Přemysl Eric Janouch 31d975604b
README.adoc: remove unintended line break 2024-02-10 10:21:09 +01:00
Přemysl Eric Janouch 59f82b7a72
Bump liberty, set the window icon 2024-02-10 10:09:32 +01:00
Přemysl Eric Janouch 8bcdb0afd5
Bump liberty, install rasterized icons 2024-02-10 06:04:39 +01:00
Přemysl Eric Janouch 9dfd89ef06
CMakeLists.txt: declare compatibility with 3.27
Sadly, the 3.5 deprecation warning doesn't go away after this.
2023-08-01 03:01:06 +02:00
Přemysl Eric Janouch 4b592ec295
Fix build in bare configurations 2023-08-01 02:37:48 +02:00
Přemysl Eric Janouch 58eb7edfd5
Make the scroll wheel act on the gauge and volume 2023-07-23 15:51:22 +02:00
Přemysl Eric Janouch 48fc9bdb19
Add vertical padding to the status bar as well
For symmetry, if for nothing else.
2023-07-23 15:36:37 +02:00
Přemysl Eric Janouch 9ab5ab6928
Change volume in finer steps 2023-07-20 13:20:34 +02:00
Přemysl Eric Janouch 93e0d7027a
Fix build on systems without A_ITALIC
Unfortunately, this font style doesn't work in X11 either in that case.
2023-07-04 06:45:33 +02:00
Přemysl Eric Janouch 5900b0708a
README.adoc: update package information 2023-07-01 22:00:34 +02:00
Přemysl Eric Janouch 5f97b95026
Bump liberty 2023-06-28 16:27:05 +02:00
Přemysl Eric Janouch b3fe85995f
Dissolve widget_redistribute()
liberty-xui has xui_hbox() and xui_vbox(),
the way we use widgets is odd.
2023-06-19 13:44:53 +02:00
Přemysl Eric Janouch 19a0a468a9
Bump liberty, move the UI to liberty-xui.c
Also bump termo.

This deduplicates code between nncmpp and hex,
and adds support for bold + italic attributes under X11.

There is still a lot of space for prettification.
Unfortunately, most of our specialized widgets are quite entangled.
2023-06-19 13:38:24 +02:00
Přemysl Eric Janouch 016469d5a8
Cleanup 2023-06-18 16:12:35 +02:00
Přemysl Eric Janouch 6bd40f3ec1
Fix potential issue with struct padding
The UI could very theoretically end up textless.
2023-06-18 13:01:14 +02:00
Přemysl Eric Janouch eb925f18c8
Fix CMake option description 2023-06-17 21:50:46 +02:00
Přemysl Eric Janouch 0c35cc301d
Cleanup 2023-06-17 21:50:46 +02:00
Přemysl Eric Janouch 89e6826586
Update NEWS 2023-06-15 16:35:26 +02:00
Přemysl Eric Janouch 4166b17b00
Add an icon and a desktop file for the GUI 2023-06-15 16:25:01 +02:00
Přemysl Eric Janouch d0d248e44c
Fix crash when scrolling too far up 2023-06-15 14:45:26 +02:00
Přemysl Eric Janouch ebda305e2d
Change default active tab bar attributes
It mostly just looked weird when the underline wasn't on the last row.
2023-06-15 14:38:47 +02:00
Přemysl Eric Janouch 5d5f73f22f
Don't layout during rendering 2023-06-15 12:35:43 +02:00
Přemysl Eric Janouch 4a9e621d92
Slightly clean up layouting 2023-06-15 11:07:47 +02:00
Přemysl Eric Janouch 9cd511a2e2
Implement font fallbacks
The editor doesn't support this so far, and it could be faster.
2023-06-10 21:28:49 +02:00
Přemysl Eric Janouch b11f5d0e3c
Factor out app_widget_by_id() 2023-06-09 17:44:43 +02:00
Přemysl Eric Janouch 13cf0da8c4
Fix build without optional dependencies 2023-06-09 17:44:43 +02:00
Přemysl Eric Janouch f05be01fba
Change volume adjustment key bindings
Use the much more obvious ones.
2023-05-30 15:17:04 +02:00
Přemysl Eric Janouch 14dba91dd1
Add a go-to-playing action and binding 2023-05-30 14:41:44 +02:00
Přemysl Eric Janouch dcb2829e9b
Separate the actions of aborting and quitting
The user should not be afraid of pressing Escape too many times.
2023-03-25 11:39:34 +01:00
Přemysl Eric Janouch 349c907cbf
X11: act on DestroyNotify rather than UnmapNotify
This makes the program survive i3 restarts, which cause a sequence
of: UnmapNotify, ReparentNotify, MapNotify.
2023-03-25 11:10:26 +01:00
Přemysl Eric Janouch 0b62b2a788
Update NEWS 2023-03-07 01:56:26 +01:00
Přemysl Eric Janouch d58856571d
Improve display of files lacking proper metadata 2023-03-07 01:53:50 +01:00
Přemysl Eric Janouch 61fac878ad
X11: fix rendering of overflowing last list items 2022-10-30 18:49:10 +01:00
Přemysl Eric Janouch da83dbee1f
Bump liberty 2022-10-09 01:10:50 +02:00
Přemysl Eric Janouch 41fda4e317
Bump liberty, improve fallback manual page output 2022-09-30 18:22:28 +02:00
Přemysl Eric Janouch d4d2259825
Bump liberty, make use of its new asciiman.awk 2022-09-25 21:14:36 +02:00
Přemysl Eric Janouch 568abc896c
10-azlyrics.pl: fix "the" stripping 2022-09-20 12:45:45 +02:00
Přemysl Eric Janouch 8aac4ae0a8
Update documentation 2022-09-20 12:24:00 +02:00
Přemysl Eric Janouch e72ed71f53
X11: support italic fonts as well
The bold + italic combination isn't supported thus far,
because it seems unnecessary.
2022-09-20 11:15:20 +02:00
Přemysl Eric Janouch 28ed7a85a8
Implement lyrics lookup
There is now a generic mechanism for loading lyrics,
or any other arbitrary content related to songs.
2022-09-20 11:04:39 +02:00
Přemysl Eric Janouch b6dd940720
Implement M-u, M-l, M-c from Readline 2022-09-18 04:24:58 +02:00
Přemysl Eric Janouch d8e0d1b2fe
Make M-f behave like it does in Readline 2022-09-18 01:07:47 +02:00
Přemysl Eric Janouch 5cda848f94
Don't depend on a standalone C preprocessor
And get rid of the sed insanity.
2022-09-13 01:01:35 +02:00
Přemysl Eric Janouch a167ae40b3
Document configuration file key binding 2022-09-12 20:02:57 +02:00
Přemysl Eric Janouch b4222365c3
Bump version 2022-09-03 15:13:07 +02:00
Přemysl Eric Janouch b5e48c29f9
Put connecting/disconnected messages in status bar
Those are general status messages, and seem to belong to the bottom.
Partially motivated by the status bar being empty when disconnected.

And add a missing window invalidation.
2022-09-03 15:04:30 +02:00
Přemysl Eric Janouch eaa19be1c8
Fix Clang build 2022-09-03 14:39:47 +02:00
Přemysl Eric Janouch dad95ef444
X11: render partially visible list items 2022-09-03 13:23:07 +02:00
Přemysl Eric Janouch 7e74d1a80a
X11: make the scrollbar span the full height 2022-09-03 13:02:47 +02:00
Přemysl Eric Janouch 8529f24a46
Bind Tab and S-Tab to tab switching 2022-08-27 17:35:17 +02:00
Přemysl Eric Janouch 190e813d49
X11: fix Ctrl+Space 2022-08-27 17:32:12 +02:00
Přemysl Eric Janouch 9af74259d2
Add debugging facilities for input events 2022-08-27 17:20:06 +02:00
Přemysl Eric Janouch 9a996c8440
Adjust the "action unavailable" message 2022-08-25 07:45:49 +02:00
Přemysl Eric Janouch a9d4f86493
Add an action to describe items
And improve the display of messages, separating any prefix out.
2022-08-25 07:42:10 +02:00
Přemysl Eric Janouch 172ceffa9e
X11: support copying text to CLIPBOARD
Use the right mouse button.
2022-08-25 06:14:08 +02:00
Přemysl Eric Janouch 6dad74f3c9
Enable configuring spectrum analyzer FPS 2022-08-25 02:31:32 +02:00
Přemysl Eric Janouch b196ef4f08
X11: mildly optimize drawing
There is no real performance impact on modern systems.
2022-08-25 02:16:36 +02:00
Přemysl Eric Janouch d82be07807
X11: handle Shift+clicks in the list 2022-08-25 00:25:03 +02:00
Přemysl Eric Janouch 2d219f1a4b
Rework mouse event processing
X11's triple-click bug is gone, and we may pass modifier state.
2022-08-25 00:10:17 +02:00
Přemysl Eric Janouch a77d872e7f
Try to stuff more things in the status bar 2022-08-24 23:21:16 +02:00
Přemysl Eric Janouch 179e0a123b
X11: ring the bell as well, when appropriate 2022-08-24 09:35:28 +02:00
Přemysl Eric Janouch da14bdbd39
Improve help tab item choice error messages 2022-08-24 09:05:28 +02:00
Přemysl Eric Janouch 24e57872d8
Hide messages when interacting with the editor 2022-08-24 09:03:40 +02:00
Přemysl Eric Janouch a20e4c74d8
Implement mouse drags on the gauge and scrollbar 2022-08-24 08:23:54 +02:00
Přemysl Eric Janouch d33c17b888
Click to close messages
While not ideal, it is at least some way to discard them.
2022-08-24 07:41:03 +02:00
Přemysl Eric Janouch 6033f6a869
Notify when actions can't be taken 2022-08-24 07:26:47 +02:00
Přemysl Eric Janouch 88e86724c3
Always show messages to the user 2022-08-24 07:25:21 +02:00
Přemysl Eric Janouch 92c1bf783f
Build with AsciiDoc as well as Asciidoctor 2022-08-23 22:32:45 +02:00
Přemysl Eric Janouch 435485d145
X11: remark that triple clicks are mishandled 2022-08-23 12:59:59 +02:00
Přemysl Eric Janouch d876bbfcd3
Update README and the screenshot 2022-08-23 11:06:50 +02:00
Přemysl Eric Janouch 1c37b15253
X11: improve rendering of denormalized text
Xft is dumb.
2022-08-23 10:35:28 +02:00
Přemysl Eric Janouch 2803a8153b
X11: clean up after Xft on exit 2022-08-23 09:00:20 +02:00
Přemysl Eric Janouch f1ab0e2d8a
X11: use input methods, abandon xkbcommon
And fix a redundant XCreatePixmap() call leaking resources.
2022-08-23 08:42:38 +02:00
Přemysl Eric Janouch 830a744a11
Add an X11 user interface
This is meant to exactly mimic the terminal UI, just without
the restriction to ugly monospace fonts.

The Curses implementation has been reworked, and may have become
more computationally expensive.  In general, though, the codebase
has been significantly cleaned up.
2022-08-23 02:50:24 +02:00
Přemysl Eric Janouch 8260842aef
Fix event processing with PulseAudio integration 2022-08-19 23:48:33 +02:00
Přemysl Eric Janouch fe017b4daa
Mark an invalid assumption 2022-08-18 19:56:47 +02:00
Přemysl Eric Janouch f3c4cec24a
Add and bind an action to center the cursor
"z" stands for VIM's "zz".
2021-12-23 20:36:00 +01:00
Přemysl Eric Janouch 410136a647
Fix up coding style inconsistency 2021-12-21 06:26:53 +01:00
Přemysl Eric Janouch 61a141203b
Bump liberty and version, update NEWS 2021-12-21 05:46:28 +01:00
Přemysl Eric Janouch 48482ef2e5
Make incremental search more useful
Make it unanchored, as well as case-insensitive.
2021-12-21 05:46:22 +01:00
Přemysl Eric Janouch 840c69767c
Prepare a NEWS entry for the next release 2021-12-08 19:05:58 +01:00
Přemysl Eric Janouch a14a907b18
Indicate that a stream download is in progress 2021-12-08 18:58:03 +01:00
Přemysl Eric Janouch 333049de01
Do not run cURL fully synchronously
The improvement is very minor in character.
2021-12-08 18:23:30 +01:00
Přemysl Eric Janouch 4e3596db35
Add rudimentary incremental search facility 2021-12-08 17:23:25 +01:00
Přemysl Eric Janouch 5aa07fd8af
Clean up mpd_process_info() better 2021-12-07 20:38:02 +01:00
Přemysl Eric Janouch 2060da4a8e
Do not jump to beginning after unqueueing
Instead, assume that the whole previously selected range
has been removed, and try to go after or before it accordingly.
2021-12-07 20:34:32 +01:00
Přemysl Eric Janouch f5b5cec340
Clean up unreadable code 2021-12-07 20:10:35 +01:00
Přemysl Eric Janouch 1a671dfad5
Document PulseAudio integration 2021-11-16 05:17:15 +01:00
Přemysl Eric Janouch 587a02fa15
Indent man page snippets with spaces 2021-11-16 05:16:51 +01:00
Přemysl Eric Janouch 227b8e0fa2
Do not show both volumes if unnecessary
Also, make it apparent which value comes from where.
2021-11-16 04:48:52 +01:00
Přemysl Eric Janouch e66e9f249a
Rename an action to be shorter
Also, fix make dependencies to include the source file for actions.
2021-11-16 04:48:52 +01:00
Přemysl Eric Janouch 32203f8117
Fix the comment for settings.pulseaudio 2021-11-08 07:23:08 +01:00
Přemysl Eric Janouch 6b871898d8
Fix build on macOS and other non-GNU systems 2021-11-08 06:36:01 +01:00
Přemysl Eric Janouch 4598c45d2f
Generate actions from a text file
Mostly because I wanted to nest preprocessing.

This makes the build more complex and slightly less portable,
but the code itself is much cleaner.
2021-11-08 06:07:04 +01:00
Přemysl Eric Janouch 66c77c3f8d
Update README 2021-11-07 23:21:32 +01:00
Přemysl Eric Janouch 7165a8eb02
Add ability to control PulseAudio volume
I know, son, it might be hard to accept,
but you're imported.  Your true parents are wmstatus
and paswitch, from the desktop-tools family.

Also, fix unnecessary linking of optional dependencies.
2021-11-07 23:07:55 +01:00
Přemysl Eric Janouch 87b57bb24c
Add a comment about the music directory 2021-11-07 13:29:13 +01:00
Přemysl Eric Janouch ba86961ba5
Bump version, update NEWS 2021-11-04 14:19:50 +01:00
Přemysl Eric Janouch 0cdb4989e5
Bump termo 2021-11-04 14:16:25 +01:00
Přemysl Eric Janouch 6de940fe96
Do not beep on focus changes 2021-11-04 13:24:15 +01:00
Přemysl Eric Janouch 6bd8c1db2f
CMakeLists.txt: fix macOS build 2021-11-02 17:17:32 +01:00
Přemysl Eric Janouch 56efe9c6a9
Update .gitignore 2021-10-30 03:35:49 +02:00
Přemysl Eric Janouch 8a17e674f8
CMakeLists.txt: clean up 2021-10-30 03:02:00 +02:00
Přemysl Eric Janouch bd0ee66c19
Add clang-format configuration 2021-10-30 03:02:00 +02:00
Přemysl Eric Janouch 6f6efe077b
CMakeLists.txt: synchronize with sdtui 2021-10-27 19:48:47 +02:00
Přemysl Eric Janouch ee5c41b2bf
README: update dependencies 2021-10-21 09:21:08 +02:00
Přemysl Eric Janouch 9a67e076a9
Bump version, update NEWS 2021-10-21 09:16:51 +02:00
Přemysl Eric Janouch 53fbb3dec1
Fix the line editor/spectrum analyser interaction
The updater assumed the terminal cursor was invisible.
2021-10-21 09:13:07 +02:00
Přemysl Eric Janouch 267598643a
Add program arguments to MPD's current playlist
I was tired of using `mpv --no-video`, this is a bit better.

It's all rather quirky, but very little code is involved.

I've added a few related TODO entries.
2021-09-07 06:35:24 +02:00
Přemysl Eric Janouch fba1210e9f
Clean up connection initialisation
Also, do not set up the spectrum visualiser before a password is sent.

It would look a bit weird to have it run but display "Disconnected",
even though technically, it would probably work.
2021-09-06 21:48:27 +02:00
Přemysl Eric Janouch 30777e8fd3
Improve terminal initialisation
Don't just abort() on failures, print a proper error message.

Also, set up ncurses as late as possible.  This should be alright wrt.
signal handlers according to ncurses code, as well as XSI:

> Curses implementations may provide for special handling of
> the SIGINT, SIGQUIT and SIGTSTP signals if their disposition
> is SIG_DFL at the time initscr is called ...

termo blocks job control, so SIGTSTP is not a concern at all.
2021-09-06 21:30:03 +02:00
Přemysl Eric Janouch 353174ee3c
Spetrum analyser: expand my favourite comment 2021-07-09 20:08:53 +02:00
Přemysl Eric Janouch 2d641d087f
Spectrum analyser: add some useful comments 2021-07-09 06:25:48 +02:00
Přemysl Eric Janouch 20c8385f2e
Spectrum analyser: optimise the x:16:2 case
nncmpp CPU usage went from 2 to 1.7 percent, a 15% improvement.

Sort of worth it, given that it's a constant load.

The assembly certainly looks nicer.
2021-07-08 19:14:26 +02:00
Přemysl Eric Janouch fa4443a3ce
Rectify an obsolete comment 2021-07-08 04:33:03 +02:00
Přemysl Eric Janouch 14ba637d4b
Expand the last comment once again 2021-07-08 04:32:53 +02:00
Přemysl Eric Janouch 66bc3f1c2c
Expand the comment on spectrum frequency filtering 2021-07-05 23:42:51 +02:00
Přemysl Eric Janouch 0646cea126
Silence a compiler warning
The statement can be eliminated, then it suggests braces.
2021-07-05 01:26:16 +02:00
Přemysl Eric Janouch a439a56ee9
Add an optional spectrum visualiser
This is really more of a demo.  It's doable, just rather ugly.

It would deserve some further tuning, if anyone cared enough.
2021-07-05 01:10:46 +02:00
Přemysl Eric Janouch 120a11ca1b
Update a comment about mouse modes
We might even depend on termo now more than is stated.
2021-07-04 10:23:37 +02:00
Přemysl Eric Janouch 7e531e95c5
Process focus events
Should help prevent accidents in other windows.
2021-06-29 05:28:54 +02:00
19 changed files with 3686 additions and 1279 deletions

32
.clang-format Normal file
View File

@ -0,0 +1,32 @@
# clang-format is fairly limited, and these rules are approximate:
# - array initializers can get terribly mangled with clang-format 12.0,
# - sometimes it still aligns with space characters,
# - struct name NL { NL ... NL } NL name; is unachievable.
BasedOnStyle: GNU
ColumnLimit: 80
IndentWidth: 4
TabWidth: 4
UseTab: ForContinuationAndIndentation
BreakBeforeBraces: Allman
SpaceAfterCStyleCast: true
AlignAfterOpenBracket: DontAlign
AlignOperands: DontAlign
AlignConsecutiveMacros: Consecutive
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
IndentGotoLabels: false
# IncludeCategories has some potential, but it may also break the build.
# Note that the documentation says the value should be "Never".
SortIncludes: false
# This is a compromise, it generally works out aesthetically better.
BinPackArguments: false
# Unfortunately, this can't be told to align to column 40 or so.
SpacesBeforeTrailingComments: 2
# liberty-specific macro body wrappers.
MacroBlockBegin: "BLOCK_START"
MacroBlockEnd: "BLOCK_END"
ForEachMacros: ["LIST_FOR_EACH"]

2
.gitignore vendored
View File

@ -7,3 +7,5 @@
/nncmpp.files
/nncmpp.creator*
/nncmpp.includes
/nncmpp.cflags
/nncmpp.cxxflags

View File

@ -1,5 +1,5 @@
cmake_minimum_required (VERSION 3.0)
project (nncmpp VERSION 1.0.0 LANGUAGES C)
cmake_minimum_required (VERSION 3.0...3.27)
project (nncmpp VERSION 2.1.1 LANGUAGES C)
# Moar warnings
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU" OR CMAKE_COMPILER_IS_GNUCC)
@ -10,6 +10,14 @@ 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)
@ -19,73 +27,167 @@ pkg_check_modules (curl REQUIRED libcurl)
include (AddThreads)
find_package (Termo QUIET NO_MODULE)
option (USE_SYSTEM_TERMO
"Don't compile our own termo library, use the system one" ${Termo_FOUND})
add_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")
endif (NOT Termo_FOUND)
endif ()
else ()
# We don't want the library to install, but EXCLUDE_FROM_ALL ignores tests
add_subdirectory (termo EXCLUDE_FROM_ALL)
# We don't have many good choices when we don't want to install it and want
# to support older versions of CMake; this is a relatively clean approach
# (other possibilities: setting a variable in the parent scope, using a
# cache variable, writing a special config file with build paths in it and
# including it here, or setting a custom property on the targets).
file (WRITE ${PROJECT_BINARY_DIR}/CTestCustom.cmake
"execute_process (COMMAND ${CMAKE_COMMAND} --build termo)")
# We don't have many good choices; this is a relatively clean approach
# (other possibilities: setting a variable in the parent scope, using
# a cache variable, writing a special config file with build paths in it
# and including it here, or setting a custom property on the targets)
get_directory_property (Termo_INCLUDE_DIRS
DIRECTORY termo INCLUDE_DIRECTORIES)
set (Termo_LIBRARIES termo-static)
endif ()
pkg_check_modules (fftw fftw3 fftw3f)
add_option (WITH_FFTW
"Use FFTW to enable spectrum visualisation" "${fftw_FOUND}")
if (WITH_FFTW)
if (NOT fftw_FOUND)
message (FATAL_ERROR "FFTW not found")
endif ()
list (APPEND extra_libraries ${fftw_LIBRARIES})
endif ()
pkg_check_modules (libpulse libpulse)
add_option (WITH_PULSE
"Enable PulseAudio sink volume control" "${libpulse_FOUND}")
if (WITH_PULSE)
if (NOT libpulse_FOUND)
message (FATAL_ERROR "libpulse not found")
endif ()
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}")
if (WITH_X11)
if (NOT x11_FOUND)
message (FATAL_ERROR "Some X11 libraries were not found")
endif ()
list (APPEND extra_libraries ${x11_LIBRARIES})
endif ()
include_directories (${Unistring_INCLUDE_DIRS}
${Ncursesw_INCLUDE_DIRS} ${Termo_INCLUDE_DIRS} ${curl_INCLUDE_DIRS})
link_directories (${curl_LIBRARY_DIRS})
${Ncursesw_INCLUDE_DIRS} ${Termo_INCLUDE_DIRS} ${curl_INCLUDE_DIRS}
${fftw_INCLUDE_DIRS} ${libpulse_INCLUDE_DIRS} ${x11_INCLUDE_DIRS})
link_directories (${curl_LIBRARY_DIRS}
${fftw_LIBRARY_DIRS} ${libpulse_LIBRARY_DIRS} ${x11_LIBRARY_DIRS})
# Configuration
include (CheckFunctionExists)
set (CMAKE_REQUIRED_LIBRARIES ${Ncursesw_LIBRARIES})
CHECK_FUNCTION_EXISTS ("resizeterm" HAVE_RESIZETERM)
if ("${CMAKE_SYSTEM_NAME}" MATCHES "BSD")
# Need this for SIGWINCH in FreeBSD and OpenBSD respectively;
# our POSIX version macros make it undefined
add_definitions (-D__BSD_VISIBLE=1 -D_BSD_SOURCE=1)
elseif (APPLE)
add_definitions (-D_DARWIN_C_SOURCE)
endif ()
include (CheckFunctionExists)
set (CMAKE_REQUIRED_LIBRARIES ${Ncursesw_LIBRARIES})
CHECK_FUNCTION_EXISTS ("resizeterm" HAVE_RESIZETERM)
# -lm may or may not be a part of libc
foreach (extra m)
find_library (extra_lib_${extra} ${extra})
if (extra_lib_${extra})
list (APPEND extra_libraries ${extra_lib_${extra}})
endif ()
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})
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)
# Build the main executable and link it
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c)
add_executable (${PROJECT_NAME} ${PROJECT_NAME}.c ${actions})
target_link_libraries (${PROJECT_NAME} ${Unistring_LIBRARIES}
${Ncursesw_LIBRARIES} termo-static ${curl_LIBRARIES})
${Ncursesw_LIBRARIES} ${Termo_LIBRARIES} ${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)
if (NOT ASCIIDOCTOR_EXECUTABLE)
message (FATAL_ERROR "asciidoctor not found")
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")
endif ()
foreach (page ${PROJECT_NAME})
set (page_output "${PROJECT_BINARY_DIR}/${page}.1")
list (APPEND project_MAN_PAGES "${page_output}")
add_custom_command (OUTPUT ${page_output}
COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
-a release-version=${PROJECT_VERSION}
"${PROJECT_SOURCE_DIR}/${page}.adoc"
-o "${page_output}"
DEPENDS ${page}.adoc
COMMENT "Generating man page for ${page}" VERBATIM)
if (ASCIIDOCTOR_EXECUTABLE)
add_custom_command (OUTPUT ${page_output}
COMMAND ${ASCIIDOCTOR_EXECUTABLE} -b manpage
-a release-version=${PROJECT_VERSION}
-o "${page_output}"
"${PROJECT_SOURCE_DIR}/${page}.adoc"
DEPENDS ${page}.adoc
COMMENT "Generating man page for ${page}" VERBATIM)
elseif (A2X_EXECUTABLE)
add_custom_command (OUTPUT ${page_output}
COMMAND ${A2X_EXECUTABLE} --doctype manpage --format manpage
-a release-version=${PROJECT_VERSION}
-D "${PROJECT_BINARY_DIR}"
"${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 ()
add_custom_target (docs ALL DEPENDS ${project_MAN_PAGES})
@ -96,8 +198,35 @@ 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 "MPD client")
set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Terminal/X11 MPD client")
set (CPACK_PACKAGE_VENDOR "Premysl Eric Janouch")
set (CPACK_PACKAGE_CONTACT "Přemysl Eric Janouch <p@janouch.name>")
set (CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE")

View File

@ -1,4 +1,4 @@
Copyright (c) 2016 - 2020, Přemysl Eric Janouch <p@janouch.name>
Copyright (c) 2016 - 2023, 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.

84
NEWS
View File

@ -1,3 +1,87 @@
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
* Implemented mouse drags on the elapsed time gauge and the scrollbar
* Added Tab and S-Tab bindings to iterate tabs
* Added a "z" binding to center the view on the selected item
* Added a "?" binding to describe items in various tabs
* Made it possible to adjust the spectrum analyzer's FPS limit
* Moved "Disconnected" and "Connecting..." messages to the status bar
* Fixed possibility of connection timeouts with PulseAudio integration
1.2.0 (2021-12-21)
* Added ability to control the volume of MPD's current PulseAudio sink
* Now fetching Internet stream information asynchronously
* Added basic incremental search, normally bound to C-s, in all tabs
* Fixed jumping to the beginning of the queue after deleting items
1.1.1 (2021-11-04)
* Terminal focus in/out events no longer ring the terminall bell
* Made mouse work in non-rxvt terminals with recent xterm terminfo
1.1.0 (2021-10-21)
* Now requesting and processing terminal de/focus events,
using a new "defocused" attribute for selected rows
* Made it possible to show a spectrum visualiser when built against FFTW
* Any program arguments are now added to MPD's current playlist
1.0.0 (2020-11-05)
* Coming with a real manual page instead of a help2man-generated stub

View File

@ -1,26 +1,37 @@
nncmpp
======
'nncmpp' is yet another MPD client. It is in effect a simplified TUI version
of Sonata. I had already written a lot of the required code before, so I had
the perfect opportunity to get rid of the unmaintained Python application and
make the first TUI client that doesn't feel awkward to use.
'nncmpp' is yet another MPD client. Its specialty is running equally well in
the terminal, or as an X11 client--it will provide the same keyboard- and
mouse-friendly interface.
This project began its life as a simplified TUI version of Sonata. I had
already written a lot of the required code before, so I had the perfect
opportunity to get rid of the unmaintained Python application, and to make
the first TUI client that doesn't feel awkward to use.
If it's not obvious enough, the name is a pun on all those ridiculous client
names, and should be pronounced as "nincompoop".
Features
--------
Most things are there. Enough for me to use it exclusively. Note that since I
only use the filesystem browsing mode, that's also the only thing I care to
implement for the time being.
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,
and the search feature is known to be clumsy.
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 from Archlinux's AUR.
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].
Documentation
-------------
@ -29,9 +40,12 @@ The rest of this README will concern itself with externalities.
Building
--------
Build dependencies: CMake, pkg-config, asciidoctor, liberty (included),
termo (included) +
Runtime dependencies: ncursesw, libunistring, cURL
Build-only dependencies: CMake, pkg-config, awk, liberty (included),
termo (included), asciidoctor or asciidoc (recommended but optional),
rsvg-convert (X11) +
Runtime dependencies: ncursesw, libunistring, cURL +
Optional runtime dependencies: fftw3, libpulse, x11 + xft + libpng (X11),
Perl + cURL (lyrics)
$ git clone --recursive https://git.janouch.name/p/nncmpp.git
$ mkdir nncmpp/build
@ -48,16 +62,19 @@ Or you can try telling CMake to make a package for you. For Debian it is:
$ cpack -G DEB
# dpkg -i nncmpp-*.deb
Terminal caveats
----------------
This application aspires to be as close to a GUI as possible. It expects you
to use the mouse (though it's not required). Terminals are, however, somewhat
tricky to get consistent results on, so be aware of the following:
User interface caveats
----------------------
The ncurses interface aspires to be as close to a GUI as possible. Don't shy
away from using your mouse (though keyboard is also fine). Terminals are,
however, tricky to get consistent results on, so be aware of the following:
- use a UTF-8 locale to get finer resolution progress bars and scrollbars
- Xterm needs `XTerm*metaSendsEscape: true` for the default bindings to work
- urxvt's 'vtwheel' plugin sabotages scrolling
The X11 graphical interface is a second-class citizen, so some limitations of
terminals carry over, such as the plain default theme.
Contributing and Support
------------------------
Use https://git.janouch.name/p/nncmpp to report any bugs, request features,

View File

@ -1,10 +1,15 @@
#ifndef CONFIG_H
#define CONFIG_H
#define PROGRAM_NAME "${CMAKE_PROJECT_NAME}"
#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
#cmakedefine WITH_X11
#endif // ! CONFIG_H
#endif /* ! CONFIG_H */

View File

@ -12,6 +12,7 @@ colors = {
selection = "231 202"
multiselect = "231 88"
defocused = "231 250"
directory = "16 231 bold"
incoming = "28"

43
info/10-azlyrics.pl Executable file
View File

@ -0,0 +1,43 @@
#!/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 d71c47f8ce7aecdc4856630e9d73a48912be68c1
Subproject commit 969a4cfc3ea1c4d7c0327907385fc64906ed5d4c

View File

@ -1,288 +0,0 @@
/*
* 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;
}

79
nncmpp.actions Normal file
View File

@ -0,0 +1,79 @@
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
TAB_NEXT, Switch to next tab
MPD_TOGGLE, Toggle play/pause
MPD_STOP, Stop playback
MPD_PREVIOUS, Previous song
MPD_NEXT, Next song
MPD_BACKWARD, Seek backwards
MPD_FORWARD, Seek forwards
MPD_VOLUME_UP, Increase MPD volume
MPD_VOLUME_DOWN, Decrease MPD volume
MPD_SEARCH, Global search
MPD_ADD, Add selection to playlist
MPD_REPLACE, Replace playlist
MPD_REPEAT, Toggle repeat
MPD_RANDOM, Toggle random playback
MPD_SINGLE, Toggle single song playback
MPD_CONSUME, Toggle consume
MPD_UPDATE_DB, Update MPD database
MPD_COMMAND, Send raw command to MPD
.ifdef WITH_PULSE
PULSE_VOLUME_UP, Increase PulseAudio volume
PULSE_VOLUME_DOWN, Decrease PulseAudio volume
PULSE_MUTE, Toggle PulseAudio sink mute
.endif
CHOOSE, Choose item
DELETE, Delete item
DESCRIBE, Describe item
UP, Go up a level
MULTISELECT, Toggle multiselect
INCREMENTAL_SEARCH, Incremental search
SCROLL_UP, Scroll up
SCROLL_DOWN, Scroll down
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
GOTO_ITEM_NEXT, Go to next item
GOTO_PAGE_PREVIOUS, Go to previous page
GOTO_PAGE_NEXT, Go to next page
GOTO_VIEW_TOP, Select top item
GOTO_VIEW_CENTER, Select center item
GOTO_VIEW_BOTTOM, Select bottom item
EDITOR_CONFIRM, Confirm input
EDITOR_B_CHAR, Go back a character
EDITOR_F_CHAR, Go forward a character
EDITOR_B_WORD, Go back a word
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
EDITOR_B_KILL_LINE, Delete everything up to BOL
EDITOR_F_KILL_LINE, Delete everything up to EOL

106
nncmpp.actions.awk Normal file
View File

@ -0,0 +1,106 @@
# 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

@ -6,25 +6,33 @@ nncmpp(1)
Name
----
nncmpp - terminal-based MPD client
nncmpp - MPD client
Synopsis
--------
*nncmpp* [_OPTION_]...
*nncmpp* [_OPTION_]... [_URL_ | _PATH_]...
Description
-----------
*nncmpp* is a terminal-based GUI-like MPD client. On start up it will welcome
*nncmpp* is a hybrid terminal/X11 MPD client. On start up it will welcome
you with an overview of all key bindings and the actions they're assigned to.
Individual tabs can be switched to either using the mouse or by pressing *M-1*
through *M-9*, corresponding to the order they appear in.
As a convenience utility, any program arguments are added to the MPD queue.
Note that to add files outside the database, you need to connect to MPD using
a socket file.
Options
-------
*-d*, *--debug*::
Adds a "Debug" tab showing all MPD communication and other information
that help debug various issues.
*-x*, *--x11*::
Use an X11 interface even when run from a terminal.
Note that the application may be built with this feature disabled.
*-h*, *--help*::
Display a help message and exit.
@ -33,43 +41,106 @@ Options
Configuration
-------------
Unless you run MPD on a remote machine, on an unusual port, or protected by
a password, the client doesn't need a configuration file to work. It is,
however, likely that you'll want to customize the looks or add some streams.
You can start off with the following snippet:
Unless you run MPD on a remote machine, on an unusual port, protected by
a password, or only accessible through a Unix socket, the client doesn't need
a configuration file to work. It is, however, likely that you'll want to
customize the looks or add some streams. You can start off with the following
snippet:
....
settings = {
address = "localhost:6600"
password = "<your password>"
root = "~/Music"
address = "~/.mpd/mpd.socket"
password = "<your password>"
pulseaudio = on
x11_font = "sans\\-serif-11"
}
colors = {
normal = ""
highlight = "bold"
elapsed = "reverse"
remains = "ul"
tab_bar = "reverse"
tab_active = "ul"
even = ""
odd = ""
selection = "reverse"
multiselect = "-1 6"
scrollbar = ""
normal = ""
highlight = "bold"
elapsed = "reverse"
remains = "ul"
tab_bar = "reverse"
tab_active = "ul"
even = ""
odd = ""
selection = "reverse"
multiselect = "-1 6"
defocused = "ul"
scrollbar = ""
}
streams = {
"dnbradio.com" = "http://www.dnbradio.com/hi.m3u"
"BassDrive.com" = "http://bassdrive.com/v2/streams/BassDrive.pls"
"dnbradio.com" = "https://dnbradio.com/hi.pls"
"BassDrive.com" = "http://bassdrive.com/v2/streams/BassDrive.pls"
}
....
Terminal attributes are accepted in a format similar to that of *git-config*(1),
only named colours aren't supported. The distribution contains example colour
schemes in the _contrib_ directory.
Terminal attributes also apply to the GUI, and are accepted in a format similar
to that of *git-config*(1), only named colours aren't supported.
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"
output plugin to show the audio spectrum. This has some caveats, namely that
it may not be properly synchronized, only one instance of a client can read from
a given named pipe at a time, it will cost you some CPU time, and finally you'll
need to set it up manually to match your MPD configuration, e.g.:
....
settings = {
...
spectrum_path = "~/.mpd/mpd.fifo" # "path"
spectrum_format = "44100:16:2" # "format" (samplerate:bits:channels)
spectrum_bars = 8 # beware of exponential complexity
...
}
....
The sample rate should be greater than 40 kHz, the number of bits 8 or 16,
and the number of channels doesn't matter, as they're simply averaged together.
PulseAudio
----------
If you find standard MPD volume control useless, you may instead configure
*nncmpp* to show and control the volume of any PulseAudio sink MPD is currently
connected to.
This feature may be enabled with the *settings.pulseaudio* configuration option,
as in the snippet above. To replace the default volume control bindings, use:
....
normal = {
"+" = "pulse-volume-up"
"-" = "pulse-volume-down"
}
....
The respective actions may also be invoked from the help tab directly.
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.
@ -77,6 +148,14 @@ 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,
@ -84,4 +163,4 @@ or submit pull requests.
See also
--------
*mpd*(1)
*mpd*(1), *pulseaudio*(1)

3922
nncmpp.c

File diff suppressed because it is too large Load Diff

9
nncmpp.desktop Normal file
View File

@ -0,0 +1,9 @@
[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;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 35 KiB

9
nncmpp.svg Normal file
View File

@ -0,0 +1,9 @@
<?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>

After

Width:  |  Height:  |  Size: 385 B

2
termo

@ -1 +1 @@
Subproject commit f7912a8ce7bbf7f701b5217bbb3879b13b66cfe7
Subproject commit 2518b53e5ae4579bf84ed58fa7a62806f64e861c