mirror of
https://github.com/dankamongmen/notcurses
synced 2025-03-08 23:59:02 -05:00
Split up notcurses/notcurses-core (#1297)
Extract `libnotcurses-core` from `libnotcurses`. The former contains everything except multimedia code. The latter contains multimedia stuff (a wrapper around FFmpeg or OIIO). If built with `-DUSE_MULTIMEDIA=none`, there will not be any `libnotcurses.so` generated. `libnotcurses.so` uses library constructors/destructors to insert its implementation into the `ncvisual` stack at runtime. Users linking `-lnotcurses` will get the full implementation; users linking `-lnotcurses-core` only will get the stack less multimedia code. The upshot of this is that someone can compile/install only `libnotcurses-core`, and a program linked against it will work just fine. This eliminates the need to install the full (large) dependency stack of the multimedia code unless necessary. This will hopefully be useful for e.g. installers etc. Closes #339.
This commit is contained in:
parent
c62a3f7464
commit
9112185657
295
CMakeLists.txt
295
CMakeLists.txt
@ -60,6 +60,12 @@ if("${USE_COVERAGE}")
|
||||
string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
|
||||
endif()
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
else()
|
||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
|
||||
endif()
|
||||
|
||||
# global compiler flags
|
||||
add_compile_definitions(FORTIFY_SOURCE=2)
|
||||
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -fexceptions)
|
||||
@ -114,11 +120,97 @@ endif()
|
||||
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND qrcodegen)
|
||||
endif()
|
||||
find_library(LIBRT rt)
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
file(GLOB COMPATSRC CONFIGURE_DEPENDS src/compat/compat.c)
|
||||
|
||||
# libnotcurses (core shared library, core static library)
|
||||
file(GLOB NCSRCS CONFIGURE_DEPENDS src/lib/*.c src/lib/*.cpp)
|
||||
############################################################################
|
||||
# libnotcurses-core (core shared library, core static library)
|
||||
file(GLOB NCCORESRCS CONFIGURE_DEPENDS src/lib/*.c src/lib/*.cpp)
|
||||
add_library(notcurses-core SHARED ${NCCORESRCS} ${COMPATSRC})
|
||||
if(${USE_STATIC})
|
||||
add_library(notcurses-core-static STATIC ${NCCORESRCS})
|
||||
else()
|
||||
add_library(notcurses-core-static STATIC EXCLUDE_FROM_ALL ${NCCORESRCS})
|
||||
endif()
|
||||
set_target_properties(
|
||||
notcurses-core-static PROPERTIES
|
||||
OUTPUT_NAME notcurses-core
|
||||
)
|
||||
set_target_properties(notcurses-core PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
)
|
||||
set_target_properties(notcurses-core-static PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
)
|
||||
target_include_directories(notcurses-core
|
||||
PRIVATE
|
||||
include
|
||||
src
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${TERMINFO_INCLUDE_DIRS}"
|
||||
"${READLINE_INCLUDE_DIRS}"
|
||||
)
|
||||
target_include_directories(notcurses-core-static
|
||||
PRIVATE
|
||||
include
|
||||
src
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${TERMINFO_STATIC_INCLUDE_DIRS}"
|
||||
"${READLINE_STATIC_INCLUDE_DIRS}"
|
||||
)
|
||||
target_link_libraries(notcurses-core
|
||||
PRIVATE
|
||||
"${TERMINFO_LIBRARIES}"
|
||||
"${READLINE_LIBRARIES}"
|
||||
"${LIBRT}"
|
||||
unistring
|
||||
PUBLIC
|
||||
Threads::Threads
|
||||
)
|
||||
target_link_libraries(notcurses-core-static
|
||||
PRIVATE
|
||||
"${TERMINFO_STATIC_LIBRARIES}"
|
||||
"${READLINE_STATIC_LIBRARIES}"
|
||||
"${LIBRT}"
|
||||
unistring
|
||||
PUBLIC
|
||||
Threads::Threads
|
||||
)
|
||||
target_link_directories(notcurses-core
|
||||
PRIVATE
|
||||
"${TERMINFO_LIBRARY_DIRS}"
|
||||
"${READLINE_LIBRARY_DIRS}"
|
||||
)
|
||||
target_link_directories(notcurses-core-static
|
||||
PRIVATE
|
||||
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
||||
"${READLINE_STATIC_LIBRARY_DIRS}"
|
||||
)
|
||||
# don't want these on freebsd/dragonfly/osx
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_compile_definitions(notcurses-core
|
||||
PUBLIC
|
||||
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
||||
PRIVATE
|
||||
_GNU_SOURCE _DEFAULT_SOURCE
|
||||
)
|
||||
target_compile_definitions(notcurses-core-static
|
||||
PUBLIC
|
||||
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
||||
PRIVATE
|
||||
_GNU_SOURCE _DEFAULT_SOURCE
|
||||
)
|
||||
endif()
|
||||
if(${USE_QRCODEGEN})
|
||||
target_link_libraries(notcurses-core PRIVATE qrcodegen)
|
||||
target_link_libraries(notcurses-core-static PRIVATE qrcodegen)
|
||||
endif()
|
||||
|
||||
############################################################################
|
||||
# libnotcurses (multimedia shared library+static library)
|
||||
file(GLOB NCSRCS CONFIGURE_DEPENDS src/media/*.c src/media/*.cpp)
|
||||
add_library(notcurses SHARED ${NCSRCS} ${COMPATSRC})
|
||||
if(${USE_STATIC})
|
||||
add_library(notcurses-static STATIC ${NCSRCS})
|
||||
@ -129,76 +221,38 @@ set_target_properties(
|
||||
notcurses-static PROPERTIES
|
||||
OUTPUT_NAME notcurses
|
||||
)
|
||||
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
set_target_properties(notcurses PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
SOVERSION ${PROJECT_VERSION_MAJOR}
|
||||
)
|
||||
set_target_properties(notcurses-static PROPERTIES
|
||||
VERSION ${PROJECT_VERSION}
|
||||
)
|
||||
target_include_directories(notcurses
|
||||
PRIVATE
|
||||
include
|
||||
src
|
||||
src/lib
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${TERMINFO_INCLUDE_DIRS}"
|
||||
"${READLINE_INCLUDE_DIRS}"
|
||||
)
|
||||
target_include_directories(notcurses-static
|
||||
PRIVATE
|
||||
include
|
||||
src
|
||||
src/lib
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
"${TERMINFO_STATIC_INCLUDE_DIRS}"
|
||||
"${READLINE_STATIC_INCLUDE_DIRS}"
|
||||
)
|
||||
target_link_libraries(notcurses
|
||||
PRIVATE
|
||||
"${TERMINFO_LIBRARIES}"
|
||||
"${READLINE_LIBRARIES}"
|
||||
"${LIBRT}"
|
||||
unistring
|
||||
PUBLIC
|
||||
Threads::Threads
|
||||
notcurses-core
|
||||
)
|
||||
target_link_libraries(notcurses-static
|
||||
PRIVATE
|
||||
"${TERMINFO_STATIC_LIBRARIES}"
|
||||
"${READLINE_STATIC_LIBRARIES}"
|
||||
"${LIBRT}"
|
||||
unistring
|
||||
PUBLIC
|
||||
Threads::Threads
|
||||
notcurses-core-static
|
||||
)
|
||||
target_link_directories(notcurses
|
||||
PRIVATE
|
||||
"${TERMINFO_LIBRARY_DIRS}"
|
||||
"${READLINE_LIBRARY_DIRS}"
|
||||
)
|
||||
target_link_directories(notcurses-static
|
||||
PRIVATE
|
||||
"${TERMINFO_STATIC_LIBRARY_DIRS}"
|
||||
"${READLINE_STATIC_LIBRARY_DIRS}"
|
||||
)
|
||||
|
||||
if(${USE_QRCODEGEN})
|
||||
target_link_libraries(notcurses PRIVATE qrcodegen)
|
||||
target_link_libraries(notcurses-static PRIVATE qrcodegen)
|
||||
endif()
|
||||
|
||||
if(${USE_FFMPEG})
|
||||
target_include_directories(notcurses
|
||||
PUBLIC
|
||||
PRIVATE
|
||||
"${AVCODEC_INCLUDE_DIRS}"
|
||||
"${AVFORMAT_INCLUDE_DIRS}"
|
||||
"${AVUTIL_INCLUDE_DIRS}"
|
||||
"${SWSCALE_INCLUDE_DIRS}"
|
||||
)
|
||||
target_include_directories(notcurses-static
|
||||
PUBLIC
|
||||
PRIVATE
|
||||
"${AVCODEC_STATIC_INCLUDE_DIRS}"
|
||||
"${AVFORMAT_STATIC_INCLUDE_DIRS}"
|
||||
"${AVUTIL_STATIC_INCLUDE_DIRS}"
|
||||
@ -209,7 +263,6 @@ target_link_libraries(notcurses
|
||||
"${AVCODEC_LIBRARIES}"
|
||||
"${AVFORMAT_LIBRARIES}"
|
||||
"${SWSCALE_LIBRARIES}"
|
||||
PUBLIC
|
||||
"${AVUTIL_LIBRARIES}"
|
||||
)
|
||||
target_link_libraries(notcurses-static
|
||||
@ -217,7 +270,6 @@ target_link_libraries(notcurses-static
|
||||
"${AVCODEC_STATIC_LIBRARIES}"
|
||||
"${AVFORMAT_STATIC_LIBRARIES}"
|
||||
"${SWSCALE_STATIC_LIBRARIES}"
|
||||
PUBLIC
|
||||
"${AVUTIL_STATIC_LIBRARIES}"
|
||||
)
|
||||
target_link_directories(notcurses
|
||||
@ -225,7 +277,6 @@ target_link_directories(notcurses
|
||||
"${AVCODEC_LIBRARY_DIRS}"
|
||||
"${AVFORMAT_LIBRARY_DIRS}"
|
||||
"${SWSCALE_LIBRARY_DIRS}"
|
||||
PUBLIC
|
||||
"${AVUTIL_LIBRARY_DIRS}"
|
||||
)
|
||||
target_link_directories(notcurses-static
|
||||
@ -233,7 +284,6 @@ target_link_directories(notcurses-static
|
||||
"${AVCODEC_STATIC_LIBRARY_DIRS}"
|
||||
"${AVFORMAT_STATIC_LIBRARY_DIRS}"
|
||||
"${SWSCALE_STATIC_LIBRARY_DIRS}"
|
||||
PUBLIC
|
||||
"${AVUTIL_STATIC_LIBRARY_DIRS}"
|
||||
)
|
||||
elseif(${USE_OIIO})
|
||||
@ -245,28 +295,8 @@ target_link_directories(notcurses PRIVATE ${OIIO_LIBRARY_DIRS})
|
||||
target_link_directories(notcurses-static PRIVATE ${OIIO_STATIC_LIBRARY_DIRS})
|
||||
endif()
|
||||
|
||||
# don't want these on freebsd
|
||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
target_compile_definitions(notcurses
|
||||
PUBLIC
|
||||
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
||||
PRIVATE
|
||||
_GNU_SOURCE _DEFAULT_SOURCE
|
||||
)
|
||||
target_compile_definitions(notcurses-static
|
||||
PUBLIC
|
||||
_XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
|
||||
PRIVATE
|
||||
_GNU_SOURCE _DEFAULT_SOURCE
|
||||
)
|
||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD")
|
||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
|
||||
elseif(${CMAKE_SYSTEM_NAME} STREQUAL "DragonFly")
|
||||
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
|
||||
endif()
|
||||
|
||||
# libnotcurses++
|
||||
############################################################################
|
||||
# libnotcurses++ (C++ wrappers)
|
||||
set(NCPP_SOURCES
|
||||
src/libcpp/FDPlane.cc
|
||||
src/libcpp/Menu.cc
|
||||
@ -375,31 +405,6 @@ install(FILES ${NOTCURSES_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/notcu
|
||||
install(FILES ${NCPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp)
|
||||
install(FILES ${NCPP_INTERNAL_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp/internal)
|
||||
|
||||
# notcurses-demo
|
||||
file(GLOB DEMOSRCS CONFIGURE_DEPENDS src/demo/*.c)
|
||||
add_executable(notcurses-demo ${DEMOSRCS} ${COMPATSRC})
|
||||
target_compile_definitions(notcurses-demo
|
||||
PRIVATE
|
||||
_GNU_SOURCE
|
||||
)
|
||||
target_include_directories(notcurses-demo
|
||||
PRIVATE
|
||||
include
|
||||
src
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
PUBLIC
|
||||
"${AVCODEC_INCLUDE_DIRS}"
|
||||
"${AVFORMAT_INCLUDE_DIRS}"
|
||||
"${AVUTIL_INCLUDE_DIRS}"
|
||||
"${SWSCALE_INCLUDE_DIRS}"
|
||||
)
|
||||
target_link_libraries(notcurses-demo
|
||||
PRIVATE
|
||||
notcurses
|
||||
unistring
|
||||
${MATH_LIBRARIES}
|
||||
)
|
||||
|
||||
# tiny proofs of concept, one binary per source file
|
||||
if(USE_POC)
|
||||
file(GLOB POCSRCS CONFIGURE_DEPENDS src/poc/*.c src/poc/*.cpp)
|
||||
@ -496,19 +501,35 @@ if(USE_DOXYGEN)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# ncneofetch
|
||||
file(GLOB FETCHSRCS CONFIGURE_DEPENDS src/fetch/*.c)
|
||||
add_executable(ncneofetch ${FETCHSRCS})
|
||||
target_include_directories(ncneofetch
|
||||
############################################################################
|
||||
# notcurses-demo
|
||||
file(GLOB DEMOSRCS CONFIGURE_DEPENDS src/demo/*.c)
|
||||
add_executable(notcurses-demo ${DEMOSRCS} ${COMPATSRC})
|
||||
target_compile_definitions(notcurses-demo
|
||||
PRIVATE
|
||||
_GNU_SOURCE
|
||||
)
|
||||
target_include_directories(notcurses-demo
|
||||
PRIVATE
|
||||
include
|
||||
src
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
PUBLIC
|
||||
"${AVCODEC_INCLUDE_DIRS}"
|
||||
"${AVFORMAT_INCLUDE_DIRS}"
|
||||
"${AVUTIL_INCLUDE_DIRS}"
|
||||
"${SWSCALE_INCLUDE_DIRS}"
|
||||
)
|
||||
target_link_libraries(ncneofetch
|
||||
target_link_libraries(notcurses-demo
|
||||
PRIVATE
|
||||
notcurses
|
||||
unistring
|
||||
${MATH_LIBRARIES}
|
||||
PUBLIC
|
||||
Threads::Threads
|
||||
)
|
||||
|
||||
############################################################################
|
||||
# notcurses-input
|
||||
file(GLOB INPUTSRCS CONFIGURE_DEPENDS src/input/input.cpp)
|
||||
add_executable(notcurses-input ${INPUTSRCS})
|
||||
@ -522,19 +543,7 @@ target_link_libraries(notcurses-input
|
||||
notcurses++
|
||||
)
|
||||
|
||||
# ncls
|
||||
file(GLOB LSSRC CONFIGURE_DEPENDS src/ls/*.cpp)
|
||||
add_executable(ncls ${LSSRC})
|
||||
target_include_directories(ncls
|
||||
PRIVATE
|
||||
include
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
)
|
||||
target_link_libraries(ncls
|
||||
PRIVATE
|
||||
notcurses++
|
||||
)
|
||||
|
||||
############################################################################
|
||||
# notcurses-tetris
|
||||
file(GLOB TETRISSRC CONFIGURE_DEPENDS src/tetris/*.cpp)
|
||||
add_executable(notcurses-tetris ${TETRISSRC})
|
||||
@ -548,8 +557,39 @@ target_link_libraries(notcurses-tetris
|
||||
notcurses++
|
||||
)
|
||||
|
||||
# all further binaries require multimedia support
|
||||
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||
############################################################################
|
||||
# ncneofetch
|
||||
file(GLOB FETCHSRCS CONFIGURE_DEPENDS src/fetch/*.c)
|
||||
add_executable(ncneofetch ${FETCHSRCS})
|
||||
target_include_directories(ncneofetch
|
||||
PRIVATE
|
||||
include
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
)
|
||||
target_link_libraries(ncneofetch
|
||||
PRIVATE
|
||||
notcurses
|
||||
)
|
||||
|
||||
############################################################################
|
||||
# ncls
|
||||
file(GLOB LSSRC CONFIGURE_DEPENDS src/ls/*.cpp)
|
||||
add_executable(ncls ${LSSRC})
|
||||
target_include_directories(ncls
|
||||
PRIVATE
|
||||
include
|
||||
"${PROJECT_BINARY_DIR}/include"
|
||||
)
|
||||
target_link_libraries(ncls
|
||||
PRIVATE
|
||||
notcurses++
|
||||
)
|
||||
|
||||
############################################################################
|
||||
# notcurses-view
|
||||
if(${USE_FFMPEG} OR ${USE_OIIO})
|
||||
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||
file(GLOB VIEWSRCS CONFIGURE_DEPENDS src/view/*.cpp)
|
||||
add_executable(notcurses-view ${VIEWSRCS} ${COMPATSRC})
|
||||
target_include_directories(notcurses-view
|
||||
@ -563,8 +603,10 @@ target_link_libraries(notcurses-view
|
||||
notcurses++
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Testing
|
||||
############################################################################
|
||||
# testing
|
||||
if(${BUILD_TESTING})
|
||||
#set(CMAKE_CTEST_ARGUMENTS "-V")
|
||||
if(${USE_DOCTEST})
|
||||
@ -579,8 +621,8 @@ target_include_directories(notcurses-tester
|
||||
)
|
||||
target_link_libraries(notcurses-tester
|
||||
PRIVATE
|
||||
unistring
|
||||
notcurses++
|
||||
unistring
|
||||
"${TERMINFO_LIBRARIES}"
|
||||
)
|
||||
add_test(
|
||||
@ -629,11 +671,14 @@ add_custom_target(demo
|
||||
)
|
||||
|
||||
# pkg-config support
|
||||
configure_file(tools/notcurses-core.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/notcurses-core.pc
|
||||
@ONLY
|
||||
)
|
||||
configure_file(tools/notcurses.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/notcurses.pc
|
||||
@ONLY
|
||||
)
|
||||
|
||||
configure_file(tools/notcurses++.pc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/notcurses++.pc
|
||||
@ONLY
|
||||
@ -686,7 +731,7 @@ install(FILES
|
||||
DESTINATION ${PKGCONFIG_DIR}
|
||||
)
|
||||
|
||||
if(${USE_FFMPEG} OR ${USE_OIIO})
|
||||
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||
file(GLOB TESTDATA CONFIGURE_DEPENDS data/*)
|
||||
# Don't install source materia for self-originated multimedia
|
||||
list(FILTER TESTDATA EXCLUDE REGEX ".*xcf$")
|
||||
@ -701,14 +746,14 @@ install(FILES ${MARKDOWN} DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
|
||||
install(TARGETS notcurses-demo DESTINATION bin)
|
||||
install(TARGETS notcurses-input DESTINATION bin)
|
||||
install(TARGETS ncls DESTINATION bin)
|
||||
install(TARGETS ncneofetch DESTINATION bin)
|
||||
install(TARGETS notcurses-tetris DESTINATION bin)
|
||||
if(${USE_FFMPEG} OR ${USE_OIIO})
|
||||
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
|
||||
install(TARGETS ncneofetch DESTINATION bin)
|
||||
install(TARGETS ncls DESTINATION bin)
|
||||
install(TARGETS notcurses-view DESTINATION bin)
|
||||
endif()
|
||||
|
||||
install(TARGETS notcurses notcurses++
|
||||
install(TARGETS notcurses-core notcurses notcurses++
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT Libraries
|
||||
@ -716,7 +761,7 @@ install(TARGETS notcurses notcurses++
|
||||
)
|
||||
if(${USE_STATIC})
|
||||
install(
|
||||
TARGETS notcurses-static notcurses++-static
|
||||
TARGETS notcurses-core-static notcurses-static notcurses++-static
|
||||
LIBRARY
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
COMPONENT Libraries
|
||||
|
16
INSTALL.md
16
INSTALL.md
@ -14,6 +14,14 @@ On an APT-based distribution, run:
|
||||
|
||||
`apt-get install build-essential cmake doctest-dev libavformat-dev libavutil-dev libncurses-dev libreadline-dev libqrcodegen-dev libswscale-dev libunistring-dev pandoc pkg-config`
|
||||
|
||||
If you only intend to build core Notcurses (without multimedia support), run:
|
||||
|
||||
`apt-get install build-essential cmake libncurses-dev libreadline-dev libqrcodegen-dev pandoc pkg-config`
|
||||
|
||||
If you only intend to build core Notcurses (without multimedia support), run:
|
||||
|
||||
`apt-get install build-essential cmake libncurses-dev libreadline-dev libqrcodegen-dev pandoc pkg-config`
|
||||
|
||||
If you want to build the Python wrappers, you'll also need:
|
||||
|
||||
`apt-get install python3-cffi python3-dev python3-pypandoc python3-setuptools`
|
||||
@ -28,8 +36,11 @@ If you want to build the Rust wrappers, you'll also need:
|
||||
|
||||
## Building
|
||||
|
||||
* Create a subdirectory, traditionally `build`. Enter the directory.
|
||||
* `cmake ..`. You might want to set e.g. `CMAKE_BUILD_TYPE`.
|
||||
* Create a subdirectory, traditionally `build` (this is not strictly necessary,
|
||||
but it keeps your source tree clean). Enter the directory.
|
||||
* `cmake ..`
|
||||
** You might want to set e.g. `CMAKE_BUILD_TYPE`. Use `-DVAR=val`.
|
||||
** To build without multimedia support, use `-DUSE_MULTIMEDIA=none`.
|
||||
* `make`
|
||||
* `make test`
|
||||
* `make install`
|
||||
@ -89,4 +100,3 @@ but must be `Debug` for use of `USE_COVERAGE`.
|
||||
* `USE_POC`: build small, uninstalled proof-of-concept binaries
|
||||
* `USE_QRCODEGEN`: build qrcode support via libqrcodegen
|
||||
* `USE_STATIC`: build static libraries (in addition to shared ones)
|
||||
|
||||
|
6
NEWS.md
6
NEWS.md
@ -1,6 +1,12 @@
|
||||
This document attempts to list user-visible changes and any major internal
|
||||
rearrangements of Notcurses.
|
||||
|
||||
* 2.1.6 (not yet released):
|
||||
* Notcurses has been split into two libraries, `notcurses-core` and
|
||||
`notcurses`. The latter contains the heavyweight multimedia code,
|
||||
so that applications which don't need this functionality can link against
|
||||
only the former. `pkg-config` support is present for both.
|
||||
|
||||
* 2.1.5 (2021-01-15):
|
||||
* Notcurses **now depends on GNU Readline at build and runtime**, entirely
|
||||
for the benefit of direct mode, which now prepares GNU Readline for safe
|
||||
|
@ -86,6 +86,8 @@ Why use this non-standard library?
|
||||
* The library object exports a minimal set of symbols. Where reasonable,
|
||||
`static inline` header-only code is used. This facilitates compiler
|
||||
optimizations, and reduces loader time.
|
||||
* Notcurses can be built without its multimedia functionality, requiring a
|
||||
significantly lesser set of dependencies.
|
||||
|
||||
* All APIs natively support the Universal Character Set (Unicode). The `cell`
|
||||
API is based around Unicode's [Extended Grapheme Cluster](https://unicode.org/reports/tr29/) concept.
|
||||
|
15
USAGE.md
15
USAGE.md
@ -1,7 +1,7 @@
|
||||
# Usage
|
||||
|
||||
As of version 2.0.0, Notcurses honors Semantic Versioning, the API is stable,
|
||||
and the project is committed to backwards compatibility.
|
||||
As of version 2.0.0, the Notcurses API is stable, and the project is committed
|
||||
to backwards compatibility.
|
||||
|
||||
* [Direct Mode](#direct-mode)
|
||||
* [Alignment](#alignment)
|
||||
@ -15,16 +15,17 @@ and the project is committed to backwards compatibility.
|
||||
* [Stats](#stats)
|
||||
* [C++](#c++)
|
||||
|
||||
A full API reference [is available](https://nick-black.com/notcurses/). Manual
|
||||
pages ought have been installed along with Notcurses. This document is a
|
||||
A full API reference [is available](https://nick-black.com/notcurses/) in the
|
||||
form of manual pages; these ought have been installed along with Notcurses. This document is a
|
||||
secondary reference, and should not be considered authoritative. For a more
|
||||
unified commentary, consider the [paperback](https://www.amazon.com/dp/B086PNVNC9)
|
||||
(also available as a free PDF from https://nick-black.com).
|
||||
(also available as a [free PDF](https://nick-black.com/dankwiki/index.php?title=Hacking_The_Planet!_with_Notcurses).
|
||||
|
||||
A program wishing to use Notcurses will need to link it, ideally using the
|
||||
output of `pkg-config --libs notcurses`. It is advised to compile with the
|
||||
output of `pkg-config --cflags notcurses`. If using CMake, a support file is
|
||||
provided, and can be accessed as `Notcurses`.
|
||||
output of `pkg-config --cflags notcurses`. To use the minimal core Notcurses,
|
||||
without multimedia support, use `pkg-config --libs notcurses-core` etc. If
|
||||
using CMake, a support file is provided, and can be accessed as `Notcurses`.
|
||||
|
||||
Before calling into Notcurses—and usually as one of the first calls of the
|
||||
program—be sure to call `setlocale(3)` with an appropriate UTF-8 locale. It is
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 23 KiB |
@ -8,9 +8,10 @@ notcurses - TUI library for modern terminal emulators
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
**#include <notcurses/notcurses.h>**
|
||||
**#include <notcurses/notcurses.h>** or
|
||||
**#include <notcurses/notcurses-core.h>**
|
||||
|
||||
**-lnotcurses**
|
||||
**-lnotcurses** or **-lnotcurses-core**
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
@ -23,7 +24,8 @@ A program wishing to use Notcurses will need to link it, ideally using the
|
||||
output of **pkg-config --libs notcurses** (see **pkg-config(1)**). It is
|
||||
advised to compile with the output of **pkg-config --cflags notcurses**. If
|
||||
using CMake, a support file is provided, and can be accessed as **Notcurses**
|
||||
(see **cmake(1)**).
|
||||
(see **cmake(1)**). If multimedia capabilities are not needed, it is possible
|
||||
to link against a minimal Notcurses using **pkg-config --libs notcurses-core**.
|
||||
|
||||
**notcurses_init(3)** can then be used to initialize a Notcurses instance for a
|
||||
given **FILE** (usually **stdout**, usually attached to a terminal).
|
||||
|
@ -105,7 +105,6 @@ int dragon_demo(struct notcurses* nc){
|
||||
}
|
||||
DEMO_RENDER(nc);
|
||||
demo_nanosleep(nc, &scaled);
|
||||
ncplane_erase(n);
|
||||
}while(lasttotal != r);
|
||||
ncvisual_destroy(ncv);
|
||||
return 0;
|
||||
|
@ -878,13 +878,6 @@ int ncblit_rgba(const void* data, int linesize, const struct ncvisual_options* v
|
||||
leny, lenx, blend);
|
||||
}
|
||||
|
||||
int rgba_blit_dispatch(ncplane* nc, const struct blitset* bset, int placey,
|
||||
int placex, int linesize, const void* data, int begy,
|
||||
int begx, int leny, int lenx, bool blendcolors){
|
||||
return bset->blit(nc, placey, placex, linesize, data, begy, begx,
|
||||
leny, lenx, blendcolors);
|
||||
}
|
||||
|
||||
ncblitter_e ncvisual_media_defblitter(const notcurses* nc, ncscale_e scale){
|
||||
return rgba_blitter_default(nc->utf8, scale, nc->tcache.sextants);
|
||||
}
|
||||
|
@ -1,72 +0,0 @@
|
||||
#ifndef NOTCURSES_FFMPEG
|
||||
#define NOTCURSES_FFMPEG
|
||||
|
||||
#include "version.h"
|
||||
#ifdef USE_FFMPEG
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavutil/version.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/rational.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswscale/version.h>
|
||||
#include <libavformat/version.h>
|
||||
#include <libavformat/avformat.h>
|
||||
|
||||
} // extern "C"
|
||||
|
||||
struct AVFormatContext;
|
||||
struct AVCodecContext;
|
||||
struct AVFrame;
|
||||
struct AVCodec;
|
||||
struct AVCodecParameters;
|
||||
struct AVPacket;
|
||||
|
||||
typedef struct ncvisual_details {
|
||||
int packet_outstanding;
|
||||
struct AVFormatContext* fmtctx;
|
||||
struct AVCodecContext* codecctx; // video codec context
|
||||
struct AVCodecContext* subtcodecctx; // subtitle codec context
|
||||
struct AVFrame* frame; // frame as read
|
||||
struct AVFrame* oframe; // RGBA frame
|
||||
struct AVCodec* codec;
|
||||
struct AVCodecParameters* cparams;
|
||||
struct AVCodec* subtcodec;
|
||||
struct AVPacket* packet;
|
||||
struct SwsContext* swsctx;
|
||||
AVSubtitle subtitle;
|
||||
int stream_index; // match against this following av_read_frame()
|
||||
int sub_stream_index; // subtitle stream index, can be < 0 if no subtitles
|
||||
} ncvisual_details;
|
||||
|
||||
static inline auto
|
||||
ncvisual_details_init(ncvisual_details* deets) -> int {
|
||||
memset(deets, 0, sizeof(*deets));
|
||||
deets->stream_index = -1;
|
||||
deets->sub_stream_index = -1;
|
||||
if((deets->frame = av_frame_alloc()) == nullptr){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline auto
|
||||
ncvisual_details_destroy(ncvisual_details* deets) -> void {
|
||||
avcodec_close(deets->codecctx);
|
||||
avcodec_free_context(&deets->codecctx);
|
||||
av_frame_free(&deets->frame);
|
||||
av_freep(&deets->oframe);
|
||||
//avcodec_parameters_free(&ncv->cparams);
|
||||
sws_freeContext(deets->swsctx);
|
||||
av_packet_free(&deets->packet);
|
||||
avformat_close_input(&deets->fmtctx);
|
||||
avsubtitle_free(&deets->subtitle);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -681,9 +681,13 @@ memdup(const void* src, size_t len){
|
||||
|
||||
void* bgra_to_rgba(const void* data, int rows, int rowstride, int cols);
|
||||
|
||||
int rgba_blit_dispatch(ncplane* nc, const struct blitset* bset, int placey,
|
||||
int placex, int linesize, const void* data, int begy,
|
||||
int begx, int leny, int lenx, bool blendcolors);
|
||||
static inline int
|
||||
rgba_blit_dispatch(ncplane* nc, const struct blitset* bset, int placey,
|
||||
int placex, int linesize, const void* data, int begy,
|
||||
int begx, int leny, int lenx, bool blendcolors){
|
||||
return bset->blit(nc, placey, placex, linesize, data, begy, begx,
|
||||
leny, lenx, blendcolors);
|
||||
}
|
||||
|
||||
// find the "center" cell of two lengths. in the case of even rows/columns, we
|
||||
// place the center on the top/left. in such a case there will be one more
|
||||
@ -1068,6 +1072,8 @@ int setup_signals(void* nc, bool no_quit_sigs, bool no_winch_sig,
|
||||
int(*handler)(void*));
|
||||
int drop_signals(void* nc);
|
||||
|
||||
void ncvisual_printbanner(const notcurses* nc);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -802,19 +802,7 @@ init_banner(const notcurses* nc){
|
||||
#error "No __BYTE_ORDER__ definition"
|
||||
#endif
|
||||
, curses_version());
|
||||
#ifdef USE_FFMPEG
|
||||
printf(" avformat %u.%u.%u avutil %u.%u.%u swscale %u.%u.%u\n",
|
||||
LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO,
|
||||
LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO,
|
||||
LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO);
|
||||
#else
|
||||
#ifdef USE_OIIO
|
||||
printf(" openimageio %s\n", oiio_version());
|
||||
#else
|
||||
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
||||
fprintf(stderr, "\n Warning! Notcurses was built without multimedia support.\n");
|
||||
#endif
|
||||
#endif
|
||||
ncvisual_printbanner(nc);
|
||||
fflush(stdout);
|
||||
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
||||
if(!nc->tcache.RGBflag){ // FIXME
|
||||
|
@ -1,38 +0,0 @@
|
||||
#ifndef NOTCURSES_OIIO
|
||||
#define NOTCURSES_OIIO
|
||||
|
||||
// OpenImageIO implementation of ncvisual_details
|
||||
#include "version.h"
|
||||
#ifdef USE_OIIO
|
||||
#include <OpenImageIO/filter.h>
|
||||
#include <OpenImageIO/version.h>
|
||||
#include <OpenImageIO/imageio.h>
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/imagebufalgo.h>
|
||||
|
||||
typedef struct ncvisual_details {
|
||||
std::unique_ptr<OIIO::ImageInput> image; // must be close()d
|
||||
std::unique_ptr<uint32_t[]> frame;
|
||||
std::unique_ptr<OIIO::ImageBuf> ibuf;
|
||||
uint64_t framenum;
|
||||
} ncvisual_details;
|
||||
|
||||
static inline auto
|
||||
ncvisual_details_init(ncvisual_details *deets) -> int {
|
||||
deets->image = nullptr;
|
||||
deets->frame = nullptr;
|
||||
deets->ibuf = nullptr;
|
||||
deets->framenum = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline auto
|
||||
ncvisual_details_destroy(ncvisual_details* deets) -> void {
|
||||
if(deets->image){
|
||||
deets->image->close();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -4,48 +4,40 @@
|
||||
#include "builddef.h"
|
||||
#include "notcurses/notcurses.h"
|
||||
|
||||
#ifdef USE_FFMPEG
|
||||
#include "ffmpeg.h"
|
||||
#else
|
||||
#ifdef USE_OIIO
|
||||
#include "oiio.h"
|
||||
#else
|
||||
|
||||
typedef struct ncvisual_details {
|
||||
} ncvisual_details;
|
||||
|
||||
static inline auto ncvisual_details_init(ncvisual_details* deets) -> int {
|
||||
(void)deets;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline auto
|
||||
ncvisual_details_destroy(ncvisual_details* deets) -> void {
|
||||
(void)deets;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
struct ncplane;
|
||||
struct ncvisual_details;
|
||||
|
||||
typedef struct ncvisual {
|
||||
struct ncvisual_details* details;// implementation-specific details
|
||||
uint32_t* data; // (scaled) RGBA image data, rowstride bytes per row
|
||||
int cols, rows;
|
||||
// lines are sometimes padded. this many true bytes per row in data.
|
||||
int rowstride;
|
||||
ncvisual_details details;// implementation-specific details
|
||||
uint32_t* data; // (scaled) RGBA image data, rowstride bytes per row
|
||||
bool owndata; // we own data iff owndata == true
|
||||
} ncvisual;
|
||||
|
||||
static inline auto
|
||||
ncvisual_create(void) -> ncvisual* {
|
||||
auto ret = new ncvisual{};
|
||||
if(ret == nullptr){
|
||||
return nullptr;
|
||||
}
|
||||
ncvisual_details_init(&ret->details);
|
||||
return ret;
|
||||
}
|
||||
typedef struct ncvisual_implementation {
|
||||
int (*ncvisual_init)(int loglevel);
|
||||
int (*ncvisual_decode)(ncvisual*);
|
||||
int (*ncvisual_blit)(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
const struct blitset* bset, int placey, int placex,
|
||||
int begy, int begx, int leny, int lenx,
|
||||
bool blendcolors);
|
||||
ncvisual* (*ncvisual_create)(void);
|
||||
ncvisual* (*ncvisual_from_file)(const char* s);
|
||||
void (*ncvisual_printbanner)(const struct notcurses* nc);
|
||||
// ncv constructors other than ncvisual_from_file() need to set up the
|
||||
// AVFrame* 'frame' according to their own data, which is assumed to
|
||||
// have been prepared already in 'ncv'.
|
||||
void (*ncvisual_details_seed)(ncvisual* ncv);
|
||||
void (*ncvisual_details_destroy)(struct ncvisual_details* deets);
|
||||
bool canopen_images;
|
||||
bool canopen_videos;
|
||||
} ncvisual_implementation;
|
||||
|
||||
// ugh! need export this for pluggable multimedia modules without dlopen()
|
||||
__attribute__((visibility("default")))
|
||||
int notcurses_set_ncvisual_implementation(const ncvisual_implementation* imp);
|
||||
|
||||
static inline auto
|
||||
ncvisual_set_data(ncvisual* ncv, uint32_t* data, bool owned) -> void {
|
||||
|
@ -4,10 +4,111 @@
|
||||
#include "visual-details.h"
|
||||
#include "internal.h"
|
||||
|
||||
// ncv constructors other than ncvisual_from_file() need to set up the
|
||||
// AVFrame* 'frame' according to their own data, which is assumed to
|
||||
// have been prepared already in 'ncv'.
|
||||
auto ncvisual_details_seed(struct ncvisual* ncv) -> void;
|
||||
// FIXME make this a weak symbol instead so we work with static linking
|
||||
static const ncvisual_implementation* impl;
|
||||
static pthread_rwlock_t impllock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
int notcurses_set_ncvisual_implementation(const ncvisual_implementation* imp){
|
||||
int ret = -1;
|
||||
if(pthread_rwlock_wrlock(&impllock)){
|
||||
return -1;
|
||||
}
|
||||
if(impl == nullptr){
|
||||
impl = imp;
|
||||
}
|
||||
ret |= pthread_rwlock_unlock(&impllock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ncvisual_decode(ncvisual* nc) -> int {
|
||||
int ret = -1;
|
||||
if(pthread_rwlock_rdlock(&impllock)){
|
||||
return -1;
|
||||
}
|
||||
if(impl){
|
||||
ret = impl->ncvisual_decode(nc);
|
||||
}
|
||||
ret |= pthread_rwlock_unlock(&impllock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
const struct blitset* bset, int placey, int placex,
|
||||
int begy, int begx, int leny, int lenx,
|
||||
bool blendcolors) -> int {
|
||||
int ret = -1;
|
||||
if(pthread_rwlock_rdlock(&impllock)){
|
||||
return -1;
|
||||
}
|
||||
if(impl){
|
||||
if(impl->ncvisual_blit(ncv, rows, cols, n, bset, placey, placex,
|
||||
begy, begx, leny, lenx, blendcolors) >= 0){
|
||||
ret = 0;
|
||||
}
|
||||
}else{
|
||||
if(rgba_blit_dispatch(n, bset, placey, placex, ncv->rowstride, ncv->data,
|
||||
begy, begx, leny, lenx, blendcolors) >= 0){
|
||||
ret = 0;
|
||||
}
|
||||
}
|
||||
ret |= pthread_rwlock_unlock(&impllock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ncvisual_details_seed(struct ncvisual* ncv) -> void {
|
||||
pthread_rwlock_rdlock(&impllock);
|
||||
if(impl){
|
||||
impl->ncvisual_details_seed(ncv);
|
||||
}
|
||||
pthread_rwlock_unlock(&impllock);
|
||||
}
|
||||
|
||||
auto ncvisual_init(int loglevel) -> int {
|
||||
int ret = 0; // default to success here
|
||||
if(pthread_rwlock_rdlock(&impllock)){
|
||||
return -1;
|
||||
}
|
||||
if(impl){
|
||||
ret = impl->ncvisual_init(loglevel);
|
||||
}
|
||||
ret |= pthread_rwlock_unlock(&impllock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ncvisual_from_file(const char* filename) -> ncvisual* {
|
||||
ncvisual* ret = nullptr;
|
||||
if(pthread_rwlock_rdlock(&impllock) == 0){
|
||||
if(impl){
|
||||
ret = impl->ncvisual_from_file(filename);
|
||||
}
|
||||
pthread_rwlock_unlock(&impllock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ncvisual_create(void) -> ncvisual* {
|
||||
ncvisual* ret = nullptr;
|
||||
if(pthread_rwlock_rdlock(&impllock) == 0){
|
||||
if(impl){
|
||||
ret = impl->ncvisual_create();
|
||||
}else{
|
||||
ret = new ncvisual{};
|
||||
}
|
||||
pthread_rwlock_unlock(&impllock);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto ncvisual_printbanner(const notcurses* nc) -> void {
|
||||
pthread_rwlock_rdlock(&impllock);
|
||||
if(impl){
|
||||
impl->ncvisual_printbanner(nc);
|
||||
}else{
|
||||
term_fg_palindex(nc, stderr, nc->tcache.colors <= 88 ? 1 % nc->tcache.colors : 0xcb);
|
||||
fprintf(stderr, "\n Warning! Notcurses was built without multimedia support.\n");
|
||||
}
|
||||
pthread_rwlock_unlock(&impllock);
|
||||
}
|
||||
|
||||
auto ncvisual_geom(const notcurses* nc, const ncvisual* n,
|
||||
const struct ncvisual_options* vopts,
|
||||
@ -484,11 +585,15 @@ auto ncvisual_from_plane(const ncplane* n, ncblitter_e blit, int begy, int begx,
|
||||
|
||||
auto ncvisual_destroy(ncvisual* ncv) -> void {
|
||||
if(ncv){
|
||||
ncvisual_details_destroy(&ncv->details);
|
||||
pthread_rwlock_rdlock(&impllock);
|
||||
if(impl){
|
||||
impl->ncvisual_details_destroy(ncv->details);
|
||||
}
|
||||
if(ncv->owndata){
|
||||
free(ncv->data);
|
||||
}
|
||||
delete ncv;
|
||||
pthread_rwlock_unlock(&impllock);
|
||||
}
|
||||
}
|
||||
|
||||
@ -567,24 +672,18 @@ auto ncvisual_polyfill_yx(ncvisual* n, int y, int x, uint32_t rgba) -> int {
|
||||
return ncvisual_polyfill_recurse(n, y, x, rgba, *pixel);
|
||||
}
|
||||
|
||||
#ifndef USE_OIIO // built without ffmpeg or oiio
|
||||
#ifndef USE_FFMPEG
|
||||
auto ncvisual_from_file(const char* filename) -> ncvisual* {
|
||||
(void)filename;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) -> bool {
|
||||
return false;
|
||||
if(!impl){
|
||||
return false;
|
||||
}
|
||||
return impl->canopen_images;
|
||||
}
|
||||
|
||||
auto notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) -> bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ncvisual_decode(ncvisual* nc) -> int {
|
||||
(void)nc;
|
||||
return -1;
|
||||
if(!impl){
|
||||
return false;
|
||||
}
|
||||
return impl->canopen_videos;
|
||||
}
|
||||
|
||||
auto ncvisual_decode_loop(ncvisual* nc) -> int {
|
||||
@ -609,28 +708,6 @@ auto ncvisual_subtitle(const ncvisual* ncv) -> char* {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto ncvisual_init(int loglevel) -> int {
|
||||
(void)loglevel;
|
||||
return 0; // allow success here
|
||||
}
|
||||
|
||||
auto ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
const struct blitset* bset, int placey, int placex,
|
||||
int begy, int begx, int leny, int lenx,
|
||||
bool blendcolors) -> int {
|
||||
(void)rows;
|
||||
(void)cols;
|
||||
if(rgba_blit_dispatch(n, bset, placey, placex, ncv->rowstride, ncv->data,
|
||||
begy, begx, leny, lenx, blendcolors) < 0){
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto ncvisual_details_seed(struct ncvisual* ncv) -> void {
|
||||
(void)ncv;
|
||||
}
|
||||
|
||||
auto ncvisual_resize(ncvisual* nc, int rows, int cols) -> int {
|
||||
// we'd need to verify that it's RGBA as well, except that if we've got no
|
||||
// multimedia engine, we've only got memory-assembled ncvisuals, which are
|
||||
@ -640,6 +717,3 @@ auto ncvisual_resize(ncvisual* nc, int rows, int cols) -> int {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
@ -1,18 +1,47 @@
|
||||
#include "builddef.h"
|
||||
#ifdef USE_FFMPEG
|
||||
#include "ffmpeg.h"
|
||||
extern "C" {
|
||||
#include <libavutil/error.h>
|
||||
#include <libavutil/frame.h>
|
||||
#include <libavutil/pixdesc.h>
|
||||
#include <libavutil/version.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libavutil/rational.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <libswscale/version.h>
|
||||
#include <libavformat/version.h>
|
||||
#include <libavformat/avformat.h>
|
||||
}
|
||||
#include "internal.h"
|
||||
#include "visual-details.h"
|
||||
|
||||
struct AVFormatContext;
|
||||
struct AVCodecContext;
|
||||
struct AVFrame;
|
||||
struct AVCodec;
|
||||
struct AVCodecParameters;
|
||||
struct AVPacket;
|
||||
|
||||
typedef struct ncvisual_details {
|
||||
int packet_outstanding;
|
||||
struct AVFormatContext* fmtctx;
|
||||
struct AVCodecContext* codecctx; // video codec context
|
||||
struct AVCodecContext* subtcodecctx; // subtitle codec context
|
||||
struct AVFrame* frame; // frame as read
|
||||
struct AVFrame* oframe; // RGBA frame
|
||||
struct AVCodec* codec;
|
||||
struct AVCodecParameters* cparams;
|
||||
struct AVCodec* subtcodec;
|
||||
struct AVPacket* packet;
|
||||
struct SwsContext* swsctx;
|
||||
AVSubtitle subtitle;
|
||||
int stream_index; // match against this following av_read_frame()
|
||||
int sub_stream_index; // subtitle stream index, can be < 0 if no subtitles
|
||||
} ncvisual_details;
|
||||
|
||||
#define IMGALLOCALIGN 32
|
||||
|
||||
bool notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) {
|
||||
return true;
|
||||
}
|
||||
static void inject_implementation(void) __attribute__ ((constructor));
|
||||
|
||||
/*static void
|
||||
print_frame_summary(const AVCodecContext* cctx, const AVFrame* f) {
|
||||
@ -86,8 +115,8 @@ deass(const char* ass) {
|
||||
}
|
||||
|
||||
auto ncvisual_subtitle(const ncvisual* ncv) -> char* {
|
||||
for(unsigned i = 0 ; i < ncv->details.subtitle.num_rects ; ++i){
|
||||
const AVSubtitleRect* rect = ncv->details.subtitle.rects[i];
|
||||
for(unsigned i = 0 ; i < ncv->details->subtitle.num_rects ; ++i){
|
||||
const AVSubtitleRect* rect = ncv->details->subtitle.rects[i];
|
||||
if(rect->type == SUBTITLE_ASS){
|
||||
return deass(rect->ass);
|
||||
}else if(rect->type == SUBTITLE_TEXT) {;
|
||||
@ -107,49 +136,50 @@ averr2ncerr(int averr){
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ncvisual_decode(ncvisual* nc){
|
||||
if(nc->details.fmtctx == nullptr){ // not a file-backed ncvisual
|
||||
static int
|
||||
ffmpeg_decode(ncvisual* nc){
|
||||
if(nc->details->fmtctx == nullptr){ // not a file-backed ncvisual
|
||||
return -1;
|
||||
}
|
||||
bool have_frame = false;
|
||||
bool unref = false;
|
||||
// FIXME what if this was set up with e.g. ncvisual_from_rgba()?
|
||||
if(nc->details.oframe){
|
||||
av_freep(&nc->details.oframe->data[0]);
|
||||
if(nc->details->oframe){
|
||||
av_freep(&nc->details->oframe->data[0]);
|
||||
}
|
||||
do{
|
||||
do{
|
||||
if(nc->details.packet_outstanding){
|
||||
if(nc->details->packet_outstanding){
|
||||
break;
|
||||
}
|
||||
if(unref){
|
||||
av_packet_unref(nc->details.packet);
|
||||
av_packet_unref(nc->details->packet);
|
||||
}
|
||||
int averr;
|
||||
if((averr = av_read_frame(nc->details.fmtctx, nc->details.packet)) < 0){
|
||||
if((averr = av_read_frame(nc->details->fmtctx, nc->details->packet)) < 0){
|
||||
/*if(averr != AVERROR_EOF){
|
||||
fprintf(stderr, "Error reading frame info (%s)\n", av_err2str(averr));
|
||||
}*/
|
||||
return averr2ncerr(averr);
|
||||
}
|
||||
unref = true;
|
||||
if(nc->details.packet->stream_index == nc->details.sub_stream_index){
|
||||
if(nc->details->packet->stream_index == nc->details->sub_stream_index){
|
||||
int result = 0, ret;
|
||||
ret = avcodec_decode_subtitle2(nc->details.subtcodecctx, &nc->details.subtitle, &result, nc->details.packet);
|
||||
ret = avcodec_decode_subtitle2(nc->details->subtcodecctx, &nc->details->subtitle, &result, nc->details->packet);
|
||||
if(ret >= 0 && result){
|
||||
// FIXME?
|
||||
}
|
||||
}
|
||||
}while(nc->details.packet->stream_index != nc->details.stream_index);
|
||||
++nc->details.packet_outstanding;
|
||||
int averr = avcodec_send_packet(nc->details.codecctx, nc->details.packet);
|
||||
}while(nc->details->packet->stream_index != nc->details->stream_index);
|
||||
++nc->details->packet_outstanding;
|
||||
int averr = avcodec_send_packet(nc->details->codecctx, nc->details->packet);
|
||||
if(averr < 0){
|
||||
//fprintf(stderr, "Error processing AVPacket\n");
|
||||
return averr2ncerr(averr);
|
||||
}
|
||||
--nc->details.packet_outstanding;
|
||||
av_packet_unref(nc->details.packet);
|
||||
averr = avcodec_receive_frame(nc->details.codecctx, nc->details.frame);
|
||||
--nc->details->packet_outstanding;
|
||||
av_packet_unref(nc->details->packet);
|
||||
averr = avcodec_receive_frame(nc->details->codecctx, nc->details->frame);
|
||||
if(averr >= 0){
|
||||
have_frame = true;
|
||||
}else if(averr == AVERROR(EAGAIN) || averr == AVERROR_EOF){
|
||||
@ -159,12 +189,12 @@ int ncvisual_decode(ncvisual* nc){
|
||||
return averr2ncerr(averr);
|
||||
}
|
||||
}while(!have_frame);
|
||||
//print_frame_summary(nc->details.codecctx, nc->details.frame);
|
||||
const AVFrame* f = nc->details.frame;
|
||||
//print_frame_summary(nc->details->codecctx, nc->details->frame);
|
||||
const AVFrame* f = nc->details->frame;
|
||||
nc->rowstride = f->linesize[0];
|
||||
nc->cols = nc->details.frame->width;
|
||||
nc->rows = nc->details.frame->height;
|
||||
//fprintf(stderr, "good decode! %d/%d %d %p\n", nc->details.frame->height, nc->details.frame->width, nc->rowstride, f->data);
|
||||
nc->cols = nc->details->frame->width;
|
||||
nc->rows = nc->details->frame->height;
|
||||
//fprintf(stderr, "good decode! %d/%d %d %p\n", nc->details->frame->height, nc->details->frame->width, nc->rowstride, f->data);
|
||||
ncvisual_set_data(nc, reinterpret_cast<uint32_t*>(f->data[0]), false);
|
||||
return 0;
|
||||
}
|
||||
@ -172,7 +202,7 @@ int ncvisual_decode(ncvisual* nc){
|
||||
// resize frame to oframe, converting to RGBA (if necessary) along the way
|
||||
int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
||||
const int targformat = AV_PIX_FMT_RGBA;
|
||||
AVFrame* inf = nc->details.oframe ? nc->details.oframe : nc->details.frame;
|
||||
AVFrame* inf = nc->details->oframe ? nc->details->oframe : nc->details->frame;
|
||||
//fprintf(stderr, "got format: %d (%d/%d) want format: %d (%d/%d)\n", inf->format, nc->rows, nc->cols, targformat, rows, cols);
|
||||
if(inf->format == targformat && nc->rows == rows && nc->cols == cols){
|
||||
return 0;
|
||||
@ -231,72 +261,92 @@ int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
||||
nc->rows = rows;
|
||||
nc->cols = cols;
|
||||
ncvisual_set_data(nc, reinterpret_cast<uint32_t*>(sframe->data[0]), true);
|
||||
if(nc->details.oframe){
|
||||
//av_freep(nc->details.oframe->data);
|
||||
av_freep(&nc->details.oframe);
|
||||
if(nc->details->oframe){
|
||||
//av_freep(nc->details->oframe->data);
|
||||
av_freep(&nc->details->oframe);
|
||||
}
|
||||
nc->details.oframe = sframe;
|
||||
nc->details->oframe = sframe;
|
||||
|
||||
//fprintf(stderr, "SIZE SCALED: %d %d (%u)\n", nc->details.oframe->height, nc->details.oframe->width, nc->details.oframe->linesize[0]);
|
||||
//fprintf(stderr, "SIZE SCALED: %d %d (%u)\n", nc->details->oframe->height, nc->details->oframe->width, nc->details->oframe->linesize[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ncvisual* ncvisual_from_file(const char* filename) {
|
||||
auto ffmpeg_details_init(void) -> ncvisual_details* {
|
||||
auto deets = new ncvisual_details{};
|
||||
deets->stream_index = -1;
|
||||
deets->sub_stream_index = -1;
|
||||
if((deets->frame = av_frame_alloc()) == nullptr){
|
||||
delete deets;
|
||||
return nullptr;
|
||||
}
|
||||
return deets;
|
||||
}
|
||||
|
||||
auto ffmpeg_create() -> ncvisual* {
|
||||
auto nc = new ncvisual{};
|
||||
if((nc->details = ffmpeg_details_init()) == nullptr){
|
||||
delete nc;
|
||||
return nullptr;
|
||||
}
|
||||
return nc;
|
||||
}
|
||||
|
||||
ncvisual* ffmpeg_from_file(const char* filename) {
|
||||
AVStream* st;
|
||||
ncvisual* ncv = ncvisual_create();
|
||||
ncvisual* ncv = ffmpeg_create();
|
||||
if(ncv == nullptr){
|
||||
// fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno));
|
||||
return nullptr;
|
||||
}
|
||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details.frame);
|
||||
int averr = avformat_open_input(&ncv->details.fmtctx, filename, nullptr, nullptr);
|
||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details->frame);
|
||||
int averr = avformat_open_input(&ncv->details->fmtctx, filename, nullptr, nullptr);
|
||||
if(averr < 0){
|
||||
//fprintf(stderr, "Couldn't open %s (%d)\n", filename, averr);
|
||||
goto err;
|
||||
}
|
||||
averr = avformat_find_stream_info(ncv->details.fmtctx, nullptr);
|
||||
averr = avformat_find_stream_info(ncv->details->fmtctx, nullptr);
|
||||
if(averr < 0){
|
||||
//fprintf(stderr, "Error extracting stream info from %s (%d)\n", filename, averr);
|
||||
goto err;
|
||||
}
|
||||
//av_dump_format(ncv->details.fmtctx, 0, filename, false);
|
||||
if((averr = av_find_best_stream(ncv->details.fmtctx, AVMEDIA_TYPE_SUBTITLE, -1, -1, &ncv->details.subtcodec, 0)) >= 0){
|
||||
ncv->details.sub_stream_index = averr;
|
||||
if((ncv->details.subtcodecctx = avcodec_alloc_context3(ncv->details.subtcodec)) == nullptr){
|
||||
//av_dump_format(ncv->details->fmtctx, 0, filename, false);
|
||||
if((averr = av_find_best_stream(ncv->details->fmtctx, AVMEDIA_TYPE_SUBTITLE, -1, -1, &ncv->details->subtcodec, 0)) >= 0){
|
||||
ncv->details->sub_stream_index = averr;
|
||||
if((ncv->details->subtcodecctx = avcodec_alloc_context3(ncv->details->subtcodec)) == nullptr){
|
||||
//fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
|
||||
goto err;
|
||||
}
|
||||
// FIXME do we need avcodec_parameters_to_context() here?
|
||||
if(avcodec_open2(ncv->details.subtcodecctx, ncv->details.subtcodec, nullptr) < 0){
|
||||
if(avcodec_open2(ncv->details->subtcodecctx, ncv->details->subtcodec, nullptr) < 0){
|
||||
//fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr));
|
||||
goto err;
|
||||
}
|
||||
}else{
|
||||
ncv->details.sub_stream_index = -1;
|
||||
ncv->details->sub_stream_index = -1;
|
||||
}
|
||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details.frame);
|
||||
if((ncv->details.packet = av_packet_alloc()) == nullptr){
|
||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details->frame);
|
||||
if((ncv->details->packet = av_packet_alloc()) == nullptr){
|
||||
// fprintf(stderr, "Couldn't allocate packet for %s\n", filename);
|
||||
goto err;
|
||||
}
|
||||
if((averr = av_find_best_stream(ncv->details.fmtctx, AVMEDIA_TYPE_VIDEO, -1, -1, &ncv->details.codec, 0)) < 0){
|
||||
if((averr = av_find_best_stream(ncv->details->fmtctx, AVMEDIA_TYPE_VIDEO, -1, -1, &ncv->details->codec, 0)) < 0){
|
||||
// fprintf(stderr, "Couldn't find visuals in %s (%s)\n", filename, av_err2str(*averr));
|
||||
goto err;
|
||||
}
|
||||
ncv->details.stream_index = averr;
|
||||
if(ncv->details.codec == nullptr){
|
||||
ncv->details->stream_index = averr;
|
||||
if(ncv->details->codec == nullptr){
|
||||
//fprintf(stderr, "Couldn't find decoder for %s\n", filename);
|
||||
goto err;
|
||||
}
|
||||
st = ncv->details.fmtctx->streams[ncv->details.stream_index];
|
||||
if((ncv->details.codecctx = avcodec_alloc_context3(ncv->details.codec)) == nullptr){
|
||||
st = ncv->details->fmtctx->streams[ncv->details->stream_index];
|
||||
if((ncv->details->codecctx = avcodec_alloc_context3(ncv->details->codec)) == nullptr){
|
||||
//fprintf(stderr, "Couldn't allocate decoder for %s\n", filename);
|
||||
goto err;
|
||||
}
|
||||
if(avcodec_parameters_to_context(ncv->details.codecctx, st->codecpar) < 0){
|
||||
if(avcodec_parameters_to_context(ncv->details->codecctx, st->codecpar) < 0){
|
||||
goto err;
|
||||
}
|
||||
if(avcodec_open2(ncv->details.codecctx, ncv->details.codec, nullptr) < 0){
|
||||
if(avcodec_open2(ncv->details->codecctx, ncv->details->codec, nullptr) < 0){
|
||||
//fprintf(stderr, "Couldn't open codec for %s (%s)\n", filename, av_err2str(*averr));
|
||||
goto err;
|
||||
}
|
||||
@ -304,11 +354,11 @@ ncvisual* ncvisual_from_file(const char* filename) {
|
||||
//fprintf(stderr, "Couldn't allocate codec params for %s\n", filename);
|
||||
goto err;
|
||||
}
|
||||
if((*averr = avcodec_parameters_from_context(ncv->cparams, ncv->details.codecctx)) < 0){
|
||||
if((*averr = avcodec_parameters_from_context(ncv->cparams, ncv->details->codecctx)) < 0){
|
||||
//fprintf(stderr, "Couldn't get codec params for %s (%s)\n", filename, av_err2str(*averr));
|
||||
goto err;
|
||||
}*/
|
||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details.frame);
|
||||
//fprintf(stderr, "FRAME FRAME: %p\n", ncv->details->frame);
|
||||
// frame is set up in prep_details(), so that format can be set there, as
|
||||
// is necessary when it is prepared from inputs other than files. oframe
|
||||
// is set up whenever we convert to RGBA.
|
||||
@ -344,8 +394,8 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, float timescale,
|
||||
do{
|
||||
// codecctx seems to be off by a factor of 2 regularly. instead, go with
|
||||
// the time_base from the avformatctx.
|
||||
double tbase = av_q2d(ncv->details.fmtctx->streams[ncv->details.stream_index]->time_base);
|
||||
int64_t ts = ncv->details.frame->best_effort_timestamp;
|
||||
double tbase = av_q2d(ncv->details->fmtctx->streams[ncv->details->stream_index]->time_base);
|
||||
int64_t ts = ncv->details->frame->best_effort_timestamp;
|
||||
if(frame == 1 && ts){
|
||||
usets = true;
|
||||
}
|
||||
@ -362,8 +412,8 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, float timescale,
|
||||
activevopts.n = newn;
|
||||
}
|
||||
++frame;
|
||||
uint64_t duration = ncv->details.frame->pkt_duration * tbase * NANOSECS_IN_SEC;
|
||||
//fprintf(stderr, "use: %u dur: %ju ts: %ju cctx: %f fctx: %f\n", usets, duration, ts, av_q2d(ncv->details.codecctx->time_base), av_q2d(ncv->details.fmtctx->streams[ncv->stream_index]->time_base));
|
||||
uint64_t duration = ncv->details->frame->pkt_duration * tbase * NANOSECS_IN_SEC;
|
||||
//fprintf(stderr, "use: %u dur: %ju ts: %ju cctx: %f fctx: %f\n", usets, duration, ts, av_q2d(ncv->details->codecctx->time_base), av_q2d(ncv->details->fmtctx->streams[ncv->stream_index]->time_base));
|
||||
double schedns = nsbegin;
|
||||
if(usets){
|
||||
if(tbase == 0){
|
||||
@ -401,7 +451,7 @@ int ncvisual_stream(notcurses* nc, ncvisual* ncv, float timescale,
|
||||
int ncvisual_decode_loop(ncvisual* ncv){
|
||||
int r = ncvisual_decode(ncv);
|
||||
if(r == 1){
|
||||
if(av_seek_frame(ncv->details.fmtctx, ncv->details.stream_index, 0, AVSEEK_FLAG_FRAME) < 0){
|
||||
if(av_seek_frame(ncv->details->fmtctx, ncv->details->stream_index, 0, AVSEEK_FLAG_FRAME) < 0){
|
||||
// FIXME log error
|
||||
return -1;
|
||||
}
|
||||
@ -412,12 +462,11 @@ int ncvisual_decode_loop(ncvisual* ncv){
|
||||
return r;
|
||||
}
|
||||
|
||||
int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
const struct blitset* bset, int placey, int placex,
|
||||
int begy, int begx, int leny, int lenx,
|
||||
bool blendcolors) {
|
||||
const AVFrame* inframe = ncv->details.oframe ? ncv->details.oframe : ncv->details.frame;
|
||||
//fprintf(stderr, "inframe: %p oframe: %p frame: %p\n", inframe, ncv->details.oframe, ncv->details.frame);
|
||||
int ffmpeg_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
const struct blitset* bset, int placey, int placex,
|
||||
int begy, int begx, int leny, int lenx, bool blendcolors) {
|
||||
const AVFrame* inframe = ncv->details->oframe ? ncv->details->oframe : ncv->details->frame;
|
||||
//fprintf(stderr, "inframe: %p oframe: %p frame: %p\n", inframe, ncv->details->oframe, ncv->details->frame);
|
||||
void* data = nullptr;
|
||||
int stride = 0;
|
||||
AVFrame* sframe = nullptr;
|
||||
@ -431,14 +480,14 @@ int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
return -1;
|
||||
}
|
||||
//fprintf(stderr, "WHN NCV: %d/%d\n", inframe->width, inframe->height);
|
||||
ncv->details.swsctx = sws_getCachedContext(ncv->details.swsctx,
|
||||
ncv->details->swsctx = sws_getCachedContext(ncv->details->swsctx,
|
||||
ncv->cols, ncv->rows,
|
||||
static_cast<AVPixelFormat>(inframe->format),
|
||||
cols, rows,
|
||||
static_cast<AVPixelFormat>(targformat),
|
||||
SWS_LANCZOS, nullptr, nullptr, nullptr);
|
||||
if(ncv->details.swsctx == nullptr){
|
||||
//fprintf(stderr, "Error retrieving details.swsctx\n");
|
||||
if(ncv->details->swsctx == nullptr){
|
||||
//fprintf(stderr, "Error retrieving details->swsctx\n");
|
||||
return -1;
|
||||
}
|
||||
memcpy(sframe, inframe, sizeof(*inframe));
|
||||
@ -453,7 +502,7 @@ int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
//fprintf(stderr, "Error allocating visual data (%d X %d)\n", sframe->height, sframe->width);
|
||||
return -1;
|
||||
}
|
||||
int height = sws_scale(ncv->details.swsctx, (const uint8_t* const*)inframe->data,
|
||||
int height = sws_scale(ncv->details->swsctx, (const uint8_t* const*)inframe->data,
|
||||
inframe->linesize, 0, inframe->height, sframe->data,
|
||||
sframe->linesize);
|
||||
if(height < 0){
|
||||
@ -484,20 +533,58 @@ int ncvisual_blit(ncvisual* ncv, int rows, int cols, ncplane* n,
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto ncvisual_details_seed(ncvisual* ncv) -> void {
|
||||
assert(nullptr == ncv->details.oframe);
|
||||
ncv->details.frame->data[0] = reinterpret_cast<uint8_t*>(ncv->data);
|
||||
ncv->details.frame->data[1] = nullptr;
|
||||
ncv->details.frame->linesize[0] = ncv->rowstride;
|
||||
ncv->details.frame->linesize[1] = 0;
|
||||
ncv->details.frame->width = ncv->cols;
|
||||
ncv->details.frame->height = ncv->rows;
|
||||
ncv->details.frame->format = AV_PIX_FMT_RGBA;
|
||||
auto ffmpeg_details_seed(ncvisual* ncv) -> void {
|
||||
assert(nullptr == ncv->details->oframe);
|
||||
ncv->details->frame->data[0] = reinterpret_cast<uint8_t*>(ncv->data);
|
||||
ncv->details->frame->data[1] = nullptr;
|
||||
ncv->details->frame->linesize[0] = ncv->rowstride;
|
||||
ncv->details->frame->linesize[1] = 0;
|
||||
ncv->details->frame->width = ncv->cols;
|
||||
ncv->details->frame->height = ncv->rows;
|
||||
ncv->details->frame->format = AV_PIX_FMT_RGBA;
|
||||
}
|
||||
|
||||
int ncvisual_init(int loglevel) {
|
||||
auto ffmpeg_init(int loglevel) -> int {
|
||||
av_log_set_level(loglevel);
|
||||
// FIXME could also use av_log_set_callback() and capture the message...
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ffmpeg_printbanner(const notcurses* nc __attribute__ ((unused))){
|
||||
printf(" avformat %u.%u.%u avutil %u.%u.%u swscale %u.%u.%u\n",
|
||||
LIBAVFORMAT_VERSION_MAJOR, LIBAVFORMAT_VERSION_MINOR, LIBAVFORMAT_VERSION_MICRO,
|
||||
LIBAVUTIL_VERSION_MAJOR, LIBAVUTIL_VERSION_MINOR, LIBAVUTIL_VERSION_MICRO,
|
||||
LIBSWSCALE_VERSION_MAJOR, LIBSWSCALE_VERSION_MINOR, LIBSWSCALE_VERSION_MICRO);
|
||||
}
|
||||
|
||||
auto ffmpeg_details_destroy(ncvisual_details* deets) -> void {
|
||||
avcodec_close(deets->codecctx);
|
||||
avcodec_free_context(&deets->codecctx);
|
||||
av_frame_free(&deets->frame);
|
||||
av_freep(&deets->oframe);
|
||||
//avcodec_parameters_free(&ncv->cparams);
|
||||
sws_freeContext(deets->swsctx);
|
||||
av_packet_free(&deets->packet);
|
||||
avformat_close_input(&deets->fmtctx);
|
||||
avsubtitle_free(&deets->subtitle);
|
||||
delete deets;
|
||||
}
|
||||
|
||||
const static ncvisual_implementation ffmpeg_impl = {
|
||||
.ncvisual_init = ffmpeg_init,
|
||||
.ncvisual_decode = ffmpeg_decode,
|
||||
.ncvisual_blit = ffmpeg_blit,
|
||||
.ncvisual_create = ffmpeg_create,
|
||||
.ncvisual_from_file = ffmpeg_from_file,
|
||||
.ncvisual_printbanner = ffmpeg_printbanner,
|
||||
.ncvisual_details_seed = ffmpeg_details_seed,
|
||||
.ncvisual_details_destroy = ffmpeg_details_destroy,
|
||||
.canopen_images = true,
|
||||
.canopen_videos = true,
|
||||
};
|
||||
|
||||
static void inject_implementation(void){
|
||||
notcurses_set_ncvisual_implementation(&ffmpeg_impl);
|
||||
}
|
||||
|
||||
#endif
|
@ -1,29 +1,63 @@
|
||||
#include "builddef.h"
|
||||
#ifdef USE_OIIO
|
||||
#include "oiio.h"
|
||||
#include <OpenImageIO/filter.h>
|
||||
#include <OpenImageIO/version.h>
|
||||
#include <OpenImageIO/imageio.h>
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/imagebufalgo.h>
|
||||
#include "internal.h"
|
||||
#include "visual-details.h"
|
||||
|
||||
bool notcurses_canopen_images(const notcurses* nc __attribute__ ((unused))) {
|
||||
return true;
|
||||
static void inject_implementation(void) __attribute__ ((constructor));
|
||||
|
||||
typedef struct ncvisual_details {
|
||||
std::unique_ptr<OIIO::ImageInput> image; // must be close()d
|
||||
std::unique_ptr<uint32_t[]> frame;
|
||||
std::unique_ptr<OIIO::ImageBuf> ibuf;
|
||||
uint64_t framenum;
|
||||
} ncvisual_details;
|
||||
|
||||
static inline auto
|
||||
oiio_details_init(void) -> ncvisual_details* {
|
||||
auto deets = new ncvisual_details{};
|
||||
if(deets){
|
||||
deets->image = nullptr;
|
||||
deets->frame = nullptr;
|
||||
deets->ibuf = nullptr;
|
||||
deets->framenum = 0;
|
||||
}
|
||||
return deets;
|
||||
}
|
||||
|
||||
bool notcurses_canopen_videos(const notcurses* nc __attribute__ ((unused))) {
|
||||
return false; // too slow for reliable use at the moment
|
||||
static inline auto
|
||||
oiio_details_destroy(ncvisual_details* deets) -> void {
|
||||
if(deets->image){
|
||||
deets->image->close();
|
||||
}
|
||||
delete deets;
|
||||
}
|
||||
|
||||
ncvisual* ncvisual_from_file(const char* filename) {
|
||||
ncvisual* ncv = ncvisual_create();
|
||||
auto oiio_create() -> ncvisual* {
|
||||
auto nc = new ncvisual{};
|
||||
if((nc->details = oiio_details_init()) == nullptr){
|
||||
delete nc;
|
||||
return nullptr;
|
||||
}
|
||||
return nc;
|
||||
}
|
||||
|
||||
ncvisual* oiio_from_file(const char* filename) {
|
||||
ncvisual* ncv = oiio_create();
|
||||
if(ncv == nullptr){
|
||||
return nullptr;
|
||||
}
|
||||
ncv->details.image = OIIO::ImageInput::open(filename);
|
||||
if(!ncv->details.image){
|
||||
ncv->details->image = OIIO::ImageInput::open(filename);
|
||||
if(!ncv->details->image){
|
||||
// fprintf(stderr, "Couldn't create %s (%s)\n", filename, strerror(errno));
|
||||
ncvisual_destroy(ncv);
|
||||
return nullptr;
|
||||
}
|
||||
/*const auto &spec = ncv->details.image->spec_dimensions(0);
|
||||
/*const auto &spec = ncv->details->image->spec_dimensions(0);
|
||||
std::cout << "Opened " << filename << ": " << spec.height << "x" <<
|
||||
spec.width << "@" << spec.nchannels << " (" << spec.format << ")" << std::endl;*/
|
||||
if(ncvisual_decode(ncv)){
|
||||
@ -33,38 +67,38 @@ spec.width << "@" << spec.nchannels << " (" << spec.format << ")" << std::endl;*
|
||||
return ncv;
|
||||
}
|
||||
|
||||
int ncvisual_decode(ncvisual* nc) {
|
||||
//fprintf(stderr, "current subimage: %d frame: %p\n", nc->details.image->current_subimage(), nc->details.frame.get());
|
||||
const auto &spec = nc->details.image->spec_dimensions(nc->details.framenum);
|
||||
if(nc->details.frame){
|
||||
//fprintf(stderr, "seeking subimage: %d\n", nc->details.image->current_subimage() + 1);
|
||||
int oiio_decode(ncvisual* nc) {
|
||||
//fprintf(stderr, "current subimage: %d frame: %p\n", nc->details->image->current_subimage(), nc->details->frame.get());
|
||||
const auto &spec = nc->details->image->spec_dimensions(nc->details->framenum);
|
||||
if(nc->details->frame){
|
||||
//fprintf(stderr, "seeking subimage: %d\n", nc->details->image->current_subimage() + 1);
|
||||
OIIO::ImageSpec newspec;
|
||||
if(!nc->details.image->seek_subimage(nc->details.image->current_subimage() + 1, 0, newspec)){
|
||||
if(!nc->details->image->seek_subimage(nc->details->image->current_subimage() + 1, 0, newspec)){
|
||||
return 1;
|
||||
}
|
||||
// FIXME check newspec vis-a-vis image->spec()?
|
||||
}
|
||||
//fprintf(stderr, "SUBIMAGE: %d\n", nc->details.image->current_subimage());
|
||||
//fprintf(stderr, "SUBIMAGE: %d\n", nc->details->image->current_subimage());
|
||||
auto pixels = spec.width * spec.height;// * spec.nchannels;
|
||||
if(spec.nchannels < 3 || spec.nchannels > 4){
|
||||
return -1; // FIXME get some to test with
|
||||
}
|
||||
nc->details.frame = std::make_unique<uint32_t[]>(pixels);
|
||||
nc->details->frame = std::make_unique<uint32_t[]>(pixels);
|
||||
if(spec.nchannels == 3){ // FIXME replace with channel shuffle
|
||||
std::fill(nc->details.frame.get(), nc->details.frame.get() + pixels, 0xfffffffful);
|
||||
std::fill(nc->details->frame.get(), nc->details->frame.get() + pixels, 0xfffffffful);
|
||||
}
|
||||
//fprintf(stderr, "READING: %d %ju\n", nc->details.image->current_subimage(), nc->details.framenum);
|
||||
if(!nc->details.image->read_image(nc->details.framenum++, 0, 0, spec.nchannels, OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4), nc->details.frame.get(), 4)){
|
||||
//fprintf(stderr, "READING: %d %ju\n", nc->details->image->current_subimage(), nc->details->framenum);
|
||||
if(!nc->details->image->read_image(nc->details->framenum++, 0, 0, spec.nchannels, OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4), nc->details->frame.get(), 4)){
|
||||
return -1;
|
||||
}
|
||||
//fprintf(stderr, "READ: %d %ju\n", nc->details.image->current_subimage(), nc->details.framenum);
|
||||
//fprintf(stderr, "READ: %d %ju\n", nc->details->image->current_subimage(), nc->details->framenum);
|
||||
/*for(int i = 0 ; i < pixels ; ++i){
|
||||
//fprintf(stderr, "%06d %02x %02x %02x %02x\n", i,
|
||||
fprintf(stderr, "%06d %d %d %d %d\n", i,
|
||||
(nc->details.frame[i]) & 0xff,
|
||||
(nc->details.frame[i] >> 8) & 0xff,
|
||||
(nc->details.frame[i] >> 16) & 0xff,
|
||||
nc->details.frame[i] >> 24
|
||||
(nc->details->frame[i]) & 0xff,
|
||||
(nc->details->frame[i] >> 8) & 0xff,
|
||||
(nc->details->frame[i] >> 16) & 0xff,
|
||||
nc->details->frame[i] >> 24
|
||||
);
|
||||
}*/
|
||||
nc->cols = spec.width;
|
||||
@ -72,10 +106,10 @@ int ncvisual_decode(ncvisual* nc) {
|
||||
nc->rowstride = nc->cols * 4;
|
||||
OIIO::ImageSpec rgbaspec = spec;
|
||||
rgbaspec.nchannels = 4;
|
||||
nc->details.ibuf = std::make_unique<OIIO::ImageBuf>(rgbaspec, nc->details.frame.get());
|
||||
//fprintf(stderr, "SUBS: %d\n", nc->details.ibuf->nsubimages());
|
||||
ncvisual_set_data(nc, static_cast<uint32_t*>(nc->details.ibuf->localpixels()), false);
|
||||
//fprintf(stderr, "POST-DECODE DATA: %d %d %p %p\n", nc->rows, nc->cols, nc->data, nc->details.ibuf->localpixels());
|
||||
nc->details->ibuf = std::make_unique<OIIO::ImageBuf>(rgbaspec, nc->details->frame.get());
|
||||
//fprintf(stderr, "SUBS: %d\n", nc->details->ibuf->nsubimages());
|
||||
ncvisual_set_data(nc, static_cast<uint32_t*>(nc->details->ibuf->localpixels()), false);
|
||||
//fprintf(stderr, "POST-DECODE DATA: %d %d %p %p\n", nc->rows, nc->cols, nc->data, nc->details->ibuf->localpixels());
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -83,10 +117,10 @@ int ncvisual_decode_loop(ncvisual* ncv){
|
||||
int r = ncvisual_decode(ncv);
|
||||
if(r == 1){
|
||||
OIIO::ImageSpec newspec;
|
||||
if(ncv->details.image->seek_subimage(0, 0, newspec)){
|
||||
if(ncv->details->image->seek_subimage(0, 0, newspec)){
|
||||
return -1;
|
||||
}
|
||||
ncv->details.framenum = 0;
|
||||
ncv->details->framenum = 0;
|
||||
if(ncvisual_decode(ncv) < 0){
|
||||
return -1;
|
||||
}
|
||||
@ -98,13 +132,13 @@ int ncvisual_decode_loop(ncvisual* ncv){
|
||||
int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
||||
//fprintf(stderr, "%d/%d -> %d/%d on the resize\n", ncv->rows, ncv->cols, rows, cols);
|
||||
auto ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||
if(nc->details.ibuf && (nc->cols != cols || nc->rows != rows)){ // scale it
|
||||
if(nc->details->ibuf && (nc->cols != cols || nc->rows != rows)){ // scale it
|
||||
OIIO::ImageSpec sp{};
|
||||
sp.width = cols;
|
||||
sp.height = rows;
|
||||
ibuf->reset(sp, OIIO::InitializePixels::Yes);
|
||||
OIIO::ROI roi(0, cols, 0, rows, 0, 1, 0, 4);
|
||||
if(!OIIO::ImageBufAlgo::resize(*ibuf, *nc->details.ibuf, "", 0, roi)){
|
||||
if(!OIIO::ImageBufAlgo::resize(*ibuf, *nc->details->ibuf, "", 0, roi)){
|
||||
return -1;
|
||||
}
|
||||
nc->cols = cols;
|
||||
@ -112,12 +146,12 @@ int ncvisual_resize(ncvisual* nc, int rows, int cols) {
|
||||
nc->rowstride = cols * 4;
|
||||
ncvisual_set_data(nc, static_cast<uint32_t*>(ibuf->localpixels()), false);
|
||||
//fprintf(stderr, "HAVE SOME NEW DATA: %p\n", ibuf->localpixels());
|
||||
nc->details.ibuf = std::move(ibuf);
|
||||
nc->details->ibuf = std::move(ibuf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ncvisual_blit(struct ncvisual* ncv, int rows, int cols,
|
||||
int oiio_blit(struct ncvisual* ncv, int rows, int cols,
|
||||
ncplane* n, const struct blitset* bset,
|
||||
int placey, int placex, int begy, int begx,
|
||||
int leny, int lenx, bool blendcolors) {
|
||||
@ -125,13 +159,13 @@ int ncvisual_blit(struct ncvisual* ncv, int rows, int cols,
|
||||
void* data = nullptr;
|
||||
int stride = 0;
|
||||
auto ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||
if(ncv->details.ibuf && (ncv->cols != cols || ncv->rows != rows)){ // scale it
|
||||
if(ncv->details->ibuf && (ncv->cols != cols || ncv->rows != rows)){ // scale it
|
||||
OIIO::ImageSpec sp{};
|
||||
sp.width = cols;
|
||||
sp.height = rows;
|
||||
ibuf->reset(sp, OIIO::InitializePixels::Yes);
|
||||
OIIO::ROI roi(0, cols, 0, rows, 0, 1, 0, 4);
|
||||
if(!OIIO::ImageBufAlgo::resize(*ibuf, *ncv->details.ibuf, "", 0, roi)){
|
||||
if(!OIIO::ImageBufAlgo::resize(*ibuf, *ncv->details->ibuf, "", 0, roi)){
|
||||
return -1;
|
||||
}
|
||||
stride = cols * 4;
|
||||
@ -205,37 +239,53 @@ char* ncvisual_subtitle(const ncvisual* ncv) { // no support in OIIO
|
||||
/*
|
||||
auto ncvisual_rotate(ncvisual* ncv, double rads) -> int {
|
||||
OIIO::ROI roi(0, ncv->cols, 0, ncv->rows, 0, 1, 0, 4);
|
||||
auto tmpibuf = std::move(*ncv->details.ibuf);
|
||||
ncv->details.ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||
auto tmpibuf = std::move(*ncv->details->ibuf);
|
||||
ncv->details->ibuf = std::make_unique<OIIO::ImageBuf>();
|
||||
OIIO::ImageSpec sp{};
|
||||
sp.set_format(OIIO::TypeDesc(OIIO::TypeDesc::UINT8, 4));
|
||||
sp.nchannels = 4;
|
||||
ncv->details.ibuf->reset();
|
||||
if(!OIIO::ImageBufAlgo::rotate(*ncv->details.ibuf, tmpibuf, rads, "", 0, true, roi)){
|
||||
ncv->details->ibuf->reset();
|
||||
if(!OIIO::ImageBufAlgo::rotate(*ncv->details->ibuf, tmpibuf, rads, "", 0, true, roi)){
|
||||
return NCERR_DECODE; // FIXME need we do anything further?
|
||||
}
|
||||
ncv->rowstride = ncv->cols * 4;
|
||||
ncvisual_set_data(ncv, static_cast<uint32_t*>(ncv->details.ibuf->localpixels()), false);
|
||||
ncvisual_set_data(ncv, static_cast<uint32_t*>(ncv->details->ibuf->localpixels()), false);
|
||||
return NCERR_SUCCESS;
|
||||
}
|
||||
*/
|
||||
|
||||
auto ncvisual_details_seed(ncvisual* ncv) -> void {
|
||||
auto oiio_details_seed(ncvisual* ncv) -> void {
|
||||
(void)ncv;
|
||||
// FIXME?
|
||||
}
|
||||
|
||||
int ncvisual_init(int loglevel) {
|
||||
int oiio_init(int loglevel) {
|
||||
// FIXME set OIIO global attribute "debug" based on loglevel
|
||||
(void)loglevel;
|
||||
// FIXME check OIIO_VERSION_STRING components against linked openimageio_version()
|
||||
return 0; // allow success here
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// FIXME would be nice to have OIIO::attributes("libraries") in here
|
||||
const char* oiio_version(void){
|
||||
return OIIO_VERSION_STRING;
|
||||
void oiio_printbanner(const notcurses* nc __attribute__ ((unused))){
|
||||
printf(" openimageio %s\n", OIIO_VERSION_STRING);
|
||||
}
|
||||
|
||||
const static ncvisual_implementation oiio_impl = {
|
||||
.ncvisual_init = oiio_init,
|
||||
.ncvisual_decode = oiio_decode,
|
||||
.ncvisual_blit = oiio_blit,
|
||||
.ncvisual_create = oiio_create,
|
||||
.ncvisual_from_file = oiio_from_file,
|
||||
.ncvisual_printbanner = oiio_printbanner,
|
||||
.ncvisual_details_seed = oiio_details_seed,
|
||||
.ncvisual_details_destroy = oiio_details_destroy,
|
||||
.canopen_images = true,
|
||||
.canopen_videos = false,
|
||||
};
|
||||
|
||||
static void inject_implementation(void){
|
||||
notcurses_set_ncvisual_implementation(&oiio_impl);
|
||||
}
|
||||
|
||||
#endif
|
@ -8,5 +8,5 @@ Description: C++ bindings for notcurses
|
||||
Version: @PROJECT_VERSION@
|
||||
|
||||
Requires: notcurses >= @PROJECT_VERSION@
|
||||
Libs: -L${libdir} -lnotcurses++
|
||||
Libs: -L${libdir} -lnotcurses -lnotcurses++
|
||||
Cflags: -I${includedir}
|
||||
|
12
tools/notcurses-core.pc.in
Normal file
12
tools/notcurses-core.pc.in
Normal file
@ -0,0 +1,12 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=@CMAKE_INSTALL_PREFIX@
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: TUI library for modern terminal emulators
|
||||
Version: @PROJECT_VERSION@
|
||||
|
||||
Requires:
|
||||
Libs: -L${libdir} -lnotcurses-core
|
||||
Cflags: -I${includedir}
|
@ -8,5 +8,5 @@ Description: TUI library for modern terminal emulators
|
||||
Version: @PROJECT_VERSION@
|
||||
|
||||
Requires:
|
||||
Libs: -L${libdir} -lnotcurses
|
||||
Libs: -L${libdir} -lnotcurses-core -lnotcurses
|
||||
Cflags: -I${includedir}
|
||||
|
Loading…
x
Reference in New Issue
Block a user