pull/2122/head
nick black 3 years ago
commit 5949d1bf50

@ -5,7 +5,7 @@ name: debian-unstable
steps: steps:
- name: debian-build - name: debian-build
image: dankamongmen/unstable_builder:2021-07-02a image: dankamongmen/unstable_builder:2021-08-19a
commands: commands:
- export LANG=en_US.UTF-8 - export LANG=en_US.UTF-8
- export TERM=xterm - export TERM=xterm
@ -21,9 +21,9 @@ steps:
- LDFLAGS=-L/usr/local/lib CFLAGS=-I/usr/local/include python3 setup.py sdist build install - LDFLAGS=-L/usr/local/lib CFLAGS=-I/usr/local/include python3 setup.py sdist build install
- env LD_LIBRARY_PATH=/usr/local/lib ./notcurses-pydemo > /dev/null - env LD_LIBRARY_PATH=/usr/local/lib ./notcurses-pydemo > /dev/null
- env LD_LIBRARY_PATH=/usr/local/lib ./ncdirect-pydemo > /dev/null - env LD_LIBRARY_PATH=/usr/local/lib ./ncdirect-pydemo > /dev/null
#- cd ../rust - cd ../rust
#- rustc --version - rustc --version
#- cargo build - cargo build
#- cargo t_all #- cargo t_all
--- ---
kind: pipeline kind: pipeline
@ -56,11 +56,11 @@ steps:
--- ---
kind: pipeline kind: pipeline
type: docker type: docker
name: ubuntu-hirsute name: ubuntu-impish
steps: steps:
- name: ubuntu-build - name: ubuntu-build
image: dankamongmen/hirsute:2021-07-02a image: dankamongmen/impish:2021-08-19a
commands: commands:
- export LANG=es_ES.UTF-8 - export LANG=es_ES.UTF-8
- export TERM=xterm - export TERM=xterm
@ -75,19 +75,19 @@ steps:
- python3 setup.py sdist build - python3 setup.py sdist build
- cd ../python - cd ../python
- LDFLAGS=-L/usr/local/lib CFLAGS=-I/usr/local/include python3 setup.py sdist build install - LDFLAGS=-L/usr/local/lib CFLAGS=-I/usr/local/include python3 setup.py sdist build install
#--- ---
#kind: pipeline kind: pipeline
#type: docker type: docker
#name: alpine-edge name: alpine-edge
#
#steps: steps:
#- name: alpine-edge - name: alpine-edge
# image: dankamongmen/edge_builder:2021-08-05 image: dankamongmen/edge_builder:2021-08-05
# commands: commands:
# - export LANG=en_US.UTF-8 - export LANG=en_US.UTF-8
# - export TERM=vt100 - export TERM=vt100
# - mkdir build - mkdir build
# - cd build - cd build
# - cmake -DCMAKE_BUILD_TYPE=Release -DUSE_PANDOC=off .. - cmake -DCMAKE_BUILD_TYPE=Release -DUSE_PANDOC=off ..
# - make -j2 - make -j2
# - ctest --output-on-failure - ctest --output-on-failure

@ -58,22 +58,21 @@ jobs:
pip install cffi pypandoc pip install cffi pypandoc
cd cffi cd cffi
python3 setup.py sdist build python3 setup.py sdist build
sudo python3 setup.py install #sudo python3 setup.py install
notcurses-pydemo > /dev/null #notcurses-pydemo > /dev/null
ncdirect-pydemo > /dev/null #ncdirect-pydemo > /dev/null
# temporarily disabled while failing - name: python wrappers (new)
# - name: python wrappers (new) run: |
#run: | cd python
#cd python python3 setup.py build
#python3 setup.py build
#sudo python3 setup.py install #sudo python3 setup.py install
#python3 examples/000-print-version.py #python3 examples/000-print-version.py
#- name: rust wrappers - name: rust wrappers
#run: | run: |
#export PKG_CONFIG_PATH="/usr/local/libdata/pkgconfig" export PKG_CONFIG_PATH="/usr/local/libdata/pkgconfig"
#cd rust cd rust
#rustc --version rustc --version
#cargo build #cargo build
#cargo t_all #cargo t_all

@ -82,10 +82,9 @@ jobs:
sudo python3 setup.py install sudo python3 setup.py install
python3 examples/000-print-version.py python3 examples/000-print-version.py
# temporarily disabled while broken - name: rust wrappers
#- name: rust wrappers run: |
#run: | cd rust
# cd rust rustc --version
# rustc --version cargo build
# cargo build
#cargo t_all #cargo t_all

@ -35,6 +35,7 @@ jobs:
mingw-w64-ucrt-x86_64-libunistring mingw-w64-ucrt-x86_64-libunistring
mingw-w64-ucrt-x86_64-ncurses mingw-w64-ucrt-x86_64-ncurses
mingw-w64-ucrt-x86_64-toolchain mingw-w64-ucrt-x86_64-toolchain
mingw-w64-ucrt-x86_64-rust
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@ -85,10 +86,9 @@ jobs:
#notcurses-pydemo > /dev/null #notcurses-pydemo > /dev/null
#ncdirect-pydemo > /dev/null #ncdirect-pydemo > /dev/null
#- name: rust wrappers - name: rust wrappers
#mingw-w64-ucrt-x86_64-rust run: |
#run: | cd rust
#cd rust rustc --version
#rustc --version
#cargo build #cargo build
#cargo t_all #cargo t_all

@ -0,0 +1,3 @@
{
"cmake.configureSettings": { "USE_PANDOC": "off", "USE_DOCTEST": "off" }
}

@ -1,6 +1,6 @@
# 3.14.0 introduced NAME_WLE # 3.14.0 introduced NAME_WLE
cmake_minimum_required(VERSION 3.14.0) cmake_minimum_required(VERSION 3.14.0)
project(notcurses VERSION 2.3.13 project(notcurses VERSION 2.3.17
DESCRIPTION "Blingful UI for modern terminal emulators" DESCRIPTION "Blingful UI for modern terminal emulators"
HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses" HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
LANGUAGES C CXX) LANGUAGES C CXX)
@ -24,6 +24,7 @@ cmake_dependent_option(
"${BUILD_TESTING}" OFF "${BUILD_TESTING}" OFF
) )
option(USE_DOXYGEN "Build HTML cross reference with doxygen" OFF) option(USE_DOXYGEN "Build HTML cross reference with doxygen" OFF)
option(USE_GPM "Enable libgpm console mouse support" OFF)
option(USE_PANDOC "Build man pages and HTML reference with pandoc" ON) option(USE_PANDOC "Build man pages and HTML reference with pandoc" ON)
option(USE_POC "Build small, uninstalled proof-of-concept binaries" ON) option(USE_POC "Build small, uninstalled proof-of-concept binaries" ON)
option(USE_QRCODEGEN "Enable libqrcodegen QR code support" OFF) option(USE_QRCODEGEN "Enable libqrcodegen QR code support" OFF)
@ -47,6 +48,9 @@ elseif(NOT ${USE_MULTIMEDIA} STREQUAL "none")
message(FATAL_ERROR "USE_MULTIMEDIA must be one of 'oiio', 'ffmpeg', 'none' (was '${USE_MULTIMEDIA}')") message(FATAL_ERROR "USE_MULTIMEDIA must be one of 'oiio', 'ffmpeg', 'none' (was '${USE_MULTIMEDIA}')")
endif() endif()
message(STATUS "Requested multimedia engine: ${USE_MULTIMEDIA}")
message(STATUS "Requested build mode: ${CMAKE_BUILD_TYPE}")
string(APPEND CMAKE_C_FLAGS_DEBUG " -Og") string(APPEND CMAKE_C_FLAGS_DEBUG " -Og")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og") string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og")
if("${USE_COVERAGE}") if("${USE_COVERAGE}")
@ -61,6 +65,9 @@ if("${USE_COVERAGE}")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping") string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
endif() endif()
# under msys2 (and all other operating systems) we want pkgconfig. when
# building with visual studio, don't require it.
if(NOT MSVC)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig") set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
elseif(APPLE) elseif(APPLE)
@ -73,34 +80,7 @@ set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
else() else()
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig") set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
endif() endif()
# global compiler flags
add_compile_definitions(_FORTIFY_SOURCE=2)
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -Wformat-security
-fexceptions -fstrict-aliasing)
message(STATUS "Requested multimedia engine: ${USE_MULTIMEDIA}")
message(STATUS "Requested build mode: ${CMAKE_BUILD_TYPE}")
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
# don't use REQUIRED with subsequent find_package() operations; we use
# feature_summary + set_package_properties to fail in one fell swoop.
find_package(Threads)
set_package_properties(Threads PROPERTIES TYPE REQUIRED)
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES TYPE REQUIRED)
# platform-specific logics
if(WIN32)
set(LIBRT_LIBRARIES wsock32 ws2_32)
elseif(NOT APPLE)
find_library(LIBM m REQUIRED)
find_library(LIBRT rt REQUIRED)
endif()
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
set(CMAKE_REQUIRED_INCLUDES /usr/local/include)
endif()
# some distros (<cough>motherfucking alpine</cough> subsume terminfo directly # some distros (<cough>motherfucking alpine</cough> subsume terminfo directly
# into ncurses. accept either, and may god have mercy on our souls. # into ncurses. accept either, and may god have mercy on our souls.
pkg_search_module(TERMINFO REQUIRED tinfo>=6.1 ncursesw>=6.1) pkg_search_module(TERMINFO REQUIRED tinfo>=6.1 ncursesw>=6.1)
@ -124,6 +104,31 @@ elseif(${USE_OIIO})
pkg_check_modules(OIIO REQUIRED OpenImageIO>=2.1) pkg_check_modules(OIIO REQUIRED OpenImageIO>=2.1)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND OpenImageIO) set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND OpenImageIO)
endif() endif()
endif()
# global compiler flags
add_compile_definitions(_FORTIFY_SOURCE=2)
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -Wformat-security
-fexceptions -fstrict-aliasing)
# don't use REQUIRED with subsequent find_package() operations; we use
# feature_summary + set_package_properties to fail in one fell swoop.
find_package(Threads)
set_package_properties(Threads PROPERTIES TYPE REQUIRED)
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES TYPE REQUIRED)
# platform-specific logics
if(WIN32)
set(LIBRT_LIBRARIES wsock32 ws2_32)
elseif(NOT APPLE)
find_library(LIBM m REQUIRED)
find_library(LIBRT rt REQUIRED)
endif()
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
include_directories(/usr/local/include)
link_directories(/usr/local/lib)
set(CMAKE_REQUIRED_INCLUDES /usr/local/include)
endif()
if(${USE_DOCTEST}) if(${USE_DOCTEST})
find_package(doctest 2.3.5) find_package(doctest 2.3.5)
@ -141,6 +146,16 @@ endif()
find_library(unistring unistring REQUIRED) find_library(unistring unistring REQUIRED)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND libunistring) set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND libunistring)
set_package_properties(libunistring PROPERTIES TYPE REQUIRED) set_package_properties(libunistring PROPERTIES TYPE REQUIRED)
if(${USE_GPM}) # no pkgconfig from gpm
unset(HAVE_GPM_H CACHE)
check_include_file("gpm.h" HAVE_GPM_H)
if(NOT "${HAVE_GPM_H}")
message(FATAL_ERROR "Couldn't find gpm.h from libgpm")
endif()
find_library(gpm gpm REQUIRED)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND libgpm)
set_package_properties(libgpm PROPERTIES TYPE REQUIRED)
endif()
unset(HAVE_QRCODEGEN_H CACHE) unset(HAVE_QRCODEGEN_H CACHE)
if("${USE_QRCODEGEN}") if("${USE_QRCODEGEN}")
check_include_file("qrcodegen/qrcodegen.h" HAVE_QRCODEGEN_H) check_include_file("qrcodegen/qrcodegen.h" HAVE_QRCODEGEN_H)
@ -187,6 +202,7 @@ set_target_properties(notcurses-core-static PROPERTIES
OUTPUT_NAME notcurses-core OUTPUT_NAME notcurses-core
) )
target_include_directories(notcurses-core target_include_directories(notcurses-core
BEFORE
PRIVATE PRIVATE
include include
src src
@ -196,6 +212,7 @@ target_include_directories(notcurses-core
"${ZLIB_INCLUDE_DIRS}" "${ZLIB_INCLUDE_DIRS}"
) )
target_include_directories(notcurses-core-static target_include_directories(notcurses-core-static
BEFORE
PRIVATE PRIVATE
include include
src src
@ -210,6 +227,7 @@ target_link_libraries(notcurses-core
"${TERMINFO_LIBRARIES}" "${TERMINFO_LIBRARIES}"
"${LIBM}" "${LIBM}"
"${unistring}" "${unistring}"
"${gpm}"
PUBLIC PUBLIC
Threads::Threads Threads::Threads
"${LIBRT_LIBRARIES}" "${LIBRT_LIBRARIES}"
@ -220,6 +238,7 @@ target_link_libraries(notcurses-core-static
"${TERMINFO_STATIC_LIBRARIES}" "${TERMINFO_STATIC_LIBRARIES}"
"${LIBM}" "${LIBM}"
"${unistring}" "${unistring}"
"${gpm}"
PUBLIC PUBLIC
Threads::Threads Threads::Threads
"${LIBRT_LIBRARIES}" "${LIBRT_LIBRARIES}"
@ -257,19 +276,19 @@ set_target_properties(notcurses-static PROPERTIES
OUTPUT_NAME notcurses OUTPUT_NAME notcurses
) )
target_include_directories(notcurses target_include_directories(notcurses
BEFORE
PRIVATE PRIVATE
include include
src src
src/lib
"${CMAKE_REQUIRED_INCLUDES}" "${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include" "${PROJECT_BINARY_DIR}/include"
"${TERMINFO_INCLUDE_DIRS}" "${TERMINFO_INCLUDE_DIRS}"
) )
target_include_directories(notcurses-static target_include_directories(notcurses-static
BEFORE
PRIVATE PRIVATE
include include
src src
src/lib
"${CMAKE_REQUIRED_INCLUDES}" "${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include" "${PROJECT_BINARY_DIR}/include"
"${TERMINFO_INCLUDE_DIRS}" "${TERMINFO_INCLUDE_DIRS}"
@ -395,16 +414,19 @@ set_target_properties(
OUTPUT_NAME "notcurses++") OUTPUT_NAME "notcurses++")
set(NCPP_INCLUDE_DIRS set(NCPP_INCLUDE_DIRS
include "include"
"src"
"${PROJECT_BINARY_DIR}/include" "${PROJECT_BINARY_DIR}/include"
"${TERMINFO_INCLUDE_DIRS}" "${TERMINFO_INCLUDE_DIRS}"
) )
target_include_directories(notcurses++ target_include_directories(notcurses++
BEFORE
PRIVATE ${NCPP_INCLUDE_DIRS} PRIVATE ${NCPP_INCLUDE_DIRS}
) )
target_include_directories(notcurses++-static target_include_directories(notcurses++-static
BEFORE
PRIVATE ${NCPP_INCLUDE_DIRS} PRIVATE ${NCPP_INCLUDE_DIRS}
) )
@ -537,7 +559,6 @@ target_compile_definitions(notcurses-info
target_include_directories(notcurses-info target_include_directories(notcurses-info
PRIVATE PRIVATE
src src
src/lib
include include
"${CMAKE_REQUIRED_INCLUDES}" "${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include" "${PROJECT_BINARY_DIR}/include"
@ -646,12 +667,12 @@ if(${USE_DOCTEST})
file(GLOB TESTSRCS CONFIGURE_DEPENDS src/tests/*.cpp) file(GLOB TESTSRCS CONFIGURE_DEPENDS src/tests/*.cpp)
add_executable(notcurses-tester ${TESTSRCS}) add_executable(notcurses-tester ${TESTSRCS})
target_include_directories(notcurses-tester target_include_directories(notcurses-tester
BEFORE
PRIVATE PRIVATE
include include
src src
"${CMAKE_REQUIRED_INCLUDES}" "${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include" "${PROJECT_BINARY_DIR}/include"
src/lib
"${TERMINFO_INCLUDE_DIRS}" "${TERMINFO_INCLUDE_DIRS}"
) )
target_link_libraries(notcurses-tester target_link_libraries(notcurses-tester

@ -12,7 +12,7 @@ prepackaged on many distributions. Otherwise, acquire the current source via
Install build dependencies: Install build dependencies:
`apt-get install build-essential cmake doctest-dev zlib1g-dev libavformat-dev libavutil-dev libncurses-dev libreadline-dev libqrcodegen-dev libswscale-dev libunistring-dev pandoc pkg-config` `apt-get install build-essential cmake doctest-dev zlib1g-dev libavformat-dev libavutil-dev libgpm-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), you If you only intend to build core Notcurses (without multimedia support), you
can omit `libavformat-dev`, `libavutil-dev`, and `libswscale-dev` from this can omit `libavformat-dev`, `libavutil-dev`, and `libswscale-dev` from this
@ -30,7 +30,7 @@ If you want to build the Rust wrappers, you'll also need:
Install build dependencies: Install build dependencies:
`dnf install cmake doctest-devel zlib-devel ncurses-devel readline-devel libqrcodegen-devel libunistring-devel OpenImageIO-devel pandoc` `dnf install cmake doctest-devel zlib-devel ncurses-devel gpm-devel readline-devel libqrcodegen-devel libunistring-devel OpenImageIO-devel pandoc`
If you only intend to build core Notcurses (without multimedia support), you If you only intend to build core Notcurses (without multimedia support), you
can omit `OpenImageIO-devel`. If you're building outside Fedora Core (e.g. with can omit `OpenImageIO-devel`. If you're building outside Fedora Core (e.g. with
@ -109,6 +109,7 @@ but must be `Debug` for use of `USE_COVERAGE`.
* `USE_COVERAGE`: build coverage support (for developers, requires use of Clang) * `USE_COVERAGE`: build coverage support (for developers, requires use of Clang)
* `USE_DOCTEST`: build `notcurses-tester` with Doctest, requires `BUILD_TESTING` * `USE_DOCTEST`: build `notcurses-tester` with Doctest, requires `BUILD_TESTING`
* `USE_DOXYGEN`: build interlinked HTML documentation with Doxygen * `USE_DOXYGEN`: build interlinked HTML documentation with Doxygen
* `USE_GPM`: build GPM console mouse support via libgpm
* `USE_MULTIMEDIA`: `ffmpeg` for FFmpeg, `oiio` for OpenImageIO, `none` for none * `USE_MULTIMEDIA`: `ffmpeg` for FFmpeg, `oiio` for OpenImageIO, `none` for none
* `USE_PANDOC`: build man pages with pandoc * `USE_PANDOC`: build man pages with pandoc
* `USE_POC`: build small, uninstalled proof-of-concept binaries * `USE_POC`: build small, uninstalled proof-of-concept binaries

@ -1,12 +1,26 @@
This document attempts to list user-visible changes and any major internal This document attempts to list user-visible changes and any major internal
rearrangements of Notcurses. rearrangements of Notcurses.
* 2.3.14 (not yet released) * 2.3.17 (2021-08-22)
* Added `notcurses_enter_alternate_screen()` and
`notcurses_leave_alternate_screen()`.
* Added `ncplane_boundlist()`.
* Plots now support `NCBLIT_PIXEL`!
* 2.3.16 (2021-08-19)
* Fix `ncdirect_set_*_rgb()` for the case where an emulator has fewer than
8 colors, i.e. vt100. This release exists to make unit tests work again
on the Alpine and Fedora buildservers.
* 2.3.15 (2021-08-17)
* `ncneofetch` has been changed to use "CLI mode" instead of Direct Mode, * `ncneofetch` has been changed to use "CLI mode" instead of Direct Mode,
as a proof of concept. It is very likely that Direct Mode will be as a proof of concept. It is very likely that Direct Mode will be
deprecated for ABI3. New code ought not be written using it. deprecated for ABI3. New code ought not be written using it.
* Added `ncplane_scrollup()` and `ncplane_scrollup_child()`. * Added `ncplane_scrollup()` and `ncplane_scrollup_child()`.
* Fixed grotesque errors in `ncplane_set_*_palindex()`. * Fixed grotesque errors in `ncplane_set_*_palindex()`.
* Removed support for the iTerm2 graphics protocol, which is unsuitable for
the Notcurses model. macOS users who want graphics are recommended to use
Kitty or WezTerm. It will be added back if it gains necessary capabilities.
* 2.3.13 (2021-08-04) * 2.3.13 (2021-08-04)
* Added the portable utility functions `notcurses_accountname()` and * Added the portable utility functions `notcurses_accountname()` and

@ -88,6 +88,9 @@ Why use this non-standard library?
and transparent regions. All APIs natively support 24-bit color, quantized and transparent regions. All APIs natively support 24-bit color, quantized
down as necessary for the terminal. down as necessary for the terminal.
* Portable support for bitmapped graphics, whether using Sixel, Kitty, the iTerm2
protocol, or even the Linux framebuffer console.
* It's Apache2-licensed in its entirety, as opposed to the * It's Apache2-licensed in its entirety, as opposed to the
[drama in several acts](https://invisible-island.net/ncurses/ncurses-license.html) [drama in several acts](https://invisible-island.net/ncurses/ncurses-license.html)
that is the NCURSES license (the latter is [summarized](https://invisible-island.net/ncurses/ncurses-license.html#issues_freer) that is the NCURSES license (the latter is [summarized](https://invisible-island.net/ncurses/ncurses-license.html#issues_freer)
@ -107,6 +110,7 @@ may well be possible to use still older versions. Let me know of any successes!
* (OPTIONAL) (OpenImageIO, testing, C++ bindings): A C++17 compiler * (OPTIONAL) (OpenImageIO, testing, C++ bindings): A C++17 compiler
* (build+runtime) From [NCURSES](https://invisible-island.net/ncurses/announce.html): terminfo 6.1+ * (build+runtime) From [NCURSES](https://invisible-island.net/ncurses/announce.html): terminfo 6.1+
* (build+runtime) GNU [libunistring](https://www.gnu.org/software/libunistring/) 0.9.10+ * (build+runtime) GNU [libunistring](https://www.gnu.org/software/libunistring/) 0.9.10+
* (OPTIONAL) (build+runtime) [libgpm](https://www.nico.schottelius.org/software/gpm/) 1.20+
* (OPTIONAL) (build+runtime) GNU [Readline](https://www.gnu.org/software/readline/) 8.0+ * (OPTIONAL) (build+runtime) GNU [Readline](https://www.gnu.org/software/readline/) 8.0+
* (OPTIONAL) (build+runtime) From QR-Code-generator: [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+ * (OPTIONAL) (build+runtime) From QR-Code-generator: [libqrcodegen](https://github.com/nayuki/QR-Code-generator) 1.5.0+
* (OPTIONAL) (build+runtime) From [FFmpeg](https://www.ffmpeg.org/): libswscale 5.0+, libavformat 57.0+, libavutil 56.0+ * (OPTIONAL) (build+runtime) From [FFmpeg](https://www.ffmpeg.org/): libswscale 5.0+, libavformat 57.0+, libavutil 56.0+

@ -78,6 +78,7 @@ relies on the font. Patches to correct/complete this table are very welcome!
| [kmscon](https://github.com/dvdhrm/kmscon) | | ❌ | ❌ |`TERM=xterm-256color` | No RGB color AFAICT, nor any distinct terminfo entry. No actual `ccc` implementation. Sets `COLORTERM=kmscon`.| | [kmscon](https://github.com/dvdhrm/kmscon) | | ❌ | ❌ |`TERM=xterm-256color` | No RGB color AFAICT, nor any distinct terminfo entry. No actual `ccc` implementation. Sets `COLORTERM=kmscon`.|
| [Konsole](https://invent.kde.org/utilities/konsole) | ❌ | ❌ |? |`TERM=konsole-direct` | | | [Konsole](https://invent.kde.org/utilities/konsole) | ❌ | ❌ |? |`TERM=konsole-direct` | |
| Linux console | ❌ | ✅ |see [below](#the-linux-console) |`TERM=linux` `COLORTERM=24bit` | 8 (512 glyph fonts) or 16 (256 glyph fonts) colors max, but RGB values are downsampled to a 256-index palette. See below. | | Linux console | ❌ | ✅ |see [below](#the-linux-console) |`TERM=linux` `COLORTERM=24bit` | 8 (512 glyph fonts) or 16 (256 glyph fonts) colors max, but RGB values are downsampled to a 256-index palette. See below. |
| [mintty](https://github.com/mintty/mintty) | ? | ? | ? | ? | ? |
| [mlterm](https://github.com/arakiken/mlterm) | ✅ | ❌ |? |`TERM=mlterm-256color` | Do not set `COLORTERM`. `mlterm-direct` gives strange results. | | [mlterm](https://github.com/arakiken/mlterm) | ✅ | ❌ |? |`TERM=mlterm-256color` | Do not set `COLORTERM`. `mlterm-direct` gives strange results. |
| [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) | | ❌ |❌ |`TERM=putty-256color` `COLORTERM=24bit` | | | [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/latest.html) | | ❌ |❌ |`TERM=putty-256color` `COLORTERM=24bit` | |
| rxvt | ✅ | ? |? | | Seems unmaintained; many forks exist. | | rxvt | ✅ | ? |? | | Seems unmaintained; many forks exist. |
@ -118,7 +119,7 @@ Color Erase) capability, but Notcurses never relies on `bce` behavior.
### WezTerm ### WezTerm
WezTerm [implements](https://wezfurlong.org/wezterm/escape-sequences.html) some WezTerm [implements](https://wezfurlong.org/wezterm/escape-sequences.html) some
interesting underline options, and the iTerm2 graphic protocol. interesting underline options, and both the Sixel and Kitty graphic protocols.
### GNU screen ### GNU screen

@ -126,9 +126,6 @@ typedef struct notcurses_options {
// the environment variable TERM is used. Failure to open the terminal // the environment variable TERM is used. Failure to open the terminal
// definition will result in failure to initialize Notcurses. // definition will result in failure to initialize Notcurses.
const char* termtype; const char* termtype;
// If non-NULL, notcurses_render() will write each rendered frame to this
// FILE* in addition to outfp. This is used primarily for debugging.
FILE* renderfp;
// Progressively higher log levels result in more logging to stderr. By // Progressively higher log levels result in more logging to stderr. By
// default, nothing is printed to stderr once fullscreen service begins. // default, nothing is printed to stderr once fullscreen service begins.
ncloglevel_e loglevel; ncloglevel_e loglevel;
@ -165,17 +162,31 @@ int notcurses_stop(struct notcurses* nc);
`notcurses_stop` should be called before exiting your program to restore the `notcurses_stop` should be called before exiting your program to restore the
terminal settings and free resources. terminal settings and free resources.
notcurses does not typically generate diagnostics (aside from the intro banner An application can freely enter and exit the alternate screen:
```c
// Shift to the alternate screen, if available. If already using the alternate
// screen, this returns 0 immediately. If the alternate screen is not
// available, this returns -1 immediately. Entering the alternate screen turns
// off scrolling for the standard plane.
int notcurses_enter_alternate_screen(struct notcurses* nc);
// Exit the alternate screen. Immediately returns 0 if not currently using the
// alternate screen.
int notcurses_leave_alternate_screen(struct notcurses* nc);
```
Notcurses does not typically generate diagnostics (aside from the intro banner
and outro performance summary). When `stderr` is connected to the same terminal and outro performance summary). When `stderr` is connected to the same terminal
to which graphics are being printed, printing to stderr will corrupt the output. to which graphics are being printed, printing to stderr will corrupt the output.
Setting `loglevel` to a value higher than `NCLOGLEVEL_SILENT` will cause Setting `loglevel` to a value higher than `NCLOGLEVEL_SILENT` will cause
diagnostics to be printed to `stderr`: you could ensure `stderr` is redirected diagnostics to be printed to `stderr`: you could ensure `stderr` is redirected
if you make use of this functionality. if you make use of this functionality.
It's probably wise to export `NCOPTION_NO_ALTERNATE_SCREEN` to the user (e.g. via It's probably wise to export `NCOPTION_NO_ALTERNATE_SCREEN` to the user (e.g.
command line option or environment variable). Developers and motivated users via command line option or environment variable). Motivated users might
might appreciate the ability to manipulate `loglevel` and `renderfp`. The appreciate the ability to manipulate `loglevel`. The remaining options are
remaining options are typically of use only to application authors. typically of use only to application authors.
The Notcurses API draws almost entirely into the virtual buffers of `ncplane`s. The Notcurses API draws almost entirely into the virtual buffers of `ncplane`s.
Only upon a call to `notcurses_render` will the visible terminal display be Only upon a call to `notcurses_render` will the visible terminal display be
@ -828,6 +839,9 @@ int ncplane_resize_realign(struct ncplane* n);
struct ncplane* ncplane_parent(struct ncplane* n); struct ncplane* ncplane_parent(struct ncplane* n);
const struct ncplane* ncplane_parent_const(const struct ncplane* n); const struct ncplane* ncplane_parent_const(const struct ncplane* n);
// Get the head of the list of planes bound to 'n'.
struct ncplane* ncplane_boundlist(struct ncplane* n);
// Duplicate an existing ncplane. The new plane will have the same geometry, // Duplicate an existing ncplane. The new plane will have the same geometry,
// will duplicate all content, and will start with the same rendering state. // will duplicate all content, and will start with the same rendering state.
struct ncplane* ncplane_dup(struct ncplane* n, void* opaque); struct ncplane* ncplane_dup(struct ncplane* n, void* opaque);

@ -1,6 +1,6 @@
% notcurses-pydemo(1) % notcurses-pydemo(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -34,7 +34,7 @@ def read(fname):
setup( setup(
name="notcurses", name="notcurses",
version="2.3.13", version="2.3.17",
packages=['notcurses'], packages=['notcurses'],
scripts=['notcurses-pydemo', 'ncdirect-pydemo'], scripts=['notcurses-pydemo', 'ncdirect-pydemo'],
package_dir={'': 'src'}, package_dir={'': 'src'},

@ -38,7 +38,7 @@ PROJECT_NAME = Notcurses
# could be handy for archiving the generated documentation or if some version # could be handy for archiving the generated documentation or if some version
# control system is used. # control system is used.
PROJECT_NUMBER = 2.3.13 PROJECT_NUMBER = 2.3.17
# Using the PROJECT_BRIEF tag one can provide an optional one line description # Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a # for a project that appears at the top of each page and should give viewer a

@ -33,7 +33,7 @@
</div> </div>
<hr> <hr>
<iframe align="right" width="560" height="315" src="https://www.youtube.com/embed/dcjkezf1ARY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <iframe align="right" width="560" height="315" src="https://www.youtube.com/embed/dcjkezf1ARY" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<h2><a href="https://nick-black.com/dankwiki/index.php/Notcurses">notcurses</a> manual pages (<a href="https://github.com/dankamongmen/notcurses/releases/tag/v2.3.13">v2.3.13</a>)</h2> <h2><a href="https://nick-black.com/dankwiki/index.php/Notcurses">notcurses</a> manual pages (<a href="https://github.com/dankamongmen/notcurses/releases/tag/v2.3.17">v2.3.17</a>)</h2>
<a href="notcurses.3.html">notcurses(3)</a>—a blingful TUI library<br/> <a href="notcurses.3.html">notcurses(3)</a>—a blingful TUI library<br/>
<h3>Executables (section 1)</h3> <h3>Executables (section 1)</h3>
<a href="ncls.1.html">ncls</a>—list files, displaying multimedia along with them<br/> <a href="ncls.1.html">ncls</a>—list files, displaying multimedia along with them<br/>

@ -1,6 +1,6 @@
% ncls(1) % ncls(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% ncneofetch(1) % ncneofetch(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% ncplayer(1) % ncplayer(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% nctetris(1) % nctetris(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses-demo(1) % notcurses-demo(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME
@ -9,7 +9,7 @@ notcurses-demo - Show off some Notcurses features
# SYNOPSIS # SYNOPSIS
**notcurses-demo** [**-h**|**--help**] [**-p** ***path***] [**-d** ***delaymult***] **notcurses-demo** [**-h**|**--help**] [**-p** ***path***] [**-d** ***delaymult***]
[**-l** ***loglevel***] [**-f** ***renderfile***] [**-J** ***jsonfile***] [**-m** ***margins***] [**-l** ***loglevel***] [**-J** ***jsonfile***] [**-m** ***margins***]
[**-V**|**--version**] [**-kc**] ***demospec*** [**-V**|**--version**] [**-kc**] ***demospec***
# DESCRIPTION # DESCRIPTION

@ -1,6 +1,6 @@
% notcurses-info(1) % notcurses-info(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME
@ -76,11 +76,10 @@ The next five lines describe properties of the terminal environment:
* The fifth line describes the available bitmap graphics. If Sixels are * The fifth line describes the available bitmap graphics. If Sixels are
available, the maximum number of color registers and maximum Sixel available, the maximum number of color registers and maximum Sixel
geometry are reported. If the iTerm2 protocol or Linux framebuffer graphics geometry are reported. If Linux framebuffer graphics are available, that is
are available, that is reported. If the Kitty graphics protocol is reported. If the Kitty graphics protocol is detected, that will be reported
detected, that will be reported with "rgba graphics are available"; if with "rgba graphics are available"; if Kitty's animation support is also
Kitty's animation support is also present, that will be reported with present, that will be reported with "rgba pixel animation support".
"rgba pixel animation support".
The final eleven lines, only printed when in a UTF8 locale, show various The final eleven lines, only printed when in a UTF8 locale, show various
Unicode glyphs. The first four lines include the quadrant, sextant, and Unicode glyphs. The first four lines include the quadrant, sextant, and

@ -1,6 +1,6 @@
% notcurses-input(1) % notcurses-input(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses-tester(1) % notcurses-tester(1)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses(3) % notcurses(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_capabilities(3) % notcurses_capabilities(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_cell(3) % notcurses_cell(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_channels(3) % notcurses_channels(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_core(3) % notcurses_core(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_direct(3) % notcurses_direct(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_fade(3) % notcurses_fade(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_fds(3) % notcurses_fds(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_init(3) % notcurses_init(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME
@ -21,8 +21,8 @@ notcurses_init - initialize a notcurses instance
#define NCOPTION_NO_FONT_CHANGES 0x0080ull #define NCOPTION_NO_FONT_CHANGES 0x0080ull
typedef enum { typedef enum {
NCLOGLEVEL_SILENT, // default. print nothing once fullscreen service begins NCLOGLEVEL_SILENT, // print nothing once fullscreen service begins
NCLOGLEVEL_PANIC, // print diagnostics immediately related to crashing NCLOGLEVEL_PANIC, // default. print diagnostics before we crash/exit
NCLOGLEVEL_FATAL, // we're hanging around, but we've had a horrible fault NCLOGLEVEL_FATAL, // we're hanging around, but we've had a horrible fault
NCLOGLEVEL_ERROR, // we can't keep doing this, but we can do other things NCLOGLEVEL_ERROR, // we can't keep doing this, but we can do other things
NCLOGLEVEL_WARNING, // you probably don't want what's happening to happen NCLOGLEVEL_WARNING, // you probably don't want what's happening to happen
@ -34,7 +34,6 @@ typedef enum {
typedef struct notcurses_options { typedef struct notcurses_options {
const char* termtype; const char* termtype;
FILE* renderfp;
ncloglevel_e loglevel; ncloglevel_e loglevel;
int margin_t, margin_r, margin_b, margin_l; int margin_t, margin_r, margin_b, margin_l;
uint64_t flags; // from NCOPTION_* bits uint64_t flags; // from NCOPTION_* bits
@ -193,7 +192,7 @@ glyph forms this cursor, and whether it e.g. blinks.
By default, Notcurses disables this cursor in rendered mode. It can be turned By default, Notcurses disables this cursor in rendered mode. It can be turned
back on with **notcurses_enable_cursor**, which has immediate effect (there is back on with **notcurses_enable_cursor**, which has immediate effect (there is
no need to call **notcurses_render(3)**. If already visible, this function no need to call **notcurses_render(3)**). If already visible, this function
updates the location. Each time the physical screen is updated, Notcurses will updates the location. Each time the physical screen is updated, Notcurses will
disable the cursor, write the update, move the cursor back to this location, disable the cursor, write the update, move the cursor back to this location,
and finally make the cursor visible. **notcurses_cursor_yx** retrieves the and finally make the cursor visible. **notcurses_cursor_yx** retrieves the

@ -1,6 +1,6 @@
% notcurses_input(3) % notcurses_input(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_lines(3) % notcurses_lines(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_menu(3) % notcurses_menu(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_metric(3) % notcurses_metric(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_multiselector(3) % notcurses_multiselector(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_output(3) % notcurses_output(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_palette(3) % notcurses_palette(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_plane(3) % notcurses_plane(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_plot(3) % notcurses_plot(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_reader(3) % notcurses_reader(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_reel(3) % notcurses_reel(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_refresh(3) % notcurses_refresh(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_render(3) % notcurses_render(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_selector(3) % notcurses_selector(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_stats(3) % notcurses_stats(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_stdplane(3) % notcurses_stdplane(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME
@ -18,6 +18,10 @@ notcurses_stdplane - acquire the standard ncplane
**static inline const struct ncplane* notcurses_stddim_yx_const(const struct notcurses* ***nc***, int* restrict ***y***, int* restrict ***x***);** **static inline const struct ncplane* notcurses_stddim_yx_const(const struct notcurses* ***nc***, int* restrict ***y***, int* restrict ***x***);**
**int notcurses_enter_alternate_screen(struct notcurses* ***nc***);**
**int notcurses_leave_alternate_screen(struct notcurses* ***nc***);**
# DESCRIPTION # DESCRIPTION
**notcurses_stdplane** returns a handle to the standard ncplane for the context **notcurses_stdplane** returns a handle to the standard ncplane for the context
@ -40,13 +44,29 @@ non-**NULL** parameters among **y** and **x**.
A resize event does not invalidate these references. They can be used until A resize event does not invalidate these references. They can be used until
**notcurses_stop(3)** is called on the associated **nc**. **notcurses_stop(3)** is called on the associated **nc**.
**notcurses_enter_alternate_screen** and **notcurses_leave_alternate_screen**
only have meaning if the terminal implements the "alternate screen" via the
**smcup** and **rmcup** **terminfo(5)** capabilities (see the discussion of
**NCOPTION_NO_ALTERNATE_SCREEN** in **notcurses_init(3)**). If not currently
using the alternate screen, and assuming it is supported,
**notcurses_enter_alternate_screen** will switch to the alternate screen. This
redraws the contents, repositions the cursor, and usually makes scrollback
unavailable. The standard plane will have scrolling disabled upon a move to
the alternate plane.
# RETURN VALUES # RETURN VALUES
These functions cannot fail when provided a valid **struct notcurses**. They **notcurses_enter_alternate_screen** will return -1 if the alternate screen
is unavailable. Both it and **notcurses_leave_alternate_screen** will return
-1 on an I/O failure.
Other functions cannot fail when provided a valid **struct notcurses**. They
will always return a valid pointer to the standard plane. will always return a valid pointer to the standard plane.
# SEE ALSO # SEE ALSO
**notcurses(3)**, **notcurses(3)**,
**notcurses_init(3)**,
**notcurses_plane(3)**, **notcurses_plane(3)**,
**notcurses_stop(3)** **notcurses_stop(3)**,
**terminfo(5)**

@ -1,6 +1,6 @@
% notcurses_stop(3) % notcurses_stop(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,5 +1,5 @@
% notcurses_tabbed(3) % notcurses_tabbed(3)
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_tree(3) % notcurses_tree(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_util(3) % notcurses_util(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME

@ -1,6 +1,6 @@
% notcurses_visual(3) % notcurses_visual(3)
% nick black <nickblack@linux.com> % nick black <nickblack@linux.com>
% v2.3.13 % v2.3.17
# NAME # NAME
notcurses_visual - notcurses multimedia notcurses_visual - notcurses multimedia

@ -10,25 +10,31 @@ extern "C" {
// 32-bit values to little-endian (as used in the nccell gcluster field). This // 32-bit values to little-endian (as used in the nccell gcluster field). This
// ought be defined so that it's a a no-op on little-endian builds. // ought be defined so that it's a a no-op on little-endian builds.
#if defined(__MINGW64__) // Windows #ifndef __MINGW64__ // All but Windows
#define wcwidth(w) 1 // FIXME lol, no
#define wcswidth(w, s) (s) // FIXME lol, no
#define htole(x) (x) // FIXME are all windows installs LE? ugh
#else // Non-Windows, UNIX-common
#include <poll.h> #include <poll.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <termios.h> #include <termios.h>
#if defined(__linux__) || defined(__gnu_hurd__) // Linux/Hurd #endif
#if defined(__linux__) // Linux
#include <byteswap.h> #include <byteswap.h>
#define htole(x) (__bswap_32(htonl(x))) #define htole(x) (__bswap_32(htonl(x)))
#elif defined(__APPLE__) // macOS #elif defined(__APPLE__) // macOS
#include <libkern/OSByteOrder.h> #include <libkern/OSByteOrder.h>
#define htole(x) (OSSwapInt32(htonl(x))) #define htole(x) (OSSwapInt32(htonl(x)))
#else // BSD #elif defined(__gnu_hurd__) // Hurd
#include <byteswap.h>
#define htole(x) (__bswap_32(htonl(x)))
#define wcwidth(w) 1 // FIXME lol, no
#define wcswidth(w, s) (s) // FIXME lol, no
#elif defined(__MINGW64__) // Windows
#define wcwidth(w) 1 // FIXME lol, no
#define wcswidth(w, s) (s) // FIXME lol, no
#define htole(x) (x) // FIXME are all windows installs LE? ugh
#else // BSDs
#include <sys/endian.h> #include <sys/endian.h>
#define htole(x) (bswap32(htonl(x))) #define htole(x) (bswap32(htonl(x)))
#endif #endif
#endif
#ifdef __cplusplus #ifdef __cplusplus
} // extern "C" } // extern "C"

@ -898,9 +898,7 @@ typedef struct notcurses_options {
// the environment variable TERM is used. Failure to open the terminal // the environment variable TERM is used. Failure to open the terminal
// definition will result in failure to initialize notcurses. // definition will result in failure to initialize notcurses.
const char* termtype; const char* termtype;
// If non-NULL, notcurses_render() will write each rendered frame to this FILE* renderfp; // deprecated, must be NULL, will be removed for ABI3 FIXME
// FILE* in addition to outfp. This is used primarily for debugging.
FILE* renderfp;
// Progressively higher log levels result in more logging to stderr. By // Progressively higher log levels result in more logging to stderr. By
// default, nothing is printed to stderr once fullscreen service begins. // default, nothing is printed to stderr once fullscreen service begins.
ncloglevel_e loglevel; ncloglevel_e loglevel;
@ -946,6 +944,18 @@ API ALLOC struct notcurses* notcurses_core_init(const notcurses_options* opts, F
// Destroy a Notcurses context. // Destroy a Notcurses context.
API int notcurses_stop(struct notcurses* nc); API int notcurses_stop(struct notcurses* nc);
// Shift to the alternate screen, if available. If already using the alternate
// screen, this returns 0 immediately. If the alternate screen is not
// available, this returns -1 immediately. Entering the alternate screen turns
// off scrolling for the standard plane.
API int notcurses_enter_alternate_screen(struct notcurses* nc)
__attribute__ ((nonnull (1)));
// Exit the alternate screen. Immediately returns 0 if not currently using the
// alternate screen.
API int notcurses_leave_alternate_screen(struct notcurses* nc)
__attribute__ ((nonnull (1)));
// Return the topmost plane of the pile containing 'n'. // Return the topmost plane of the pile containing 'n'.
API struct ncplane* ncpile_top(struct ncplane* n); API struct ncplane* ncpile_top(struct ncplane* n);
@ -1540,8 +1550,14 @@ API int ncplane_abs_y(const struct ncplane* n) __attribute__ ((pure));
API int ncplane_abs_x(const struct ncplane* n) __attribute__ ((pure)); API int ncplane_abs_x(const struct ncplane* n) __attribute__ ((pure));
// Get the plane to which the plane 'n' is bound, if any. // Get the plane to which the plane 'n' is bound, if any.
API struct ncplane* ncplane_parent(struct ncplane* n); API struct ncplane* ncplane_parent(struct ncplane* n)
API const struct ncplane* ncplane_parent_const(const struct ncplane* n); __attribute__ ((nonnull (1)));
API const struct ncplane* ncplane_parent_const(const struct ncplane* n)
__attribute__ ((nonnull (1)));
// Get the head of the list of planes bound to 'n'.
API struct ncplane* ncplane_boundlist(struct ncplane* n)
__attribute__ ((nonnull (1)));
// Return non-zero iff 'n' is a proper descendent of 'ancestor'. // Return non-zero iff 'n' is a proper descendent of 'ancestor'.
static inline int static inline int

@ -68,7 +68,6 @@ Notcurses_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
PyObject *main_fd_object = NULL; PyObject *main_fd_object = NULL;
const char *term_type = NULL; const char *term_type = NULL;
PyObject *render_fd_object = NULL;
int log_level = {0}; int log_level = {0};
const char *margins_str = NULL; const char *margins_str = NULL;
PyObject *margin_top = NULL, *margin_right = NULL, *margin_bottom = NULL, *margin_left = NULL; PyObject *margin_top = NULL, *margin_right = NULL, *margin_bottom = NULL, *margin_left = NULL;
@ -82,7 +81,7 @@ Notcurses_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
GNU_PY_CHECK_BOOL(PyArg_ParseTupleAndKeywords(args, kwds, "|O!sO!isO!O!O!O!K", keywords, GNU_PY_CHECK_BOOL(PyArg_ParseTupleAndKeywords(args, kwds, "|O!sO!isO!O!O!O!K", keywords,
&PyLong_Type, &main_fd_object, &PyLong_Type, &main_fd_object,
&term_type, &PyLong_Type, &render_fd_object, &log_level, &term_type, &PyLong_Type, &log_level,
&margins_str, &margins_str,
&PyLong_Type, &margin_top, &PyLong_Type, &margin_right, &PyLong_Type, &margin_bottom, &PyLong_Type, &margin_left, &PyLong_Type, &margin_top, &PyLong_Type, &margin_right, &PyLong_Type, &margin_bottom, &PyLong_Type, &margin_left,
&flags)); &flags));
@ -91,20 +90,6 @@ Notcurses_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
options.termtype = term_type; options.termtype = term_type;
if (NULL != render_fd_object)
{
long render_fd = GNU_PY_LONG_CHECK(render_fd_object);
FILE *renderfp = fdopen((int)render_fd, "w");
if (NULL == renderfp)
{
PyErr_SetString(PyExc_ValueError, "Failed to open render file descriptor.");
return NULL;
}
options.renderfp = renderfp;
}
options.loglevel = (ncloglevel_e)log_level; options.loglevel = (ncloglevel_e)log_level;
if (NULL != margins_str) if (NULL != margins_str)

@ -38,7 +38,7 @@ if environ.get('LDFLAGS') is None:
setup( setup(
name="notcurses", name="notcurses",
version="2.3.13", version="2.3.17",
packages=['notcurses'], packages=['notcurses'],
ext_modules=[ ext_modules=[
Extension( Extension(

@ -1,6 +1,6 @@
[package] [package]
name = "libnotcurses-sys" name = "libnotcurses-sys"
version = "2.3.13" version = "2.3.17"
authors = [ authors = [
"nick black <dankamongmen@gmail.com>", "nick black <dankamongmen@gmail.com>",
"José Luis Cruz <joseluis@andamira.net>" "José Luis Cruz <joseluis@andamira.net>"

@ -67,7 +67,6 @@ use libnotcurses_sys::*;
fn main() { fn main() {
let options = ffi::notcurses_options { let options = ffi::notcurses_options {
termtype: null(), termtype: null(),
renderfp: null_mut(),
loglevel: 0, loglevel: 0,
margin_t: 0, margin_t: 0,
margin_r: 0, margin_r: 0,

@ -7,7 +7,7 @@ use std::path::PathBuf;
// largely taken from https://rust-lang.github.io/rust-bindgen/tutorial-3.html // largely taken from https://rust-lang.github.io/rust-bindgen/tutorial-3.html
fn main() { fn main() {
let plib = pkg_config::Config::new() let plib = pkg_config::Config::new()
.atleast_version("2.3.13") .atleast_version("2.3.17")
.probe("notcurses") .probe("notcurses")
.unwrap(); .unwrap();

@ -73,7 +73,6 @@
//! fn main() { //! fn main() {
//! let options = ffi::notcurses_options { //! let options = ffi::notcurses_options {
//! termtype: null(), //! termtype: null(),
//! renderfp: null_mut(),
//! loglevel: 0, //! loglevel: 0,
//! margin_t: 0, //! margin_t: 0,
//! margin_r: 0, //! margin_r: 0,

@ -68,8 +68,8 @@ impl NcOptions {
) -> Self { ) -> Self {
Self { Self {
termtype: null(), termtype: null(),
renderfp: null_mut(),
loglevel, loglevel,
renderfp: null_mut(),
margin_t: margin_t as i32, margin_t: margin_t as i32,
margin_r: margin_r as i32, margin_r: margin_r as i32,
margin_b: margin_b as i32, margin_b: margin_b as i32,

@ -24,15 +24,19 @@ int set_fd_nonblocking(int fd, unsigned state, unsigned* oldstate){ // FIXME
(void)oldstate; (void)oldstate;
return 0; return 0;
} }
#define INFINITE 0xffffffff // FIXME pid_t waitpid(pid_t pid, int* state, int options){ // FIXME
pid_t waitpid(pid_t pid, int* state, int options){ (void)options;
(void)options; // FIXME honor WNOHANG (void)pid;
(void)state;
/*
WaitForSingleObject(pid, INFINITE); WaitForSingleObject(pid, INFINITE);
long unsigned pstate; long unsigned pstate;
GetExitCodeProcess(pid, &pstate); GetExitCodeProcess(pid, &pstate);
*state = pstate; *state = pstate;
CloseHandle(pid); CloseHandle(pid);
return pid; return pid;
*/
return 0;
} }
#else // not windows #else // not windows
#include <time.h> #include <time.h>

@ -3,13 +3,6 @@
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#ifndef __APPLE__
#define NOEXCEPT noexcept
#else
#define NOEXCEPT
#endif
#else
#define NOEXCEPT
#endif #endif
#include <time.h> #include <time.h>
@ -79,10 +72,10 @@ int clock_nanosleep(clockid_t clockid, int flags,
const struct timespec *request, const struct timespec *request,
struct timespec *remain); struct timespec *remain);
char* strndup(const char* str, size_t size) NOEXCEPT;
#ifdef __cplusplus #ifdef __cplusplus
} }
#else
char* strndup(const char* str, size_t size);
#endif #endif
#endif #endif

@ -132,7 +132,7 @@ usage(const char* exe, int status){
if(n) ncdirect_set_fg_rgb8(n, 0x80, 0xff, 0x80); if(n) ncdirect_set_fg_rgb8(n, 0x80, 0xff, 0x80);
fprintf(out, "%s ", exe); fprintf(out, "%s ", exe);
const char* options[] = { "-hVkc", "-m margins", "-p path", "-l loglevel", const char* options[] = { "-hVkc", "-m margins", "-p path", "-l loglevel",
"-d mult", "-J jsonfile", "-f renderfile", "demospec", "-d mult", "-J jsonfile", "demospec",
NULL }; NULL };
for(const char** op = options ; *op ; ++op){ for(const char** op = options ; *op ; ++op){
usage_option(out, n, *op); usage_option(out, n, *op);
@ -145,7 +145,6 @@ usage(const char* exe, int status){
"-k", "keep screen; do not switch to alternate", "-k", "keep screen; do not switch to alternate",
"-d", "delay multiplier (non-negative float)", "-d", "delay multiplier (non-negative float)",
"-J", "emit JSON summary to file", "-J", "emit JSON summary to file",
"-f", "render to file (in addition to stdout)",
"-c", "constant PRNG seed, useful for benchmarking", "-c", "constant PRNG seed, useful for benchmarking",
"-m", "margin, or 4 comma-separated margins", "-m", "margin, or 4 comma-separated margins",
NULL NULL
@ -243,7 +242,6 @@ ext_demos(struct notcurses* nc, const char* spec){
static const char* static const char*
handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
bool constant_seed = false; bool constant_seed = false;
char *renderfile = NULL;
*json_output = NULL; *json_output = NULL;
int c; int c;
const struct option longopts[] = { const struct option longopts[] = {
@ -252,7 +250,7 @@ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
{ .name = NULL, .has_arg = 0, .flag = NULL, .val = 0, }, { .name = NULL, .has_arg = 0, .flag = NULL, .val = 0, },
}; };
int lidx; int lidx;
while((c = getopt_long(argc, argv, "VhckJ:l:r:d:f:p:m:", longopts, &lidx)) != EOF){ while((c = getopt_long(argc, argv, "VhckJ:l:d:p:m:", longopts, &lidx)) != EOF){
switch(c){ switch(c){
case 'h': case 'h':
usage(*argv, EXIT_SUCCESS); usage(*argv, EXIT_SUCCESS);
@ -297,21 +295,9 @@ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
case 'k': case 'k':
opts->flags |= NCOPTION_NO_ALTERNATE_SCREEN; opts->flags |= NCOPTION_NO_ALTERNATE_SCREEN;
break; break;
case 'f':
if(opts->renderfp){
fprintf(stderr, "-f may only be supplied once\n");
usage(*argv, EXIT_FAILURE);
}
if((opts->renderfp = fopen(optarg, "wb")) == NULL){
usage(*argv, EXIT_FAILURE);
}
break;
case 'p': case 'p':
datadir = optarg; datadir = optarg;
break; break;
case 'r':
renderfile = optarg;
break;
case 'd':{ case 'd':{
float f; float f;
if(sscanf(optarg, "%f", &f) != 1){ if(sscanf(optarg, "%f", &f) != 1){
@ -334,13 +320,6 @@ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
if(!constant_seed){ if(!constant_seed){
srand(time(NULL)); // a classic blunder lol srand(time(NULL)); // a classic blunder lol
} }
if(renderfile){
opts->renderfp = fopen(renderfile, "wb");
if(opts->renderfp == NULL){
fprintf(stderr, "Error opening %s for write\n", renderfile);
usage(*argv, EXIT_FAILURE);
}
}
if(optind < argc - 1){ if(optind < argc - 1){
fprintf(stderr, "Extra argument: %s\n", argv[optind + 1]); fprintf(stderr, "Extra argument: %s\n", argv[optind + 1]);
usage(*argv, EXIT_FAILURE); usage(*argv, EXIT_FAILURE);
@ -350,29 +329,33 @@ handle_opts(int argc, char** argv, notcurses_options* opts, FILE** json_output){
} }
static int static int
table_segment_color(struct ncdirect* nc, const char* str, const char* delim, unsigned color){ table_segment_color(struct ncplane* n, const char* str, const char* delim, unsigned color){
ncdirect_set_fg_rgb(nc, color); ncplane_set_fg_rgb(n, color);
fputs(str, stdout); if(ncplane_putstr(n, str) < 0){
ncdirect_set_fg_rgb8(nc, 178, 102, 255); return -1;
fputs(delim, stdout); }
ncplane_set_fg_rgb8(n, 178, 102, 255);
if(ncplane_putstr(n, delim) < 0){
return -1;
}
return 0; return 0;
} }
static int static int
table_segment(struct ncdirect* nc, const char* str, const char* delim){ table_segment(struct ncplane* n, const char* str, const char* delim){
return table_segment_color(nc, str, delim, 0xffffff); return table_segment_color(n, str, delim, 0xffffff);
} }
static int static int
table_printf(struct ncdirect* nc, const char* delim, const char* fmt, ...){ table_printf(struct ncplane* n, const char* delim, const char* fmt, ...){
ncdirect_set_fg_rgb8(nc, 0xD4, 0xAF, 0x37); ncplane_set_fg_rgb8(n, 0xD4, 0xAF, 0x37);
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
vfprintf(stdout, fmt, va); int r = ncplane_vprintf(n, fmt, va);
va_end(va); va_end(va);
ncdirect_set_fg_rgb8(nc, 178, 102, 255); ncplane_set_fg_rgb8(n, 178, 102, 255);
fputs(delim, stdout); ncplane_putstr(n, delim);
return 0; return r;
} }
static int static int
@ -393,21 +376,24 @@ summary_json(FILE* f, const char* spec, int rows, int cols){
} }
static int static int
summary_table(struct ncdirect* nc, const char* spec, bool canimage, bool canvideo){ summary_table(struct notcurses* nc, const char* spec, bool canimage, bool canvideo){
notcurses_leave_alternate_screen(nc);
struct ncplane* n = notcurses_stdplane(nc);
ncplane_set_scrolling(n, true);
bool failed = false; bool failed = false;
uint64_t totalbytes = 0; uint64_t totalbytes = 0;
long unsigned totalframes = 0; long unsigned totalframes = 0;
uint64_t totalrenderns = 0; uint64_t totalrenderns = 0;
uint64_t totalwriteoutns = 0; uint64_t totalwriteoutns = 0;
printf("\n"); ncplane_putchar(n, '\n');
table_segment(nc, " runtime", ""); table_segment(n, " runtime", "");
table_segment(nc, " frames", ""); table_segment(n, " frames", "");
table_segment(nc, "output(B)", ""); table_segment(n, "output(B)", "");
table_segment(nc, " FPS", ""); table_segment(n, " FPS", "");
table_segment(nc, "%r", ""); table_segment(n, "%r", "");
table_segment(nc, "%a", ""); table_segment(n, "%a", "");
table_segment(nc, "%w", ""); table_segment(n, "%w", "");
table_segment(nc, "TheoFPS", "\n══╤════════╤════════╪═══════╪═════════╪═══════╪══╪══╪══╪═══════╣\n"); table_segment(n, "TheoFPS", "\n══╤════════╤════════╪═══════╪═════════╪═══════╪══╪══╪══╪═══════╣\n");
char timebuf[PREFIXSTRLEN + 1]; char timebuf[PREFIXSTRLEN + 1];
char tfpsbuf[PREFIXSTRLEN + 1]; char tfpsbuf[PREFIXSTRLEN + 1];
char totalbuf[BPREFIXSTRLEN + 1]; char totalbuf[BPREFIXSTRLEN + 1];
@ -433,14 +419,14 @@ summary_table(struct ncdirect* nc, const char* spec, bool canimage, bool canvide
}else{ }else{
rescolor = 0x32CD32; rescolor = 0x32CD32;
} }
ncdirect_set_fg_rgb(nc, rescolor); ncplane_set_fg_rgb(n, rescolor);
printf("%2zu", i + 1); ncplane_printf(n, "%2llu", (unsigned long long)(i + 1)); // windows has %zu problems
ncdirect_set_fg_rgb8(nc, 178, 102, 255); ncplane_set_fg_rgb8(n, 178, 102, 255);
printf(""); ncplane_putwc(n, L'');
ncdirect_set_fg_rgb(nc, rescolor); ncplane_set_fg_rgb(n, rescolor);
printf("%8s", demos[results[i].selector - 'a'].name); ncplane_printf(n, "%8s", demos[results[i].selector - 'a'].name);
ncdirect_set_fg_rgb8(nc, 178, 102, 255); ncplane_set_fg_rgb8(n, 178, 102, 255);
printf("│%*ss│%7ju│%*s│%7.1f│%2jd│%2jd│%2jd│%*s║", ncplane_printf(n, "│%*ss│%7ju│%*s│%7.1f│%2jd│%2jd│%2jd│%*s║",
PREFIXFMT(timebuf), (uintmax_t)(results[i].stats.renders), PREFIXFMT(timebuf), (uintmax_t)(results[i].stats.renders),
BPREFIXFMT(totalbuf), BPREFIXFMT(totalbuf),
results[i].timens ? results[i].timens ?
@ -452,8 +438,8 @@ summary_table(struct ncdirect* nc, const char* spec, bool canimage, bool canvide
(uintmax_t)(results[i].timens ? (uintmax_t)(results[i].timens ?
results[i].stats.writeout_ns * 100 / results[i].timens : 0), results[i].stats.writeout_ns * 100 / results[i].timens : 0),
PREFIXFMT(tfpsbuf)); PREFIXFMT(tfpsbuf));
ncdirect_set_fg_rgb(nc, rescolor); ncplane_set_fg_rgb(n, rescolor);
printf("%s\n", results[i].result < 0 ? "FAILED" : ncplane_printf(n, "%s\n", results[i].result < 0 ? "FAILED" :
results[i].result > 0 ? "ABORTED" : results[i].result > 0 ? "ABORTED" :
!results[i].stats.renders ? "SKIPPED" : ""); !results[i].stats.renders ? "SKIPPED" : "");
if(results[i].result < 0){ if(results[i].result < 0){
@ -466,27 +452,25 @@ summary_table(struct ncdirect* nc, const char* spec, bool canimage, bool canvide
} }
qprefix(nsdelta, NANOSECS_IN_SEC, timebuf, 0); qprefix(nsdelta, NANOSECS_IN_SEC, timebuf, 0);
bprefix(totalbytes, 1, totalbuf, 0); bprefix(totalbytes, 1, totalbuf, 0);
table_segment(nc, "", "══╧════════╧════════╪═══════╪═════════╪═══════╧══╧══╧══╧═══════╝\n"); table_segment(n, "", "══╧════════╧════════╪═══════╪═════════╪═══════╧══╧══╧══╧═══════╝\n");
printf(" "); ncplane_putstr(n, " ");
table_printf(nc, "", "%*ss", PREFIXFMT(timebuf)); table_printf(n, "", "%*ss", PREFIXFMT(timebuf));
table_printf(nc, "", "%7lu", totalframes); table_printf(n, "", "%7lu", totalframes);
table_printf(nc, "", "%*s", BPREFIXFMT(totalbuf)); table_printf(n, "", "%*s", BPREFIXFMT(totalbuf));
//table_printf(nc, "│", "%7.1f", nsdelta ? totalframes / ((double)nsdelta / NANOSECS_IN_SEC) : 0); //table_printf(nc, "│", "%7.1f", nsdelta ? totalframes / ((double)nsdelta / NANOSECS_IN_SEC) : 0);
printf("\n"); ncplane_putchar(n, '\n');
ncdirect_set_fg_rgb8(nc, 0xff, 0xb0, 0xb0); ncplane_set_fg_rgb8(n, 0xfe, 0x20, 0x76); // PANTONE Strong Red C + 3x0x20
fflush(stdout); // in case we print to stderr below, we want color from above
if(failed){
fprintf(stderr, "\nError running demo.\nIs \"%s\" the correct data path? Supply it with -p.\n", datadir);
}
ncdirect_set_fg_rgb8(nc, 0xfe, 0x20, 0x76); // PANTONE Strong Red C + 3x0x20
fflush(stdout); // in case we print to stderr below, we want color from above
#ifdef DFSG_BUILD #ifdef DFSG_BUILD
fprintf(stderr, "\nDFSG version. Some demos are unavailable.\n"); ncplane_putstr(n, "\nDFSG version. Some demos are unavailable.\n");
#endif #endif
if(!canimage){ if(!canimage){
fprintf(stderr, "\nNo multimedia support. Some demos are unavailable.\n"); ncplane_putstr(n, "\nNo multimedia support. Some demos are unavailable.\n");
}else if(!canvideo){ }else if(!canvideo){
fprintf(stderr, "\nNo video support. Some demos are unavailable.\n"); ncplane_putstr(n, "\nNo video support. Some demos are unavailable.\n");
}
ncplane_set_fg_rgb8(n, 0xff, 0xb0, 0xb0);
if(failed){
ncplane_printf(n, "\nError running demo.\nIs \"%s\" the correct data path? Supply it with -p.\n", datadir);
} }
return failed; return failed;
} }
@ -585,37 +569,26 @@ int main(int argc, char** argv){
}while(restart_demos); }while(restart_demos);
ncmenu_destroy(menu); ncmenu_destroy(menu);
stop_input(); stop_input();
if(notcurses_stop(nc)){ notcurses_render(nc); // rid ourselves of any remaining demo output
return EXIT_FAILURE; if(summary_table(nc, spec, canimage, canvideo)){
} goto err;
if(nopts.renderfp){
if(fclose(nopts.renderfp)){
fprintf(stderr, "Warning: error closing renderfile\n");
}
}
struct ncdirect* ncd = ncdirect_init(NULL, stdout, 0);
if(!ncd){
return EXIT_FAILURE;
} }
if(json && summary_json(json, spec, ncdirect_dim_y(ncd), ncdirect_dim_x(ncd))){ notcurses_render(nc); // render our summary table
free(results);
if(notcurses_stop(nc)){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
// reinitialize without alternate screen to do some coloring if(json && summary_json(json, spec, dimy, dimx)){
if(summary_table(ncd, spec, canimage, canvideo)){
ncdirect_stop(ncd);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
free(results);
ncdirect_stop(ncd);
return EXIT_SUCCESS; return EXIT_SUCCESS;
err: err:
stop_input(); stop_input();
notcurses_term_dim_yx(nc, &dimy, &dimx);
notcurses_stop(nc); notcurses_stop(nc);
if(dimy < MIN_SUPPORTED_ROWS || dimx < MIN_SUPPORTED_COLS){ if(dimy < MIN_SUPPORTED_ROWS || dimx < MIN_SUPPORTED_COLS){
fprintf(stderr, "At least a %dx%d terminal is required (current: %dx%d)\n", fprintf(stderr, "At least a %dx%d terminal is required (current: %dx%d)\n",
MIN_SUPPORTED_COLS, MIN_SUPPORTED_ROWS, dimx, dimy); MIN_SUPPORTED_ROWS, MIN_SUPPORTED_COLS, dimy, dimx);
} }
return EXIT_FAILURE; return EXIT_FAILURE;
} }

@ -582,6 +582,10 @@ int demo_render(struct notcurses* nc){
clock_gettime(CLOCK_MONOTONIC, &ts); clock_gettime(CLOCK_MONOTONIC, &ts);
if(plot){ if(plot){
if(!plot_hidden){ if(!plot_hidden){
struct ncplane* pixelp = ncplane_boundlist(ncuplot_plane(plot));
if(pixelp){
ncplane_move_top(pixelp);
}
ncplane_move_top(ncuplot_plane(plot)); ncplane_move_top(ncuplot_plane(plot));
} }
uint64_t ns = (timespec_to_ns(&ts) - plottimestart) / (NANOSECS_IN_SEC / FPSHZ); uint64_t ns = (timespec_to_ns(&ts) - plottimestart) / (NANOSECS_IN_SEC / FPSHZ);

@ -205,7 +205,7 @@ int unicodeblocks_demo(struct notcurses* nc){
{ .name = "Cuneiform", .start = 0x12000, }, { .name = "Cuneiform", .start = 0x12000, },
{ .name = "Cuneiform (cont.)", .start = 0x12200, }, { .name = "Cuneiform (cont.)", .start = 0x12200, },
{ .name = "Byzantine Musical Symbols, Musical Symbols", .start = 0x1d000, }, { .name = "Byzantine Musical Symbols, Musical Symbols", .start = 0x1d000, },
{ .name = "Ancient Greek Musical Notation, Mayan Numerals, Tai Xuan Jing, Counting Rods", .start = 0x1d200, }, { .name = "Greek Musical Notation, Mayan Numerals, Tai Xuan Jing, Counting Rods", .start = 0x1d200, },
{ .name = "Mathematical Alphanumeric Symbols", .start = 0x1d400, }, { .name = "Mathematical Alphanumeric Symbols", .start = 0x1d400, },
{ .name = "Mathematical Alphanumeric Symbols (cont.)", .start = 0x1d600, }, { .name = "Mathematical Alphanumeric Symbols (cont.)", .start = 0x1d600, },
{ .name = "Sutton SignWriting", .start = 0x1d800, }, { .name = "Sutton SignWriting", .start = 0x1d800, },

@ -2,7 +2,7 @@
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <notcurses/notcurses.h> #include <notcurses/notcurses.h>
#include "internal.h" // internal headers #include "lib/internal.h" // internal headers
static inline wchar_t static inline wchar_t
capboolbool(unsigned utf8, bool cap){ capboolbool(unsigned utf8, bool cap){
@ -126,7 +126,11 @@ emoji_viz(struct ncplane* n){
} }
} }
} }
finish_line(n); int x;
ncplane_cursor_yx(n, NULL, &x);
while(x++ < 80){
ncplane_putchar(n, ' ');
}
return 0; return 0;
} }
@ -291,10 +295,10 @@ unicodedumper(struct ncplane* n, const char* indent){
uint64_t lr = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0xdB, 0x18, 0x8E); uint64_t lr = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0xdB, 0x18, 0x8E);
uint64_t ul = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0x19, 0x19, 0x70); uint64_t ul = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0x19, 0x19, 0x70);
uint64_t ll = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0x19, 0x19, 0x70); uint64_t ll = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0x19, 0x19, 0x70);
ncplane_cursor_move_yx(n, y - 16, 0); ncplane_cursor_move_yx(n, y - 15, 0);
ncplane_stain(n, y - 2, 79, ul, ur, ll, lr); ncplane_stain(n, y - 1, 79, ul, ur, ll, lr);
ncplane_set_styles(n, NCSTYLE_BOLD | NCSTYLE_ITALIC); ncplane_set_styles(n, NCSTYLE_BOLD | NCSTYLE_ITALIC);
ncplane_cursor_move_yx(n, y - 13, 54); ncplane_cursor_move_yx(n, y - 12, 54);
wviz(n, L"🯁🯂🯃https://notcurses.com"); wviz(n, L"🯁🯂🯃https://notcurses.com");
ncplane_set_styles(n, NCSTYLE_NONE); ncplane_set_styles(n, NCSTYLE_NONE);
} }
@ -309,7 +313,6 @@ display_logo(struct ncplane* n, const char* path){
if(ncv == NULL){ if(ncv == NULL){
return -1; return -1;
} }
// FIXME ought be exactly 4:1
if(ncvisual_resize(ncv, 3 * cpixy, 24 * cpixx)){ if(ncvisual_resize(ncv, 3 * cpixy, 24 * cpixx)){
ncvisual_destroy(ncv); ncvisual_destroy(ncv);
return -1; return -1;
@ -338,7 +341,7 @@ tinfo_debug_bitmaps(struct ncplane* n, const tinfo* ti, const char* indent){
ti->bg_collides_default & 0xfffffful, ti->bg_collides_default & 0xfffffful,
(ti->bg_collides_default & 0x01000000) ? "" : "not "); (ti->bg_collides_default & 0x01000000) ? "" : "not ");
finish_line(n); finish_line(n);
if(!ti->pixel_draw){ if(!ti->pixel_draw && !ti->pixel_draw_late){
ncplane_printf(n, "%sno bitmap graphics detected", indent); ncplane_printf(n, "%sno bitmap graphics detected", indent);
}else{ // we do have support; draw one }else{ // we do have support; draw one
if(ti->color_registers){ if(ti->color_registers){
@ -348,12 +351,8 @@ tinfo_debug_bitmaps(struct ncplane* n, const tinfo* ti, const char* indent){
}else{ }else{
ncplane_printf(n, "%ssixel colorregs: %u", indent, ti->color_registers); ncplane_printf(n, "%ssixel colorregs: %u", indent, ti->color_registers);
} }
#ifdef __linux__ }else if(ti->pixel_draw_late){
}else if(ti->linux_fb_fd >= 0){
ncplane_printf(n, "%sframebuffer graphics supported", indent); ncplane_printf(n, "%sframebuffer graphics supported", indent);
#endif
}else if(ti->pixel_move == NULL){
ncplane_printf(n, "%siTerm2 graphics support", indent);
}else if(ti->sixel_maxy_pristine){ }else if(ti->sixel_maxy_pristine){
ncplane_printf(n, "%srgba pixel graphics support", indent); ncplane_printf(n, "%srgba pixel graphics support", indent);
}else{ }else{
@ -400,7 +399,6 @@ tinfo_debug_styles(const notcurses* nc, struct ncplane* n, const char* indent){
tinfo_debug_cap(n, "u7", get_escape(ti, ESCAPE_U7)); tinfo_debug_cap(n, "u7", get_escape(ti, ESCAPE_U7));
tinfo_debug_cap(n, "ccc", ti->caps.can_change_colors); tinfo_debug_cap(n, "ccc", ti->caps.can_change_colors);
tinfo_debug_cap(n, "rgb", ti->caps.rgb); tinfo_debug_cap(n, "rgb", ti->caps.rgb);
tinfo_debug_cap(n, "csr", get_escape(ti, ESCAPE_CSR));
finish_line(n); finish_line(n);
ncplane_putstr(n, indent); ncplane_putstr(n, indent);
tinfo_debug_cap(n, "utf8", ti->caps.utf8); tinfo_debug_cap(n, "utf8", ti->caps.utf8);
@ -441,7 +439,13 @@ int main(int argc, const char** argv){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
const char indent[] = ""; const char indent[] = "";
struct ncplane* stdn = notcurses_stdplane(nc); int dimx;
struct ncplane* stdn = notcurses_stddim_yx(nc, NULL, &dimx);
if(dimx < 80){
notcurses_stop(nc);
fprintf(stderr, "This program requires at least 80 columns.\n");
return EXIT_FAILURE;
}
ncplane_set_scrolling(stdn, true); ncplane_set_scrolling(stdn, true);
tinfo_debug_caps(stdn, &nc->tcache, indent); tinfo_debug_caps(stdn, &nc->tcache, indent);
tinfo_debug_styles(nc, stdn, indent); tinfo_debug_styles(nc, stdn, indent);

@ -227,7 +227,7 @@ int input_demo(ncpp::NotCurses* nc) {
popts.minchannels = popts.maxchannels = 0; popts.minchannels = popts.maxchannels = 0;
ncchannels_set_fg_rgb8(&popts.minchannels, 0x40, 0x50, 0xb0); ncchannels_set_fg_rgb8(&popts.minchannels, 0x40, 0x50, 0xb0);
ncchannels_set_fg_rgb8(&popts.maxchannels, 0x40, 0xff, 0xd0); ncchannels_set_fg_rgb8(&popts.maxchannels, 0x40, 0xff, 0xd0);
popts.gridtype = static_cast<ncblitter_e>(NCBLIT_8x1); popts.gridtype = static_cast<ncblitter_e>(NCBLIT_PIXEL);
plot = ncuplot_create(pplane, &popts, 0, 0); plot = ncuplot_create(pplane, &popts, 0, 0);
if(!plot){ if(!plot){
return EXIT_FAILURE; return EXIT_FAILURE;
@ -338,9 +338,7 @@ int main(void){
notcurses_options nopts{}; notcurses_options nopts{};
nopts.flags = NCOPTION_INHIBIT_SETLOCALE; nopts.flags = NCOPTION_INHIBIT_SETLOCALE;
NotCurses nc(nopts); NotCurses nc(nopts);
if(!nc.mouse_enable()){ nc.mouse_enable(); // might fail if no mouse is available
return EXIT_FAILURE;
}
int ret = input_demo(&nc); int ret = input_demo(&nc);
if(!nc.stop() || ret){ if(!nc.stop() || ret){
return EXIT_FAILURE; return EXIT_FAILURE;

@ -930,7 +930,7 @@ const struct blitset* lookup_blitset(const tinfo* tcache, ncblitter_e setid, boo
} }
} }
// without bitmap support, NCBLIT_PIXEL decays to NCBLIT_3x2 // without bitmap support, NCBLIT_PIXEL decays to NCBLIT_3x2
if(!tcache->pixel_draw && setid == NCBLIT_PIXEL){ if(!tcache->pixel_draw && !tcache->pixel_draw_late && setid == NCBLIT_PIXEL){
if(may_degrade){ if(may_degrade){
setid = NCBLIT_3x2; setid = NCBLIT_3x2;
}else{ }else{

@ -531,6 +531,11 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
if(fbuf_finalize(&f, n->ttyfp)){ if(fbuf_finalize(&f, n->ttyfp)){
return -1; return -1;
} }
if(n->tcache.pixel_draw_late){
if(n->tcache.pixel_draw_late(&n->tcache, np->sprite, y, xoff) < 0){
return -1;
}
}
return 0; return 0;
} }
//fprintf(stderr, "rasterizing %dx%d+%d\n", dimy, dimx, xoff); //fprintf(stderr, "rasterizing %dx%d+%d\n", dimy, dimx, xoff);
@ -1330,7 +1335,7 @@ int ncdirect_flush(const ncdirect* nc){
} }
int ncdirect_check_pixel_support(const ncdirect* n){ int ncdirect_check_pixel_support(const ncdirect* n){
if(n->tcache.pixel_draw){ if(n->tcache.pixel_draw || n->tcache.pixel_draw_late){
return 1; return 1;
} }
return 0; return 0;
@ -1367,7 +1372,10 @@ int ncdirect_stream(ncdirect* n, const char* filename, ncstreamcb streamer,
if(v->sprite){ if(v->sprite){
thisid = v->sprite->id; thisid = v->sprite->id;
} }
ncdirect_raster_frame(n, v, (vopts->flags & NCVISUAL_OPTION_HORALIGNED) ? vopts->x : 0); if(ncdirect_raster_frame(n, v, (vopts->flags & NCVISUAL_OPTION_HORALIGNED) ? vopts->x : 0)){
ncvisual_destroy(ncv);
return -1;
}
if(lastid > -1){ if(lastid > -1){
if(n->tcache.pixel_remove){ if(n->tcache.pixel_remove){
fbuf f = {}; fbuf f = {};

@ -76,7 +76,7 @@ fbuf_grow(fbuf* f, size_t n){
return -1; return -1;
} }
#else #else
tmp = realloc(f->buf, f->size); tmp = realloc(f->buf, size);
if(tmp == NULL){ if(tmp == NULL){
return -1; return -1;
} }

@ -388,7 +388,7 @@ ncsubproc* ncsubproc_createvpe(ncplane* n, const ncsubproc_options* opts,
if(ret->pid == 0){ if(ret->pid == 0){
#ifdef __linux__ #ifdef __linux__
execvpe(bin, arg, env); execvpe(bin, arg, env);
#elif defined(__APPLE__) #elif defined(__APPLE__) || defined(__gnu_hurd__)
(void)env; (void)env;
execvp(bin, arg); // FIXME env? execvp(bin, arg); // FIXME env?
#elif defined(__MINGW64__) #elif defined(__MINGW64__)

@ -0,0 +1,52 @@
#include "internal.h"
#ifdef USE_GPM
#undef buttons // defined by both term.h and gpm.h, ugh
#include <lib/gpm.h>
#include <gpm.h>
static Gpm_Connect gpmconn; // gpm server handle
int gpm_connect(tinfo* ti){
(void)ti;
gpm_zerobased = 1;
gpmconn.eventMask = ~0;
gpmconn.defaultMask = 0;
gpmconn.minMod = 0;
gpmconn.maxMod = ~0;
if(Gpm_Open(&gpmconn, 0) == -1){
logerror("couldn't connect to gpm");
return -1;
}
loginfo("connected to gpm on %d\n", gpm_fd);
return gpm_fd;
}
int gpm_read(tinfo* ti, ncinput* ni){
(void)ti;
(void)ni;
return -1;
}
int gpm_close(tinfo* ti){
(void)ti;
Gpm_Close();
memset(&gpmconn, 0, sizeof(gpmconn));
return 0;
}
#else
int gpm_connect(tinfo* ti){
(void)ti;
return -1;
}
int gpm_read(tinfo* ti, ncinput* ni){
(void)ti;
(void)ni;
return -1;
}
int gpm_close(tinfo* ti){
(void)ti;
return -1;
}
#endif

@ -0,0 +1,30 @@
#ifndef NOTCURSES_GPM
#define NOTCURSES_GPM
#ifdef __cplusplus
extern "C" {
#endif
// internal header, not installed
struct tinfo;
struct ncinput;
// GPM ("General Purpose Mouse") provides an interface to mice in the Linux
// and FreeBSD consoles. The gpm server must be running; we do not attempt to
// start it. We must have been built with -DUSE_GPM.
// Returns the poll()able file descriptor associated with gpm, or -1 on failure.
int gpm_connect(struct tinfo* ti);
// Read from the gpm connection, which ought have been poll()ed. Translates
// the libgpm input to an ncinput.
int gpm_read(struct tinfo* ti, struct ncinput* ni);
int gpm_close(struct tinfo* ti);
#ifdef __cplusplus
}
#endif
#endif

@ -114,8 +114,9 @@ unpop_keypress(ncinputlayer* nc, int kpress){
// we assumed escapes can only be composed of 7-bit chars // we assumed escapes can only be composed of 7-bit chars
typedef struct esctrie { typedef struct esctrie {
uint32_t special; // composed key terminating here
struct esctrie** trie; // if non-NULL, next level of radix-128 trie struct esctrie** trie; // if non-NULL, next level of radix-128 trie
uint32_t special; // composed key terminating here
bool shift, ctrl, alt;
} esctrie; } esctrie;
static esctrie* static esctrie*
@ -124,6 +125,9 @@ create_esctrie_node(int special){
if(e){ if(e){
e->special = special; e->special = special;
e->trie = NULL; e->trie = NULL;
e->shift = 0;
e->ctrl = 0;
e->alt = 0;
} }
return e; return e;
} }
@ -145,14 +149,12 @@ input_free_esctrie(esctrie** eptr){
} }
} }
// multiple input escapes might map to the same input
static int static int
ncinputlayer_add_input_escape(ncinputlayer* nc, const char* esc, uint32_t special){ ncinputlayer_add_input_escape(ncinputlayer* nc, const char* esc, uint32_t special,
unsigned shift, unsigned ctrl, unsigned alt){
if(esc[0] != NCKEY_ESC || strlen(esc) < 2){ // assume ESC prefix + content if(esc[0] != NCKEY_ESC || strlen(esc) < 2){ // assume ESC prefix + content
logerror("Not an escape: %s (0x%x)\n", esc, special); logerror("not an escape: %s (0x%x)\n", esc, special);
return -1;
}
if(!nckey_supppuab_p(special) && special != NCKEY_CSI){
logerror("Not a supplementary-b PUA char: %u (0x%x)\n", special, special);
return -1; return -1;
} }
esctrie** cur = &nc->inputescapes; esctrie** cur = &nc->inputescapes;
@ -182,9 +184,12 @@ ncinputlayer_add_input_escape(ncinputlayer* nc, const char* esc, uint32_t specia
// it appears that multiple keys can be mapped to the same escape string. as // it appears that multiple keys can be mapped to the same escape string. as
// an example, see "kend" and "kc1" in st ("simple term" from suckless) :/. // an example, see "kend" and "kc1" in st ("simple term" from suckless) :/.
if((*cur)->special != NCKEY_INVALID){ // already had one here! if((*cur)->special != NCKEY_INVALID){ // already had one here!
logwarn("Warning: already added escape (got 0x%x, wanted 0x%x)\n", (*cur)->special, special); logwarn("already added escape (got 0x%x, wanted 0x%x)\n", (*cur)->special, special);
}else{ }else{
(*cur)->special = special; (*cur)->special = special;
(*cur)->shift = shift;
(*cur)->ctrl = ctrl;
(*cur)->alt = alt;
} }
return 0; return 0;
} }
@ -356,6 +361,9 @@ handle_getc(ncinputlayer* nc, int kpress, ncinput* ni, int leftmargin, int topma
logtrace("move to %p (%u)\n", esc, esc ? esc->special : 0); logtrace("move to %p (%u)\n", esc, esc ? esc->special : 0);
} }
if(esc && esc->special != NCKEY_INVALID){ if(esc && esc->special != NCKEY_INVALID){
ni->shift = esc->shift;
ni->ctrl = esc->ctrl;
ni->alt = esc->alt;
return esc->special; return esc->special;
} }
if(csi){ if(csi){
@ -633,7 +641,9 @@ prep_special_keys(ncinputlayer* nc){
static const struct { static const struct {
const char* tinfo; const char* tinfo;
uint32_t key; uint32_t key;
bool shift, ctrl, alt;
} keys[] = { } keys[] = {
{ .tinfo = "kcbt", .key = '\t', .shift = true, },
{ .tinfo = "kcub1", .key = NCKEY_LEFT, }, { .tinfo = "kcub1", .key = NCKEY_LEFT, },
{ .tinfo = "kcuf1", .key = NCKEY_RIGHT, }, { .tinfo = "kcuf1", .key = NCKEY_RIGHT, },
{ .tinfo = "kcuu1", .key = NCKEY_UP, }, { .tinfo = "kcuu1", .key = NCKEY_UP, },
@ -720,84 +730,66 @@ prep_special_keys(ncinputlayer* nc){
{ .tinfo = "kext", .key = NCKEY_EXIT, }, { .tinfo = "kext", .key = NCKEY_EXIT, },
{ .tinfo = "kprt", .key = NCKEY_PRINT, }, { .tinfo = "kprt", .key = NCKEY_PRINT, },
{ .tinfo = "krfr", .key = NCKEY_REFRESH, }, { .tinfo = "krfr", .key = NCKEY_REFRESH, },
{ .tinfo = "kDC", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDC", .key = NCKEY_DEL, .shift = 1, },
{ .tinfo = "kDC1", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDC3", .key = NCKEY_DEL, .alt = 1, },
{ .tinfo = "kDC2", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDC4", .key = NCKEY_DEL, .alt = 1, .shift = 1, },
{ .tinfo = "kDC3", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDC5", .key = NCKEY_DEL, .ctrl = 1, },
{ .tinfo = "kDC4", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDC6", .key = NCKEY_DEL, .ctrl = 1, .shift = 1, },
{ .tinfo = "kDC5", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDC7", .key = NCKEY_DEL, .alt = 1, .ctrl = 1, },
{ .tinfo = "kDC6", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDN", .key = NCKEY_DOWN, .shift = 1, },
{ .tinfo = "kDC7", .key = NCKEY_DEL, }, // FIXME plus modifier { .tinfo = "kDN3", .key = NCKEY_DOWN, .alt = 1, },
{ .tinfo = "kDN", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kDN4", .key = NCKEY_DOWN, .alt = 1, .shift = 1, },
{ .tinfo = "kDN1", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kDN5", .key = NCKEY_DOWN, .ctrl = 1, },
{ .tinfo = "kDN2", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kDN6", .key = NCKEY_DOWN, .ctrl = 1, .shift = 1, },
{ .tinfo = "kDN3", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kDN7", .key = NCKEY_DOWN, .alt = 1, .ctrl = 1, },
{ .tinfo = "kDN4", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kEND", .key = NCKEY_END, .shift = 1, },
{ .tinfo = "kDN5", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kEND3", .key = NCKEY_END, .alt = 1, },
{ .tinfo = "kDN6", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kEND4", .key = NCKEY_END, .alt = 1, .shift = 1, },
{ .tinfo = "kDN7", .key = NCKEY_DOWN, }, // FIXME plus modifier { .tinfo = "kEND5", .key = NCKEY_END, .ctrl = 1, },
{ .tinfo = "kEND", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kEND6", .key = NCKEY_END, .ctrl = 1, .shift = 1, },
{ .tinfo = "kEND1", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kEND7", .key = NCKEY_END, .alt = 1, .ctrl = 1, },
{ .tinfo = "kEND2", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kHOM", .key = NCKEY_HOME, .shift = 1, },
{ .tinfo = "kEND3", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kHOM3", .key = NCKEY_HOME, .alt = 1, },
{ .tinfo = "kEND4", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kHOM4", .key = NCKEY_HOME, .alt = 1, .shift = 1, },
{ .tinfo = "kEND5", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kHOM5", .key = NCKEY_HOME, .ctrl = 1, },
{ .tinfo = "kEND6", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kHOM6", .key = NCKEY_HOME, .ctrl = 1, .shift = 1, },
{ .tinfo = "kEND7", .key = NCKEY_END, }, // FIXME plus modifier { .tinfo = "kHOM7", .key = NCKEY_HOME, .alt = 1, .ctrl = 1, },
{ .tinfo = "kHOM", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kIC", .key = NCKEY_INS, .shift = 1, },
{ .tinfo = "kHOM1", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kIC3", .key = NCKEY_INS, .alt = 1, },
{ .tinfo = "kHOM2", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kIC4", .key = NCKEY_INS, .alt = 1, .shift = 1, },
{ .tinfo = "kHOM3", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kIC5", .key = NCKEY_INS, .ctrl = 1, },
{ .tinfo = "kHOM4", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kIC6", .key = NCKEY_INS, .ctrl = 1, .shift = 1, },
{ .tinfo = "kHOM5", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kIC7", .key = NCKEY_INS, .alt = 1, .ctrl = 1, },
{ .tinfo = "kHOM6", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kLFT", .key = NCKEY_LEFT, .shift = 1, },
{ .tinfo = "kHOM7", .key = NCKEY_HOME, }, // FIXME plus modifier { .tinfo = "kLFT3", .key = NCKEY_LEFT, .alt = 1, },
{ .tinfo = "kIC", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kLFT4", .key = NCKEY_LEFT, .alt = 1, .shift = 1, },
{ .tinfo = "kIC1", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kLFT5", .key = NCKEY_LEFT, .ctrl = 1, },
{ .tinfo = "kIC2", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kLFT6", .key = NCKEY_LEFT, .ctrl = 1, .shift = 1, },
{ .tinfo = "kIC3", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kLFT7", .key = NCKEY_LEFT, .alt = 1, .ctrl = 1, },
{ .tinfo = "kIC4", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kNXT", .key = NCKEY_PGDOWN, .shift = 1, },
{ .tinfo = "kIC5", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kNXT3", .key = NCKEY_PGDOWN, .alt = 1, },
{ .tinfo = "kIC6", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kNXT4", .key = NCKEY_PGDOWN, .alt = 1, .shift = 1, },
{ .tinfo = "kIC7", .key = NCKEY_INS, }, // FIXME plus modifier { .tinfo = "kNXT5", .key = NCKEY_PGDOWN, .ctrl = 1, },
{ .tinfo = "kLFT", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kNXT6", .key = NCKEY_PGDOWN, .ctrl = 1, .shift = 1, },
{ .tinfo = "kLFT1", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kNXT7", .key = NCKEY_PGDOWN, .alt = 1, .ctrl = 1, },
{ .tinfo = "kLFT2", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kPRV", .key = NCKEY_PGUP, .shift = 1, },
{ .tinfo = "kLFT3", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kPRV3", .key = NCKEY_PGUP, .alt = 1, },
{ .tinfo = "kLFT4", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kPRV4", .key = NCKEY_PGUP, .alt = 1, .shift = 1, },
{ .tinfo = "kLFT5", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kPRV5", .key = NCKEY_PGUP, .ctrl = 1, },
{ .tinfo = "kLFT6", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kPRV6", .key = NCKEY_PGUP, .ctrl = 1, .shift = 1, },
{ .tinfo = "kLFT7", .key = NCKEY_LEFT, }, // FIXME plus modifier { .tinfo = "kPRV7", .key = NCKEY_PGUP, .alt = 1, .ctrl = 1, },
{ .tinfo = "kNXT", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kRIT", .key = NCKEY_RIGHT, .shift = 1, },
{ .tinfo = "kNXT2", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kRIT3", .key = NCKEY_RIGHT, .alt = 1, },
{ .tinfo = "kNXT3", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kRIT4", .key = NCKEY_RIGHT, .alt = 1, .shift = 1, },
{ .tinfo = "kNXT4", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kRIT5", .key = NCKEY_RIGHT, .ctrl = 1, },
{ .tinfo = "kNXT5", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kRIT6", .key = NCKEY_RIGHT, .ctrl = 1, .shift = 1, },
{ .tinfo = "kNXT6", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kRIT7", .key = NCKEY_RIGHT, .alt = 1, .ctrl = 1, },
{ .tinfo = "kNXT7", .key = NCKEY_PGDOWN, }, // FIXME plus modifier { .tinfo = "kUP", .key = NCKEY_UP, .shift = 1, },
{ .tinfo = "kPRV", .key = NCKEY_PGUP, }, // FIXME plus modifier { .tinfo = "kUP3", .key = NCKEY_UP, .alt = 1, },
{ .tinfo = "kPRV2", .key = NCKEY_PGUP, }, // FIXME plus modifier { .tinfo = "kUP4", .key = NCKEY_UP, .alt = 1, .shift = 1, },
{ .tinfo = "kPRV3", .key = NCKEY_PGUP, }, // FIXME plus modifier { .tinfo = "kUP5", .key = NCKEY_UP, .ctrl = 1, },
{ .tinfo = "kPRV4", .key = NCKEY_PGUP, }, // FIXME plus modifier { .tinfo = "kUP6", .key = NCKEY_UP, .ctrl = 1, .shift = 1, },
{ .tinfo = "kPRV5", .key = NCKEY_PGUP, }, // FIXME plus modifier { .tinfo = "kUP7", .key = NCKEY_UP, .alt = 1, .ctrl = 1, },
{ .tinfo = "kPRV6", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .tinfo = "kPRV7", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .tinfo = "kRIT", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT1", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT2", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT3", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT4", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT5", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT6", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kRIT7", .key = NCKEY_RIGHT, }, // FIXME plus modifier
{ .tinfo = "kUP", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP1", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP2", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP3", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP4", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP5", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP6", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = "kUP7", .key = NCKEY_UP, }, // FIXME plus modifier
{ .tinfo = NULL, .key = NCKEY_INVALID, } { .tinfo = NULL, .key = NCKEY_INVALID, }
}, *k; }, *k;
for(k = keys ; k->tinfo ; ++k){ for(k = keys ; k->tinfo ; ++k){
@ -807,15 +799,15 @@ prep_special_keys(ncinputlayer* nc){
continue; continue;
} }
if(seq[0] != NCKEY_ESC){ if(seq[0] != NCKEY_ESC){
//fprintf(stderr, "Terminfo's %s is not an escape sequence (%zub)\n", k->tinfo, strlen(seq)); loginfo("%s is not an escape sequence\n", k->tinfo);
continue; continue;
} }
logdebug("support for terminfo's %s: %s\n", k->tinfo, seq); logdebug("support for terminfo's %s: %s\n", k->tinfo, seq);
if(ncinputlayer_add_input_escape(nc, seq, k->key)){ if(ncinputlayer_add_input_escape(nc, seq, k->key, k->shift, k->ctrl, k->alt)){
return -1; return -1;
} }
} }
if(ncinputlayer_add_input_escape(nc, CSIPREFIX, NCKEY_CSI)){ if(ncinputlayer_add_input_escape(nc, CSIPREFIX, NCKEY_CSI, 0, 0, 0)){
return -1; return -1;
} }
return 0; return 0;
@ -1688,10 +1680,10 @@ void ncinput_extract_clrs(ncinputlayer* ni){
if(rlen >= sizeof(ni->inputbuf) / sizeof(*ni->inputbuf) - ni->inputbuf_write_at){ if(rlen >= sizeof(ni->inputbuf) / sizeof(*ni->inputbuf) - ni->inputbuf_write_at){
rlen = sizeof(ni->inputbuf) / sizeof(*ni->inputbuf) - ni->inputbuf_write_at; rlen = sizeof(ni->inputbuf) / sizeof(*ni->inputbuf) - ni->inputbuf_write_at;
} }
logdebug("Reading %zu from %d\n", rlen, ni->infd); logdebug("Reading %llu from %d\n", rlen, ni->infd);
ssize_t r; ssize_t r;
if((r = read(ni->infd, ni->inputbuf + ni->inputbuf_write_at, rlen)) > 0){ if((r = read(ni->infd, ni->inputbuf + ni->inputbuf_write_at, rlen)) > 0){
logdebug("Read %zu from %d\n", r, ni->infd); logdebug("Read %llu from %d\n", r, ni->infd);
ni->inputbuf_write_at += r; ni->inputbuf_write_at += r;
if(ni->inputbuf_write_at == sizeof(ni->inputbuf) / sizeof(*ni->inputbuf)){ if(ni->inputbuf_write_at == sizeof(ni->inputbuf) / sizeof(*ni->inputbuf)){
ni->inputbuf_write_at = 0; ni->inputbuf_write_at = 0;

@ -10,6 +10,9 @@ extern "C" {
#include "compat/compat.h" #include "compat/compat.h"
#include "notcurses/notcurses.h" #include "notcurses/notcurses.h"
// KEY_EVENT is defined by both ncurses.h and wincon.h. since we don't use
// either definition, kill it before inclusion of ncurses.h.
#undef KEY_EVENT
#include <ncurses.h> // needed for some definitions, see terminfo(3ncurses) #include <ncurses.h> // needed for some definitions, see terminfo(3ncurses)
#include <term.h> #include <term.h>
#include <time.h> #include <time.h>
@ -29,10 +32,11 @@ extern "C" {
#ifndef __MINGW64__ #ifndef __MINGW64__
#include <langinfo.h> #include <langinfo.h>
#endif #endif
#include "termdesc.h" #include "lib/termdesc.h"
#include "egcpool.h" #include "lib/egcpool.h"
#include "sprite.h" #include "lib/sprite.h"
#include "fbuf.h" #include "lib/fbuf.h"
#include "lib/gpm.h"
#define API __attribute__((visibility("default"))) #define API __attribute__((visibility("default")))
#define ALLOC __attribute__((malloc)) __attribute__((warn_unused_result)) #define ALLOC __attribute__((malloc)) __attribute__((warn_unused_result))
@ -362,7 +366,6 @@ typedef struct notcurses {
ncstats stashed_stats; // retain across a context reset, for closing banner ncstats stashed_stats; // retain across a context reset, for closing banner
FILE* ttyfp; // FILE* for writing rasterized data FILE* ttyfp; // FILE* for writing rasterized data
FILE* renderfp; // debugging FILE* to which renderings are written
tinfo tcache; // terminfo cache tinfo tcache; // terminfo cache
pthread_mutex_t pilelock; // guards pile list, locks resize in render pthread_mutex_t pilelock; // guards pile list, locks resize in render
bool suppress_banner; // from notcurses_options bool suppress_banner; // from notcurses_options
@ -429,26 +432,6 @@ int ncvisual_blitset_geom(const notcurses* nc, const tinfo* tcache,
int* y, int* x, int* scaley, int* scalex, int* y, int* x, int* scaley, int* scalex,
int* leny, int* lenx, const struct blitset** blitter); int* leny, int* lenx, const struct blitset** blitter);
static inline int
ncfputs(const char* ext, FILE* out){
int r;
#ifdef __USE_GNU
r = fputs_unlocked(ext, out);
#else
r = fputs(ext, out);
#endif
return r;
}
static inline int
ncfputc(char c, FILE* out){
#ifdef __USE_GNU
return putc_unlocked(c, out);
#else
return putc(c, out);
#endif
}
void reset_stats(ncstats* stats); void reset_stats(ncstats* stats);
void summarize_stats(notcurses* nc); void summarize_stats(notcurses* nc);
@ -730,9 +713,14 @@ sprite_scrub(const notcurses* n, const ncpile* p, sprixel* s){
static inline int static inline int
sprite_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f, sprite_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
int y, int x){ int y, int x){
if(!ti->pixel_draw){
return 0;
}
int offy, offx;
ncplane_yx(s->n, &offy, &offx);
//sprixel_debug(s, stderr); //sprixel_debug(s, stderr);
logdebug("sprixel %u state %d\n", s->id, s->invalidated); logdebug("sprixel %u state %d\n", s->id, s->invalidated);
return ti->pixel_draw(ti, p, s, f, y, x); return ti->pixel_draw(ti, p, s, f, y + offy, x + offx);
} }
// precondition: s->invalidated is SPRIXEL_MOVED or SPRIXEL_INVALIDATED // precondition: s->invalidated is SPRIXEL_MOVED or SPRIXEL_INVALIDATED
@ -749,6 +737,9 @@ sprite_redraw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
bool noscroll = !ti->sixel_maxy_pristine; bool noscroll = !ti->sixel_maxy_pristine;
return ti->pixel_move(s, f, noscroll); return ti->pixel_move(s, f, noscroll);
}else{ }else{
if(!ti->pixel_draw){
return 0;
}
return ti->pixel_draw(ti, p, s, f, y, x); return ti->pixel_draw(ti, p, s, f, y, x);
} }
} }
@ -1073,38 +1064,6 @@ tty_emit(const char* seq, int fd){
int set_fd_nonblocking(int fd, unsigned state, unsigned* oldstate); int set_fd_nonblocking(int fd, unsigned state, unsigned* oldstate);
// reliably flush a FILE*...except you can't, so far as i can tell. at least
// on glibc, a single fflush() error latches the FILE* error, but ceases to
// perform any work (even following a clearerr()), despite returning 0 from
// that point on. thus, after a fflush() error, even on EAGAIN and friends,
// you can't use the stream any further. doesn't this make fflush() pretty
// much useless? it sure would seem to, which is why we use an fbuf for
// all our important I/O, which we then blit with blocking_write(). if you
// care about your data, you'll do the same.
static inline int
ncflush(FILE* out){
if(ferror(out)){
logerror("Not attempting a flush following error\n");
}
if(fflush(out) == EOF){
logerror("Unrecoverable error flushing io (%s)\n", strerror(errno));
return -1;
}
return 0;
}
static inline int
term_emit(const char* seq, FILE* out, bool flush){
if(!seq){
return -1;
}
if(ncfputs(seq, out) == EOF){
logerror("Error emitting %zub escape (%s)\n", strlen(seq), strerror(errno));
return -1;
}
return flush ? ncflush(out) : 0;
}
static inline int static inline int
term_bg_palindex(const notcurses* nc, fbuf* f, unsigned pal){ term_bg_palindex(const notcurses* nc, fbuf* f, unsigned pal){
const char* setab = get_escape(&nc->tcache, ESCAPE_SETAB); const char* setab = get_escape(&nc->tcache, ESCAPE_SETAB);
@ -1184,7 +1143,10 @@ coerce_styles(fbuf* f, const tinfo* ti, uint16_t* curstyle,
#define SET_SGR_MODE_MOUSE "1006" #define SET_SGR_MODE_MOUSE "1006"
static inline int static inline int
mouse_enable(FILE* out){ mouse_enable(tinfo* ti, FILE* out){
if(ti->qterm == TERMINAL_LINUX){
return gpm_connect(ti);
}
// Sets the shift-escape option, allowing shift+mouse to override the standard // Sets the shift-escape option, allowing shift+mouse to override the standard
// mouse protocol (mainly so copy-and-paste can still be performed). // mouse protocol (mainly so copy-and-paste can still be performed).
#define XTSHIFTESCAPE "\x1b[>1s" #define XTSHIFTESCAPE "\x1b[>1s"
@ -1195,7 +1157,10 @@ mouse_enable(FILE* out){
} }
static inline int static inline int
mouse_disable(fbuf* f){ mouse_disable(tinfo* ti, fbuf* f){
if(ti->qterm == TERMINAL_LINUX){
return gpm_close(ti);
}
return fbuf_emit(f, "\x1b[?" SET_BTN_EVENT_MOUSE ";" return fbuf_emit(f, "\x1b[?" SET_BTN_EVENT_MOUSE ";"
/*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "l"); /*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "l");
} }
@ -1736,8 +1701,8 @@ typedef struct ncvisual_implementation {
API extern ncvisual_implementation visual_implementation; API extern ncvisual_implementation visual_implementation;
static inline char static inline char
path_seperator(void){ path_separator(void){
#if defined _WIN32 || defined __CYGWIN__ #ifdef __MINGW64__
return '\\'; return '\\';
#else #else
return '/'; return '/';
@ -1753,7 +1718,7 @@ prefix_data(const char* base){
char* path = (char*)malloc(len); // cast for C++ includers char* path = (char*)malloc(len); // cast for C++ includers
if(path){ if(path){
memcpy(path, NOTCURSES_SHARE, dlen); memcpy(path, NOTCURSES_SHARE, dlen);
path[dlen] = path_seperator(); path[dlen] = path_separator();
strcpy(path + dlen + 1, base); strcpy(path + dlen + 1, base);
} }
return path; return path;

@ -1,101 +0,0 @@
// the iterm2 graphics protocol is based entirely around containerized formats
// https://iterm2.com/documentation-images.html
#include <stdio.h>
#include "internal.h"
#include "termdesc.h"
#include "sprite.h"
#include "fbuf.h"
#include "png.h"
// yank a cell out of the PNG by setting all of its alphas to 0. the alphas
// will be preserved in the auxvec.
int iterm_wipe(sprixel* s, int ycell, int xcell){
(void)s; // FIXME
(void)ycell;
(void)xcell;
return -1;
}
// build a cell of the PNG back up by copying auxvec alphas to it.
int iterm_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
(void)s; // FIXME
(void)ycell;
(void)xcell;
(void)auxvec;
return -1;
}
// spit out the control sequence and data.
int iterm_draw(const tinfo* ti, const ncpile *p, sprixel* s, fbuf* f, int y, int x){
(void)ti;
if(p){
if(goto_location(p->nc, f, y, x)){
return -1;
}
}
if(fbuf_putn(f, s->glyph.buf, s->glyph.used) < 0){
return -1;
}
return s->glyph.used;
}
static int
write_iterm_graphic(tament* tam, const void* data, int leny, int stride, int lenx, fbuf *f){
if(fbuf_puts(f, "\e]1337;File=inline=1:") < 0){
return -1;
}
if(write_png_b64(tam, data, leny, stride, lenx, f)){
return -1;
}
if(fbuf_puts(f, "\x1b\\") < 0){
return -1;
}
return 0;
}
// create an iterm2 control sequence complete with base64-encoded PNG.
int iterm_blit(ncplane* n, int linesize, const void* data,
int leny, int lenx, const blitterargs* bargs){
int cols = bargs->u.pixel.spx->dimx;
int rows = bargs->u.pixel.spx->dimy;
sprixel* s = bargs->u.pixel.spx;
tament* tam = NULL;
bool reuse = false;
// if we have a sprixel attached to this plane, see if we can reuse it
// (we need the same dimensions) and thus immediately apply its T-A table.
if(n->tam){
if(n->leny == rows && n->lenx == cols){
tam = n->tam;
reuse = true;
}
}
int parse_start = 0;
if(!reuse){
tam = malloc(sizeof(*tam) * rows * cols);
if(tam == NULL){
return -1;
}
memset(tam, 0, sizeof(*tam) * rows * cols);
}
if(fbuf_init(&s->glyph)){
free(tam);
return -1;
}
if(write_iterm_graphic(tam, data, leny, linesize, lenx, &s->glyph)){
if(!reuse){
free(tam);
}
fbuf_free(&s->glyph);
return -1;
}
scrub_tam_boundaries(tam, leny, lenx, s->cellpxy, s->cellpxx);
if(plane_blit_sixel(s, &s->glyph, leny, lenx, parse_start, tam) < 0){
if(!reuse){
free(tam);
}
fbuf_free(&s->glyph);
return -1;
}
return 1;
}

@ -120,10 +120,7 @@ error:
} }
int fbcon_scrub(const struct ncpile* p, sprixel* s){ int fbcon_scrub(const struct ncpile* p, sprixel* s){
(void)p; return sixel_scrub(p, s);
(void)s;
return 0;
//return sixel_scrub(p, s);
} }
#ifdef __linux__ #ifdef __linux__
@ -172,9 +169,7 @@ int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
return 1; return 1;
} }
int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, fbuf* f, int y, int x){ int fbcon_draw(const tinfo* ti, sprixel* s, int y, int x){
(void)p;
(void)f; // we don't write to the stream
int wrote = 0; int wrote = 0;
for(unsigned l = 0 ; l < (unsigned)s->pixy && l + y * ti->cellpixy < ti->pixy ; ++l){ for(unsigned l = 0 ; l < (unsigned)s->pixy && l + y * ti->cellpixy < ti->pixy ; ++l){
// FIXME pixel size isn't necessarily 4B, line isn't necessarily psize*pixx // FIXME pixel size isn't necessarily 4B, line isn't necessarily psize*pixx
@ -688,11 +683,9 @@ int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
return 0; return 0;
} }
int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, fbuf* f, int y, int x){ int fbcon_draw(const tinfo* ti, sprixel* s, int y, int x){
(void)ti; (void)ti;
(void)p;
(void)s; (void)s;
(void)f;
(void)y; (void)y;
(void)x; (void)x;
return 0; return 0;

@ -28,6 +28,26 @@ void notcurses_version_components(int* major, int* minor, int* patch, int* tweak
*tweak = atoi(NOTCURSES_VERSION_TWEAK); *tweak = atoi(NOTCURSES_VERSION_TWEAK);
} }
int notcurses_enter_alternate_screen(notcurses* nc){
if(enter_alternate_screen(nc->ttyfp, &nc->tcache, true)){
return -1;
}
ncplane_set_scrolling(notcurses_stdplane(nc), false);
return 0;
}
int notcurses_leave_alternate_screen(notcurses* nc){
if(leave_alternate_screen(nc->ttyfp, &nc->tcache)){
return -1;
}
// move to the end of our output
if(nc->rstate.logendy < 0){
return 0;
}
ncplane_cursor_move_yx(notcurses_stdplane(nc), nc->rstate.logendy, nc->rstate.logendx);
return 0;
}
// reset the current colors, styles, and palette. called on startup (to purge // reset the current colors, styles, and palette. called on startup (to purge
// any preexisting styling) and shutdown (to not affect further programs). // any preexisting styling) and shutdown (to not affect further programs).
int reset_term_attributes(const tinfo* ti, fbuf* f){ int reset_term_attributes(const tinfo* ti, fbuf* f){
@ -70,7 +90,7 @@ notcurses_stop_minimal(void* vnc){
if(nc->tcache.pixel_shutdown){ if(nc->tcache.pixel_shutdown){
ret |= nc->tcache.pixel_shutdown(f); ret |= nc->tcache.pixel_shutdown(f);
} }
ret |= mouse_disable(f); ret |= mouse_disable(&nc->tcache, f);
ret |= reset_term_attributes(&nc->tcache, f); ret |= reset_term_attributes(&nc->tcache, f);
if(nc->tcache.ttyfd >= 0){ if(nc->tcache.ttyfd >= 0){
if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){ if((esc = get_escape(&nc->tcache, ESCAPE_RMCUP))){
@ -280,12 +300,25 @@ int update_term_dimensions(int* rows, int* cols, tinfo* tcache, int margin_b){
} }
} }
#else #else
/*
CONSOLE_SCREEN_BUFFER_INFO csbi;
int columns, rows;
if(GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi)){
if(cols){
*cols = csbi.srWindow.Right - csbi.srWindow.Left + 1;
}
if(rows){
*rows = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
}
}else{
*/
if(rows){ if(rows){
*rows = tcache->default_rows; *rows = tcache->default_rows;
} }
if(cols){ if(cols){
*cols = tcache->default_cols; *cols = tcache->default_cols;
} }
//}
#endif #endif
if(tcache->sixel_maxy_pristine){ if(tcache->sixel_maxy_pristine){
int sixelrows = *rows - 1; int sixelrows = *rows - 1;
@ -910,23 +943,18 @@ init_banner(const notcurses* nc, fbuf* f){
term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ? term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ?
14 % nc->tcache.caps.colors : 0x2080e0); 14 % nc->tcache.caps.colors : 0x2080e0);
if(nc->tcache.cellpixy && nc->tcache.cellpixx){ if(nc->tcache.cellpixy && nc->tcache.cellpixx){
fbuf_printf(f, "%d rows (%dpx) %d cols (%dpx) %dx%d %luB crend %u colors", fbuf_printf(f, "%d rows (%dpx) %d cols (%dpx) %dx%d ",
nc->stdplane->leny, nc->tcache.cellpixy, nc->stdplane->leny, nc->tcache.cellpixy,
nc->stdplane->lenx, nc->tcache.cellpixx, nc->stdplane->lenx, nc->tcache.cellpixx,
nc->stdplane->leny * nc->tcache.cellpixy, nc->stdplane->leny * nc->tcache.cellpixy,
nc->stdplane->lenx * nc->tcache.cellpixx, nc->stdplane->lenx * nc->tcache.cellpixx);
(unsigned long)sizeof(struct crender),
nc->tcache.caps.colors);
}else{ }else{
fbuf_printf(f, "%d rows %d cols (%sB) %luB crend %u colors", fbuf_printf(f, "%d rows %d cols (%sB) ",
nc->stdplane->leny, nc->stdplane->lenx, nc->stdplane->leny, nc->stdplane->lenx,
bprefix(nc->stats.s.fbbytes, 1, prefixbuf, 0), bprefix(nc->stats.s.fbbytes, 1, prefixbuf, 0));
(unsigned long)sizeof(struct crender),
nc->tcache.caps.colors);
} }
const char* setaf; const char* setaf;
if(nc->tcache.caps.rgb && (setaf = get_escape(&nc->tcache, ESCAPE_SETAF))){ if(nc->tcache.caps.rgb && (setaf = get_escape(&nc->tcache, ESCAPE_SETAF))){
fbuf_putc(f, '+');
term_fg_rgb8(&nc->tcache, f, 0xe0, 0x60, 0x60); term_fg_rgb8(&nc->tcache, f, 0xe0, 0x60, 0x60);
fbuf_putc(f, 'r'); fbuf_putc(f, 'r');
term_fg_rgb8(&nc->tcache, f, 0x60, 0xe0, 0x60); term_fg_rgb8(&nc->tcache, f, 0x60, 0xe0, 0x60);
@ -935,8 +963,10 @@ init_banner(const notcurses* nc, fbuf* f){
fbuf_putc(f, 'b'); fbuf_putc(f, 'b');
term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ? term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ?
14 % nc->tcache.caps.colors : 0x2080e0); 14 % nc->tcache.caps.colors : 0x2080e0);
fbuf_putc(f, '+');
} }
fbuf_printf(f, "\n%s%s, %zuB %s cells\nterminfo from %s zlib %s\n", fbuf_printf(f, "%u colors\n%s%s (%s)\nterminfo from %s zlib %s\n",
nc->tcache.caps.colors,
#ifdef __clang__ #ifdef __clang__
"", // name is contained in __VERSION__ "", // name is contained in __VERSION__
#else #else
@ -947,7 +977,6 @@ init_banner(const notcurses* nc, fbuf* f){
#endif #endif
#endif #endif
__VERSION__, __VERSION__,
sizeof(nccell),
#ifdef __BYTE_ORDER__ #ifdef __BYTE_ORDER__
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
"LE", "LE",
@ -1030,7 +1059,7 @@ recursive_lock_init(pthread_mutex_t *lock){
} }
int notcurses_check_pixel_support(const notcurses* nc){ int notcurses_check_pixel_support(const notcurses* nc){
if(nc->tcache.pixel_draw){ if(nc->tcache.pixel_draw || nc->tcache.pixel_draw_late){
return 1; return 1;
} }
return 0; return 0;
@ -1086,7 +1115,6 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
reset_stats(&ret->stats.s); reset_stats(&ret->stats.s);
reset_stats(&ret->stashed_stats); reset_stats(&ret->stashed_stats);
ret->ttyfp = outfp; ret->ttyfp = outfp;
ret->renderfp = opts->renderfp;
memset(&ret->rstate, 0, sizeof(ret->rstate)); memset(&ret->rstate, 0, sizeof(ret->rstate));
memset(&ret->palette_damage, 0, sizeof(ret->palette_damage)); memset(&ret->palette_damage, 0, sizeof(ret->palette_damage));
memset(&ret->palette, 0, sizeof(ret->palette)); memset(&ret->palette, 0, sizeof(ret->palette));
@ -1202,7 +1230,7 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
if(!(opts->flags & NCOPTION_NO_ALTERNATE_SCREEN)){ if(!(opts->flags & NCOPTION_NO_ALTERNATE_SCREEN)){
const char* smcup = get_escape(&ret->tcache, ESCAPE_SMCUP); const char* smcup = get_escape(&ret->tcache, ESCAPE_SMCUP);
if(smcup){ if(smcup){
if(term_emit(smcup, ret->ttyfp, false)){ if(enter_alternate_screen(ret->ttyfp, &ret->tcache, false)){
free_plane(ret->stdplane); free_plane(ret->stdplane);
goto err; goto err;
} }
@ -2234,7 +2262,7 @@ ncplane* ncplane_above(ncplane* n){
} }
int notcurses_mouse_enable(notcurses* n){ int notcurses_mouse_enable(notcurses* n){
if(mouse_enable(n->ttyfp)){ if(mouse_enable(&n->tcache, n->ttyfp)){
return -1; return -1;
} }
return 0; return 0;
@ -2247,15 +2275,13 @@ int notcurses_mouse_disable(notcurses* n){
if(fbuf_init_small(&f)){ if(fbuf_init_small(&f)){
return -1; return -1;
} }
if(mouse_disable(&f)){ if(mouse_disable(&n->tcache, &f)){
fbuf_free(&f); fbuf_free(&f);
return -1; return -1;
} }
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1 || fflush(n->ttyfp) == EOF){ if(fbuf_finalize(&f, n->ttyfp) < 0){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
return 0; return 0;
} }
@ -2393,6 +2419,10 @@ const ncplane* ncplane_parent_const(const ncplane* n){
return n->boundto; return n->boundto;
} }
ncplane* ncplane_boundlist(ncplane* n){
return n->blist;
}
void ncplane_set_resizecb(ncplane* n, int(*resizecb)(ncplane*)){ void ncplane_set_resizecb(ncplane* n, int(*resizecb)(ncplane*)){
if(n == notcurses_stdplane(ncplane_notcurses(n))){ if(n == notcurses_stdplane(ncplane_notcurses(n))){
return; return;

@ -8,6 +8,7 @@
// common elements of type-parameterized plots // common elements of type-parameterized plots
typedef struct ncplot { typedef struct ncplot {
ncplane* ncp; ncplane* ncp;
ncplane* pixelp; // only used for NCBLIT_PIXEL
/* sloutcount-element circular buffer of samples. the newest one (rightmost) /* sloutcount-element circular buffer of samples. the newest one (rightmost)
is at slots[slotstart]; they get older as you go back (and around). is at slots[slotstart]; they get older as you go back (and around).
elements. slotcount is max(columns, rangex), less label room. */ elements. slotcount is max(columns, rangex), less label room. */
@ -43,7 +44,174 @@ typedef struct nc##X##plot { \
ncplot plot; \ ncplot plot; \
} nc##X##plot; \ } nc##X##plot; \
\ \
int redraw_pixelplot_##T(nc##X##plot* ncp){ \
const int scale = ncplane_notcurses_const(ncp->plot.ncp)->tcache.cellpixx; \
ncplane_erase(ncp->plot.ncp); \
int dimy, dimx; \
ncplane_dim_yx(ncp->plot.ncp, &dimy, &dimx); \
const int scaleddim = dimx * scale; \
/* each transition is worth this much change in value */ \
const size_t states = ncplane_notcurses_const(ncp->plot.ncp)->tcache.cellpixy; \
/* FIXME can we not rid ourselves of this meddlesome double? either way, the \
interval is one row's range (for linear plots), or the base \
(base^slots == maxy-miny) of the range (for exponential plots). */ \
double interval; \
if(ncp->plot.exponentiali){ \
if(ncp->maxy > ncp->miny){ \
interval = pow(ncp->maxy - ncp->miny, (double)1 / (dimy * states)); \
/* fprintf(stderr, "miny: %ju maxy: %ju dimy: %d states: %zu\n", miny, maxy, dimy, states); */ \
}else{ \
interval = 0; \
} \
}else{ \
interval = ncp->maxy < ncp->miny ? 0 : (ncp->maxy - ncp->miny) / ((double)dimy * states); \
} \
const int startx = ncp->plot.labelaxisd ? PREFIXCOLUMNS : 0; /* plot cols begin here */ \
/* if we want fewer slots than there are available columns, our final column \
will be other than the plane's final column. most recent x goes here. */ \
const int finalx = (ncp->plot.slotcount < scaleddim - 1 - (startx * scale) ? \
startx + (ncp->plot.slotcount / scale) - 1 : dimx - 1); \
ncplane_set_styles(ncp->plot.ncp, ncp->plot.legendstyle); \
if(ncp->plot.labelaxisd){ \
/* show the *top* of each interval range */ \
for(int y = 0 ; y < dimy ; ++y){ \
uint64_t channels = 0; \
calc_gradient_channels(&channels, ncp->plot.minchannels, ncp->plot.minchannels, \
ncp->plot.maxchannels, ncp->plot.maxchannels, y, 0, dimy, dimx); \
ncplane_set_channels(ncp->plot.ncp, channels); \
char buf[PREFIXSTRLEN + 1]; \
if(ncp->plot.exponentiali){ \
if(y == dimy - 1){ /* we cheat on the top row to exactly match maxy */ \
ncmetric(ncp->maxy * 100, 100, buf, 0, 1000, '\0'); \
}else{ \
ncmetric(pow(interval, (y + 1) * states) * 100, 100, buf, 0, 1000, '\0'); \
} \
}else{ \
ncmetric((ncp->maxy - interval * states * (dimy - y - 1)) * 100, 100, buf, 0, 1000, '\0'); \
} \
if(y == dimy - 1 && strlen(ncp->plot.title)){ \
ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, 0, "%*.*s %s", \
PREFIXSTRLEN, PREFIXSTRLEN, buf, ncp->plot.title); \
}else{ \
ncplane_printf_yx(ncp->plot.ncp, dimy - y - 1, 0, "%*.*s", \
PREFIXSTRLEN, PREFIXSTRLEN, buf); \
} \
} \
}else if(strlen(ncp->plot.title)){ \
uint64_t channels = 0; \
calc_gradient_channels(&channels, ncp->plot.minchannels, ncp->plot.minchannels, \
ncp->plot.maxchannels, ncp->plot.maxchannels, dimy - 1, 0, dimy, dimx); \
ncplane_set_channels(ncp->plot.ncp, channels); \
ncplane_printf_yx(ncp->plot.ncp, 0, PREFIXCOLUMNS - strlen(ncp->plot.title), "%s", ncp->plot.title); \
} \
ncplane_set_styles(ncp->plot.ncp, NCSTYLE_NONE); \
if(finalx < startx){ /* exit on pathologically narrow planes */ \
return 0; \
} \
if(!interval){ \
interval = 1; \
} \
uint32_t* pixels = malloc(dimy * dimx * states * scale * sizeof(*pixels)); \
if(pixels == NULL){ \
return -1; \
} \
memset(pixels, 0, dimy * dimx * states * scale * sizeof(*pixels)); \
int idx = ncp->plot.slotstart; /* idx holds the real slot index; we move backwards */ \
for(int x = finalx ; x >= startx ; --x){ \
/* a column corresponds to |scale| slots' worth of samples. prepare the working gval set. */ \
T gvals[scale]; \
/* load it retaining the same ordering we have in the actual array */ \
for(int i = scale - 1 ; i >= 0 ; --i){ \
gvals[i] = ncp->slots[idx]; /* clip the value at the limits of the graph */ \
if(gvals[i] < ncp->miny){ \
gvals[i] = ncp->miny; \
} \
if(gvals[i] > ncp->maxy){ \
gvals[i] = ncp->maxy; \
} \
/* FIXME if there are an odd number, only go up through the valid ones... */ \
if(--idx < 0){ \
idx = ncp->plot.slotcount - 1; \
} \
} \
/* starting from the least-significant row, progress in the more significant \
direction, prepping pixels, aborting early if we can't draw anything in a \
given cell. */ \
T intervalbase = ncp->miny; \
bool done = !ncp->plot.bset->fill; \
for(int y = 0 ; y < dimy ; ++y){ \
uint64_t channels = 0; \
/* if we've got at least one interval's worth on the number of positions \
times the number of intervals per position plus the starting offset, \
we're going to print *something* */ \
for(int i = 0 ; i < scale ; ++i){ \
size_t egcidx; \
if(intervalbase < gvals[i]){ \
if(ncp->plot.exponentiali){ \
/* we want the log-base-interval of gvals[i] */ \
double scaled = log(gvals[i] - ncp->miny) / log(interval); \
double sival = intervalbase ? log(intervalbase) / log(interval) : 0; \
egcidx = scaled - sival; \
}else{ \
egcidx = (gvals[i] - intervalbase) / interval; \
} \
if(egcidx >= states){ \
egcidx = states; \
done = false; \
} \
}else{ \
egcidx = 0; \
} \
/*fprintf(stderr, "WRITING TO y/x %d/%d (%zu)\n", y, x, dimx * dimy * scale * states); */\
for(size_t yy = 0 ; yy < egcidx ; ++yy){ \
int poff = x * scale + i + (((dimy - 1 - y) * states + (states - 1 - yy)) * dimx * scale); \
calc_gradient_channels(&channels, ncp->plot.minchannels, ncp->plot.minchannels, \
ncp->plot.maxchannels, ncp->plot.maxchannels, \
y * states + yy, x, dimy * states, dimx); \
uint32_t color = ncchannels_fg_rgb(channels); \
ncpixel_set_a(&color, 0xff); \
pixels[poff] = color; \
} \
} \
if(done){ \
break; \
} \
if(ncp->plot.exponentiali){ \
intervalbase = ncp->miny + pow(interval, (y + 1) * states - 1); \
}else{ \
intervalbase += (states * interval); \
} \
} \
} \
if(ncp->plot.printsample){ \
int lastslot = ncp->plot.slotstart ? ncp->plot.slotstart - 1 : ncp->plot.slotcount - 1; \
ncplane_set_styles(ncp->plot.ncp, ncp->plot.legendstyle); \
ncplane_set_channels(ncp->plot.ncp, ncp->plot.maxchannels); \
ncplane_printf_aligned(ncp->plot.ncp, 0, NCALIGN_RIGHT, "%" PRIu64, (uint64_t)ncp->slots[lastslot]); \
} \
ncplane_home(ncp->plot.ncp); \
struct ncvisual* ncv = ncvisual_from_rgba(pixels, dimy * states, dimx * scale * 4, dimx * scale); \
free(pixels); \
if(ncv == NULL){ \
return -1; \
} \
struct ncvisual_options vopts = { \
.n = ncp->plot.pixelp, \
.blitter = NCBLIT_PIXEL, \
.flags = NCVISUAL_OPTION_NODEGRADE, \
}; \
if(ncvisual_render(ncplane_notcurses(ncp->plot.ncp), ncv, &vopts) == NULL){ \
ncvisual_destroy(ncv); \
return -1; \
} \
ncvisual_destroy(ncv); \
return 0; \
} \
\
int redraw_plot_##T(nc##X##plot* ncp){ \ int redraw_plot_##T(nc##X##plot* ncp){ \
if(ncp->plot.bset->geom == NCBLIT_PIXEL){ \
return redraw_pixelplot_##T(ncp); \
} \
ncplane_erase(ncp->plot.ncp); \ ncplane_erase(ncp->plot.ncp); \
const int scale = ncp->plot.bset->width; \ const int scale = ncp->plot.bset->width; \
int dimy, dimx; \ int dimy, dimx; \
@ -52,8 +220,8 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
/* each transition is worth this much change in value */ \ /* each transition is worth this much change in value */ \
const size_t states = ncp->plot.bset->height + 1; \ const size_t states = ncp->plot.bset->height + 1; \
/* FIXME can we not rid ourselves of this meddlesome double? either way, the \ /* FIXME can we not rid ourselves of this meddlesome double? either way, the \
interval is one row's range (for linear plots), or the base (base^slots== \ interval is one row's range (for linear plots), or the base \
maxy-miny) of the range (for exponential plots). */ \ (base^slots == maxy-miny) of the range (for exponential plots). */ \
double interval; \ double interval; \
if(ncp->plot.exponentiali){ \ if(ncp->plot.exponentiali){ \
if(ncp->maxy > ncp->miny){ \ if(ncp->maxy > ncp->miny){ \
@ -111,7 +279,7 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
int idx = ncp->plot.slotstart; /* idx holds the real slot index; we move backwards */ \ int idx = ncp->plot.slotstart; /* idx holds the real slot index; we move backwards */ \
for(int x = finalx ; x >= startx ; --x){ \ for(int x = finalx ; x >= startx ; --x){ \
/* a single column might correspond to more than 1 ('scale', up to \ /* a single column might correspond to more than 1 ('scale', up to \
MAXWIDTH) slot's worth of samples. prepare the working gval set. */ \ MAXWIDTH) slots' worth of samples. prepare the working gval set. */ \
T gvals[MAXWIDTH]; \ T gvals[MAXWIDTH]; \
/* load it retaining the same ordering we have in the actual array */ \ /* load it retaining the same ordering we have in the actual array */ \
for(int i = scale - 1 ; i >= 0 ; --i){ \ for(int i = scale - 1 ; i >= 0 ; --i){ \
@ -218,7 +386,7 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
return 0; \ return 0; \
} \ } \
\ \
static bool \ static const struct blitset* \
create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, const T miny, const T maxy, \ create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, const T miny, const T maxy, \
const T trueminy, const T truemaxy){ \ const T trueminy, const T truemaxy){ \
ncplot_options zeroed = {}; \ ncplot_options zeroed = {}; \
@ -231,22 +399,22 @@ create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, const T mi
/* if miny == maxy (enabling domain detection), they both must be equal to 0 */ \ /* if miny == maxy (enabling domain detection), they both must be equal to 0 */ \
if(miny == maxy && miny){ \ if(miny == maxy && miny){ \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
if(opts->rangex < 0){ \ if(opts->rangex < 0){ \
logerror("Supplied negative independent range %d\n", opts->rangex); \ logerror("Supplied negative independent range %d\n", opts->rangex); \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
if(maxy < miny){ \ if(maxy < miny){ \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
/* DETECTMAXONLY can't be used without domain detection */ \ /* DETECTMAXONLY can't be used without domain detection */ \
if(opts->flags & NCPLOT_OPTION_DETECTMAXONLY && (miny != maxy)){ \ if(opts->flags & NCPLOT_OPTION_DETECTMAXONLY && (miny != maxy)){ \
logerror("Supplied DETECTMAXONLY without domain detection"); \ logerror("Supplied DETECTMAXONLY without domain detection"); \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
const notcurses* notc = ncplane_notcurses(n); \ const notcurses* notc = ncplane_notcurses(n); \
ncblitter_e blitfxn = opts ? opts->gridtype : NCBLIT_DEFAULT; \ ncblitter_e blitfxn = opts ? opts->gridtype : NCBLIT_DEFAULT; \
@ -257,21 +425,21 @@ create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, const T mi
const struct blitset* bset = lookup_blitset(&notc->tcache, blitfxn, degrade_blitter); \ const struct blitset* bset = lookup_blitset(&notc->tcache, blitfxn, degrade_blitter); \
if(bset == NULL){ \ if(bset == NULL){ \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
int sdimy, sdimx; \ int sdimy, sdimx; \
ncplane_dim_yx(n, &sdimy, &sdimx); \ ncplane_dim_yx(n, &sdimy, &sdimx); \
if(sdimx <= 0){ \ if(sdimx <= 0){ \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
int dimx = sdimx; \ int dimx = sdimx; \
ncpp->plot.title = strdup(opts->title ? opts->title : ""); \ ncpp->plot.title = strdup(opts->title ? opts->title : ""); \
ncpp->plot.rangex = opts->rangex; \ ncpp->plot.rangex = opts->rangex; \
/* if we're sizing the plot based off the plane dimensions, scale it by the \ /* if we're sizing the plot based off the plane dimensions, scale it by the \
plot geometry's width for all calculations */ \ plot geometry's width for all calculations */ \
const int scaleddim = dimx * bset->width; \ const int scaleddim = dimx * (bset->geom == NCBLIT_PIXEL ? ncplane_notcurses(n)->tcache.cellpixx : bset->width); \
const int scaledprefixlen = PREFIXCOLUMNS * bset->width; \ const int scaledprefixlen = PREFIXCOLUMNS * (bset->geom == NCBLIT_PIXEL ? ncplane_notcurses(n)->tcache.cellpixx : bset->width); \
if((ncpp->plot.slotcount = ncpp->plot.rangex) == 0){ \ if((ncpp->plot.slotcount = ncpp->plot.rangex) == 0){ \
ncpp->plot.slotcount = scaleddim; \ ncpp->plot.slotcount = scaleddim; \
} \ } \
@ -290,7 +458,7 @@ create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, const T mi
ncpp->slots = malloc(slotsize); \ ncpp->slots = malloc(slotsize); \
if(ncpp->slots == NULL){ \ if(ncpp->slots == NULL){ \
ncplane_destroy(n); \ ncplane_destroy(n); \
return false; \ return NULL; \
} \ } \
memset(ncpp->slots, 0, slotsize); \ memset(ncpp->slots, 0, slotsize); \
ncpp->plot.ncp = n; \ ncpp->plot.ncp = n; \
@ -310,7 +478,7 @@ create_##T(nc##X##plot* ncpp, ncplane* n, const ncplot_options* opts, const T mi
ncpp->plot.slotstart = 0; \ ncpp->plot.slotstart = 0; \
ncpp->plot.slotx = 0; \ ncpp->plot.slotx = 0; \
redraw_plot_##T(ncpp); \ redraw_plot_##T(ncpp); \
return true; \ return bset; \
} \ } \
/* if x is less than the window, return -1, as the sample will be thrown away. \ /* if x is less than the window, return -1, as the sample will be thrown away. \
if the x is within the current window, find the proper slot and update it. \ if the x is within the current window, find the proper slot and update it. \
@ -382,6 +550,7 @@ static void
ncplot_destroy(ncplot* n){ ncplot_destroy(ncplot* n){
free(n->title); free(n->title);
ncplane_destroy(n->ncp); ncplane_destroy(n->ncp);
ncplane_destroy(n->pixelp);
} }
/* if we're doing domain detection, update the domain to reflect the value we /* if we're doing domain detection, update the domain to reflect the value we
@ -449,6 +618,20 @@ update_sample_double(ncdplot* ncp, int64_t x, double y, bool reset){
} }
} }
static inline int
create_pixelp(ncplot *p, ncplane* n){
if(((p->pixelp = ncplane_dup(n, NULL)) == NULL)){
return -1;
}
ncplane_reparent(p->pixelp, n);
ncplane_move_below(p->pixelp, n);
uint64_t basechan = 0;
ncchannels_set_bg_alpha(&basechan, NCALPHA_TRANSPARENT);
ncchannels_set_fg_alpha(&basechan, NCALPHA_TRANSPARENT);
ncplane_set_base(n, "", 0, basechan);
return 0;
}
// takes ownership of n on all paths // takes ownership of n on all paths
ncuplot* ncuplot_create(ncplane* n, const ncplot_options* opts, uint64_t miny, uint64_t maxy){ ncuplot* ncuplot_create(ncplane* n, const ncplot_options* opts, uint64_t miny, uint64_t maxy){
ncuplot* ret = malloc(sizeof(*ret)); ncuplot* ret = malloc(sizeof(*ret));
@ -457,10 +640,17 @@ ncuplot* ncuplot_create(ncplane* n, const ncplot_options* opts, uint64_t miny, u
return NULL; return NULL;
} }
memset(ret, 0, sizeof(*ret)); memset(ret, 0, sizeof(*ret));
if(!create_uint64_t(ret, n, opts, miny, maxy, 0, UINT64_MAX)){ const struct blitset* bset = create_uint64_t(ret, n, opts, miny, maxy, 0, UINT64_MAX);
if(bset == NULL){
free(ret); free(ret);
return NULL; return NULL;
} }
if(bset->geom == NCBLIT_PIXEL){
if(create_pixelp(&ret->plot, n)){
ncuplot_destroy(ret);
return NULL;
}
}
return ret; return ret;
} }
@ -499,10 +689,17 @@ ncdplot* ncdplot_create(ncplane* n, const ncplot_options* opts, double miny, dou
return NULL; return NULL;
} }
memset(ret, 0, sizeof(*ret)); memset(ret, 0, sizeof(*ret));
if(!create_double(ret, n, opts, miny, maxy, -DBL_MAX, DBL_MAX)){ const struct blitset* bset = create_double(ret, n, opts, miny, maxy, -DBL_MAX, DBL_MAX);
if(bset == NULL){
free(ret); free(ret);
return NULL; return NULL;
} }
if(bset->geom == NCBLIT_PIXEL){
if(create_pixelp(&ret->plot, n)){
ncdplot_destroy(ret);
return NULL;
}
}
return ret; return ret;
} }

@ -1,302 +0,0 @@
#include <zlib.h>
#include <inttypes.h>
#include <stdatomic.h>
#include "visual-details.h"
#include "internal.h"
#include "base64.h"
#include "fbuf.h"
#include "png.h"
// http://www.libpng.org/pub/png/spec/1.2/PNG-Contents.html
// 4-byte length, 4-byte type, 4-byte CRC, plus data
#define CHUNK_DESC_BYTES 12
#define IHDR_DATA_BYTES 13
// PNG generally allows unsigned 32-bit values to only reach 2**31 "to assist
// [loser] languages which have difficulty dealing with unsigned values."
#define CHUNK_MAX_DATA 0x80000000llu
static const unsigned char PNGHEADER[] = "\x89PNG\x0d\x0a\x1a\x0a";
static const unsigned char IEND[] = "\x00\x00\x00\x00IEND\xae\x42\x60\x82";
// FIXME replace with PCLMULQDQ method (and ARM CRC32 instruction)
// this is taken from the PNG reference
static unsigned long crc_table[256];
static atomic_bool crc_table_computed;
static void
make_crc_table(void){
for(size_t n = 0 ; n < sizeof(crc_table) / sizeof(*crc_table) ; n++){
unsigned long c = n;
for(int k = 0 ; k < 8 ; k++){
if(c & 1){
c = 0xedb88320L ^ (c >> 1);
}else{
c = c >> 1;
}
}
crc_table[n] = c;
}
crc_table_computed = true;
}
static inline unsigned long
update_crc(unsigned long crc, const unsigned char *buf, int len){
unsigned long c = crc;
int n;
if(!crc_table_computed){
make_crc_table();
}
for(n = 0 ; n < len ; n++){
c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8);
}
return c;
}
static inline unsigned long
crc(const unsigned char *buf, int len){
return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL;
}
// compress the ncvisual data suitably for PNG. this requires adding a byte
// of filter type (currently always 0) before each scanline =[.
static void*
compress_image(const void* data, int rows, int rowstride, int cols, size_t* dlen){
z_stream zctx = { };
int zret;
if((zret = deflateInit(&zctx, Z_DEFAULT_COMPRESSION)) != Z_OK){
logerror("Couldn't get a deflate context (%d)\n", zret);
return NULL;
}
// one byte per scanline for adaptive filtering type (always 0 for now)
uint64_t databytes = cols * rows * 4 + rows;
unsigned long bound = deflateBound(&zctx, databytes);
unsigned char* buf = malloc(bound);
if(buf == NULL){
logerror("Couldn't allocate %zuB\n", bound);
deflateEnd(&zctx);
return NULL;
}
zctx.next_out = buf;
zctx.avail_out = bound;
// enough space for a single scanline + filter byte
unsigned char* sbuf = malloc(1 + cols * 4);
if(sbuf == NULL){
logerror("Couldn't allocate %zuB\n", 1 + cols * 4);
free(buf);
deflateEnd(&zctx);
return NULL;
}
for(int i = 0 ; i < rows ; ++i){
if(zctx.avail_out == 0){
free(buf);
free(sbuf);
deflateEnd(&zctx);
return NULL;
}
zctx.avail_in = cols * 4 + 1;
// FIXME eliminate sbuf via 2x deflate(Z_NO_FLUSH)
sbuf[0] = 0;
memcpy(sbuf + 1, data + rowstride * i, cols * 4);
zctx.next_in = sbuf;
if((zret = deflate(&zctx, Z_NO_FLUSH)) != Z_OK){
logerror("Error deflating %dB to %dB (%d)\n", zctx.avail_in, zctx.avail_out, zret);
free(buf);
free(sbuf);
return NULL;
}
}
if((zret = deflate(&zctx, Z_FINISH)) != Z_STREAM_END){
logerror("Error finalizing %dB to %dB (%d)\n", zctx.avail_in, zctx.avail_out, zret);
free(buf);
free(sbuf);
return NULL;
}
free(sbuf);
*dlen = zctx.total_out;
if((zret = deflateEnd(&zctx)) != Z_OK){
logerror("Couldn't finalize the deflate context (%d)\n", zret);
free(buf);
return NULL;
}
loginfo("Deflated %"PRIu64" to %zu\n", databytes, *dlen);
return buf;
}
// number of bytes necessary to encode (uncompressed) the visual specified by
// |ncv|. on error, *|deflated| will be NULL.
size_t compute_png_size(const void* data, int rows, int rowstride, int cols,
void** deflated, size_t* dlen){
if((*deflated = compress_image(data, rows, rowstride, cols, dlen)) == NULL){
return 0;
}
//fprintf(stderr, "ACTUAL: %zu (0x%02x) (0x%02x)\n", *dlen, (*(char **)deflated)[*dlen - 1], (*(char**)deflated)[20]);
uint64_t fullchunks = *dlen / CHUNK_MAX_DATA; // full 2GB IDATs
return (sizeof(PNGHEADER) - 1) + // PNG header
CHUNK_DESC_BYTES + IHDR_DATA_BYTES + // mandatory IHDR chunk
(CHUNK_DESC_BYTES + CHUNK_MAX_DATA) * fullchunks +
(CHUNK_DESC_BYTES + *dlen % CHUNK_MAX_DATA) +
CHUNK_DESC_BYTES; // mandatory IEND chunk
}
// calculate the PNG CRC of the chunk starting at |buf|. all chunks start with
// a 4-byte length parameter in NBO, which we use to determine the area over
// which the crc must be calculated (this length only covers the data, not the
// chunk header, so we add 8 bytes). returns 0 on error, but that's also a
// valid value, so whacha gonna do?
static uint32_t
chunk_crc(const unsigned char* buf){
uint32_t length;
memcpy(&length, buf, 4);
length = ntohl(length);
if(length > CHUNK_MAX_DATA){
logerror("Chunk length too large (%lu)\n", length);
return 0;
}
length += 4; // don't use length or crc fields (but type *is* covered)
uint32_t crc32 = htonl(crc(buf + 4, length));
return crc32;
}
// write the ihdr at |buf|, which is guaranteed to be large enough (25B).
static size_t
write_ihdr(int rows, int cols, unsigned char buf[static 25]){
uint32_t length = htonl(IHDR_DATA_BYTES);
memcpy(buf, &length, 4);
static const char ctype[] = "IHDR";
memcpy(buf + 4, ctype, 4);
uint32_t width = htonl(cols);
memcpy(buf + 8, &width, 4);
uint32_t height = htonl(rows);
memcpy(buf + 12, &height, 4);
uint8_t depth = 8; // 8 bits per channel
memcpy(buf + 16, &depth, 1);
uint8_t color = 6; // RGBA
memcpy(buf + 17, &color, 1);
uint8_t compression = 0; // deflate, max window 32768
memcpy(buf + 18, &compression, 1);
uint8_t filter = 0; // adaptive filtering
memcpy(buf + 19, &filter, 1);
uint8_t interlace = 0; // no interlace
memcpy(buf + 20, &interlace, 1);
uint32_t crc = chunk_crc(buf);
memcpy(buf + 21, &crc, 4);
return CHUNK_DESC_BYTES + IHDR_DATA_BYTES; // 25
}
struct b64ctx {
unsigned char src[3]; // try to convert three at a time
size_t srcidx; // how many src bytes we have
};
static int
fbuf_putn64(fbuf* f, const void* src, size_t osize, struct b64ctx* bctx){
size_t w = 0;
char b64[4];
if(bctx->srcidx){
size_t copy = sizeof(bctx->src) - bctx->srcidx;
// the unlikely event that we don't fill the bctx with our entire chunk...
if(copy > osize){
memcpy(bctx->src + bctx->srcidx, src, osize);
bctx->srcidx += osize;
return 0;
}
memcpy(bctx->src + bctx->srcidx, src, copy);
base64x3(bctx->src, b64);
bctx->srcidx = 0;
if(fbuf_putn(f, b64, 4) != 4){
return -1;
}
w = copy;
}
// the bctx is now guaranteed to be empty
while(w + 3 <= osize){
base64x3((const unsigned char*)src + w, b64);
if(fbuf_putn(f, b64, 4) != 4){
return -1;
}
w += 3;
}
// less than 3 remain; copy them into the bctx for further use
if(w < osize){
bctx->srcidx = osize - w;
memcpy(bctx->src, src + w, bctx->srcidx);
}
return 1;
}
static size_t
fwrite_idats(tament* tam, fbuf* f, const unsigned char* data, size_t dlen,
struct b64ctx* bctx){
static const char ctype[] = "IDAT";
uint32_t written = 0;
uint32_t dwritten = 0;
while(dlen){
uint32_t thischunk = dlen;
if(thischunk > CHUNK_MAX_DATA){
thischunk = CHUNK_MAX_DATA;
}
uint32_t nclen = htonl(thischunk);
if(fbuf_putn64(f, &nclen, 4, bctx) != 1 ||
fbuf_putn64(f, ctype, 4, bctx) != 1 ||
fbuf_putn64(f, data + dwritten, thischunk, bctx) != 1){
return 0;
}
// FIXME use the TAM!
(void)tam;
// FIXME horrible; PoC; do not retain!
unsigned char* crcbuf = malloc(thischunk + 8);
memcpy(crcbuf, &nclen, 4);
memcpy(crcbuf + 4, ctype, 4);
memcpy(crcbuf + 8, data + dwritten, thischunk);
// END horribleness
uint32_t crc = chunk_crc(crcbuf);
free(crcbuf); // FIXME well a bit more
if(fbuf_putn64(f, &crc, 4, bctx) != 1){
return 0;
}
dlen -= thischunk;
dwritten += thischunk;
written += CHUNK_DESC_BYTES + thischunk;
}
return written;
}
int write_png_b64(tament* tam, const void* data, int rows, int rowstride,
int cols, fbuf* f){
void* deflated;
size_t dlen;
compute_png_size(data, rows, rowstride, cols, &deflated, &dlen);
if(deflated == NULL){
return -1;
}
struct b64ctx bctx = { };
if(fbuf_putn64(f, PNGHEADER, sizeof(PNGHEADER) - 1, &bctx) < 0){
free(deflated);
return -1;
}
unsigned char ihdr[25];
write_ihdr(rows, cols, ihdr);
if(fbuf_putn64(f, ihdr, sizeof(ihdr), &bctx) != 1){
free(deflated);
return -1;
}
if(fwrite_idats(tam, f, deflated, dlen, &bctx) == 0){
free(deflated);
return -1;
}
free(deflated);
if(fbuf_putn64(f, IEND, sizeof(IEND) - 1, &bctx) != 1){
return -1;
}
if(bctx.srcidx){
char b64[4];
base64final(bctx.src, b64, bctx.srcidx);
if(fbuf_putn(f, b64, 4) < 0){
return -1;
}
}
return 0;
}

@ -1,21 +0,0 @@
#ifndef NOTCURSES_PNG
#define NOTCURSES_PNG
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
struct fbuf;
struct tament;
// create the PNG, encode it using base64, and write it to |fp|
int write_png_b64(struct tament* tam, const void* data, int rows, int rowstride,
int cols, struct fbuf* f);
#ifdef __cplusplus
}
#endif
#endif

@ -842,7 +842,7 @@ clean_sprixels(notcurses* nc, ncpile* p, fbuf* f){
} }
if(s->invalidated == SPRIXEL_MOVED || s->invalidated == SPRIXEL_INVALIDATED || s->invalidated == SPRIXEL_UNSEEN){ if(s->invalidated == SPRIXEL_MOVED || s->invalidated == SPRIXEL_INVALIDATED || s->invalidated == SPRIXEL_UNSEEN){
int y, x; int y, x;
ncplane_yx(s->n, &y, &x); ncplane_abs_yx(s->n, &y, &x);
//fprintf(stderr, "1 MOVING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n); //fprintf(stderr, "1 MOVING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n);
if(s->invalidated == SPRIXEL_MOVED){ if(s->invalidated == SPRIXEL_MOVED){
if(p != nc->last_pile){ if(p != nc->last_pile){
@ -975,19 +975,18 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){
while( (s = *parent) ){ while( (s = *parent) ){
//fprintf(stderr, "YARR HARR HARR SPIRXLE %u STATE %d\n", s->id, s->invalidated); //fprintf(stderr, "YARR HARR HARR SPIRXLE %u STATE %d\n", s->id, s->invalidated);
if(s->invalidated == SPRIXEL_INVALIDATED){ if(s->invalidated == SPRIXEL_INVALIDATED){
int y, x; //fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, nc->margin_t, nc->margin_l, s->n);
ncplane_yx(s->n, &y, &x); int r = sprite_draw(&nc->tcache, p, s, f, nc->margin_t, nc->margin_l);
//fprintf(stderr, "3 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, y + nc->margin_t, x + nc->margin_l, s->n);
int r = sprite_draw(&nc->tcache, p, s, f, y + nc->margin_t, x + nc->margin_l);
if(r < 0){ if(r < 0){
return -1; return -1;
} }else if(r > 0){
bytesemitted += r; bytesemitted += r;
nc->rstate.hardcursorpos = true; nc->rstate.hardcursorpos = true;
}
}else if(s->invalidated == SPRIXEL_LOADED){ }else if(s->invalidated == SPRIXEL_LOADED){
if(nc->tcache.pixel_commit){ if(nc->tcache.pixel_commit){
int y, x; int y, x;
ncplane_yx(s->n, &y, &x); ncplane_abs_yx(s->n, &y, &x);
if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l)){ if(goto_location(nc, f, y + nc->margin_t, x + nc->margin_l)){
return -1; return -1;
} }
@ -1013,6 +1012,34 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){
return bytesemitted; return bytesemitted;
} }
// bitmap backends which don't use the bytestream (currently only fbcon)
// need go at the very end, following writeout. pass again, invoking
// pixel_draw_late if defined.
static int64_t
rasterize_sprixels_post(notcurses* nc, ncpile* p){
if(!nc->tcache.pixel_draw_late){
return 0;
}
int64_t bytesemitted = 0;
sprixel* s;
sprixel** parent = &p->sprixelcache;
while( (s = *parent) ){
//fprintf(stderr, "YARR HARR HARR SPIRXLE %u STATE %d\n", s->id, s->invalidated);
if(s->invalidated == SPRIXEL_INVALIDATED || s->invalidated == SPRIXEL_UNSEEN){
int offy, offx;
ncplane_abs_yx(s->n, &offy, &offx);
//fprintf(stderr, "5 DRAWING BITMAP %d STATE %d AT %d/%d for %p\n", s->id, s->invalidated, nc->margin_t + offy, nc->margin_l + offx, s->n);
int r = nc->tcache.pixel_draw_late(&nc->tcache, s, nc->margin_t + offy, nc->margin_l + offx);
if(r < 0){
return -1;
}
bytesemitted += r;
}
parent = &s->next;
}
return bytesemitted;
}
// Producing the frame requires three steps: // Producing the frame requires three steps:
// * render -- build up a flat framebuffer from a set of ncplanes // * render -- build up a flat framebuffer from a set of ncplanes
// * rasterize -- build up a UTF-8/ASCII stream of escapes and EGCs // * rasterize -- build up a UTF-8/ASCII stream of escapes and EGCs
@ -1237,10 +1264,8 @@ raster_and_write(notcurses* nc, ncpile* p, fbuf* f){
ret = -1; ret = -1;
} }
unblock_signals(&oldmask); unblock_signals(&oldmask);
rasterize_sprixels_post(nc, p);
//fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu %d\n", nc->stats.defaultelisions, nc->stats.defaultemissions, nc->stats.fgelisions, nc->stats.fgemissions, nc->stats.bgelisions, nc->stats.bgemissions, ret); //fprintf(stderr, "%lu/%lu %lu/%lu %lu/%lu %d\n", nc->stats.defaultelisions, nc->stats.defaultemissions, nc->stats.fgelisions, nc->stats.fgemissions, nc->stats.bgelisions, nc->stats.bgemissions, ret);
if(nc->renderfp){
fprintf(nc->renderfp, "%s\n", (const char*)nc->rstate.f.buf);
}
if(ret < 0){ if(ret < 0){
return ret; return ret;
} }
@ -1558,11 +1583,9 @@ int ncdirect_set_bg_rgb(ncdirect* nc, unsigned rgb){
fbuf_free(&f); fbuf_free(&f);
return -1; return -1;
} }
if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1){ if(fbuf_finalize(&f, nc->ttyfp) < 0){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
ncchannels_set_bg_rgb(&nc->channels, rgb); ncchannels_set_bg_rgb(&nc->channels, rgb);
return 0; return 0;
} }
@ -1583,11 +1606,9 @@ int ncdirect_set_fg_rgb(ncdirect* nc, unsigned rgb){
fbuf_free(&f); fbuf_free(&f);
return -1; return -1;
} }
if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1){ if(fbuf_finalize(&f, nc->ttyfp) < 0){
fbuf_free(&f);
return -1; return -1;
} }
fbuf_free(&f);
ncchannels_set_fg_rgb(&nc->channels, rgb); ncchannels_set_fg_rgb(&nc->channels, rgb);
return 0; return 0;
} }

@ -166,11 +166,9 @@ int kitty_wipe(sprixel* s, int ycell, int xcell);
int kitty_wipe_animation(sprixel* s, int ycell, int xcell); int kitty_wipe_animation(sprixel* s, int ycell, int xcell);
int kitty_wipe_selfref(sprixel* s, int ycell, int xcell); int kitty_wipe_selfref(sprixel* s, int ycell, int xcell);
// wipes out a cell by changing the alpha value throughout the PNG cell to 0. // wipes out a cell by changing the alpha value throughout the PNG cell to 0.
int iterm_wipe(sprixel* s, int ycell, int xcell);
int fbcon_wipe(sprixel* s, int ycell, int xcell); int fbcon_wipe(sprixel* s, int ycell, int xcell);
int sixel_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int sixel_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int kitty_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int iterm_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_animation(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
int kitty_rebuild_selfref(sprixel* s, int ycell, int xcell, uint8_t* auxvec); int kitty_rebuild_selfref(sprixel* s, int ycell, int xcell, uint8_t* auxvec);
@ -178,8 +176,6 @@ int sixel_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
fbuf* f, int y, int x); fbuf* f, int y, int x);
int kitty_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, int kitty_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
fbuf* f, int y, int x); fbuf* f, int y, int x);
int iterm_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
fbuf* f, int y, int x);
int kitty_move(sprixel* s, fbuf* f, unsigned noscroll); int kitty_move(sprixel* s, fbuf* f, unsigned noscroll);
int sixel_scrub(const struct ncpile* p, sprixel* s); int sixel_scrub(const struct ncpile* p, sprixel* s);
int kitty_scrub(const struct ncpile* p, sprixel* s); int kitty_scrub(const struct ncpile* p, sprixel* s);
@ -197,16 +193,13 @@ int sixel_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int kitty_blit(struct ncplane* nc, int linesize, const void* data, int kitty_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int iterm_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs);
int kitty_blit_animated(struct ncplane* n, int linesize, const void* data, int kitty_blit_animated(struct ncplane* n, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int kitty_blit_selfref(struct ncplane* nc, int linesize, const void* data, int kitty_blit_selfref(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int fbcon_blit(struct ncplane* nc, int linesize, const void* data, int fbcon_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs); int leny, int lenx, const struct blitterargs* bargs);
int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, int fbcon_draw(const tinfo* ti, sprixel* s, int y, int x);
fbuf* f, int y, int x);
void fbcon_scroll(const struct ncpile* p, tinfo* ti, int rows); void fbcon_scroll(const struct ncpile* p, tinfo* ti, int rows);
typedef enum { typedef enum {

@ -53,6 +53,7 @@ setup_sixel_bitmaps(tinfo* ti, int fd, bool invert80){
ti->pixel_init = sixel_init; ti->pixel_init = sixel_init;
} }
ti->pixel_draw = sixel_draw; ti->pixel_draw = sixel_draw;
ti->pixel_draw_late = NULL;
ti->pixel_scrub = sixel_scrub; ti->pixel_scrub = sixel_scrub;
ti->pixel_wipe = sixel_wipe; ti->pixel_wipe = sixel_wipe;
ti->pixel_remove = NULL; ti->pixel_remove = NULL;
@ -66,26 +67,6 @@ setup_sixel_bitmaps(tinfo* ti, int fd, bool invert80){
sprite_init(ti, fd); sprite_init(ti, fd);
} }
// iterm2 has a container-based protocol
static inline void
setup_iterm_bitmaps(tinfo* ti, int fd){
ti->pixel_init = NULL;
ti->pixel_shutdown = NULL;
ti->pixel_remove = NULL;
// be awarre: absence of pixel_move plus absence of sixel details is used by
// notcurses-info to determine iTerm2 support.
ti->pixel_move = NULL;
ti->color_registers = 0;
ti->pixel_scrub = sixel_scrub;
ti->pixel_scroll = NULL;
ti->pixel_draw = iterm_draw;
ti->pixel_wipe = iterm_wipe;
ti->pixel_rebuild = iterm_rebuild;
ti->pixel_trans_auxvec = kitty_trans_auxvec;
set_pixel_blitter(iterm_blit);
sprite_init(ti, fd);
}
// kitty 0.19.3 didn't have C=1, and thus needs sixel_maxy_pristine. it also // kitty 0.19.3 didn't have C=1, and thus needs sixel_maxy_pristine. it also
// lacked animation, and must thus redraw the complete image every time it // lacked animation, and must thus redraw the complete image every time it
// changes. requires the older interface. // changes. requires the older interface.
@ -94,6 +75,7 @@ setup_kitty_bitmaps(tinfo* ti, int fd, kitty_graphics_e level){
ti->pixel_scrub = kitty_scrub; ti->pixel_scrub = kitty_scrub;
ti->pixel_remove = kitty_remove; ti->pixel_remove = kitty_remove;
ti->pixel_draw = kitty_draw; ti->pixel_draw = kitty_draw;
ti->pixel_draw_late = NULL;
ti->pixel_commit = kitty_commit; ti->pixel_commit = kitty_commit;
ti->pixel_move = kitty_move; ti->pixel_move = kitty_move;
ti->pixel_scroll = NULL; ti->pixel_scroll = NULL;
@ -109,10 +91,12 @@ setup_kitty_bitmaps(tinfo* ti, int fd, kitty_graphics_e level){
if(level == KITTY_ANIMATION){ if(level == KITTY_ANIMATION){
ti->pixel_wipe = kitty_wipe_animation; ti->pixel_wipe = kitty_wipe_animation;
ti->pixel_rebuild = kitty_rebuild_animation; ti->pixel_rebuild = kitty_rebuild_animation;
ti->sixel_maxy_pristine = 0;
set_pixel_blitter(kitty_blit_animated); set_pixel_blitter(kitty_blit_animated);
}else{ }else{
ti->pixel_wipe = kitty_wipe_selfref; ti->pixel_wipe = kitty_wipe_selfref;
ti->pixel_rebuild = kitty_rebuild_selfref; ti->pixel_rebuild = kitty_rebuild_selfref;
ti->sixel_maxy_pristine = 0;
set_pixel_blitter(kitty_blit_selfref); set_pixel_blitter(kitty_blit_selfref);
} }
} }
@ -124,7 +108,8 @@ static inline void
setup_fbcon_bitmaps(tinfo* ti, int fd){ setup_fbcon_bitmaps(tinfo* ti, int fd){
ti->pixel_rebuild = fbcon_rebuild; ti->pixel_rebuild = fbcon_rebuild;
ti->pixel_wipe = fbcon_wipe; ti->pixel_wipe = fbcon_wipe;
ti->pixel_draw = fbcon_draw; ti->pixel_draw = NULL;
ti->pixel_draw_late = fbcon_draw;
ti->pixel_scroll = fbcon_scroll; ti->pixel_scroll = fbcon_scroll;
ti->pixel_scrub = fbcon_scrub; ti->pixel_scrub = fbcon_scrub;
ti->pixel_trans_auxvec = kitty_trans_auxvec; ti->pixel_trans_auxvec = kitty_trans_auxvec;
@ -574,7 +559,6 @@ apply_term_heuristics(tinfo* ti, const char* termname, queried_terminals_e qterm
} }
ti->caps.quadrants = true; ti->caps.quadrants = true;
ti->caps.rgb = true; ti->caps.rgb = true;
setup_iterm_bitmaps(ti, ti->ttyfd);
}else if(qterm == TERMINAL_APPLE){ }else if(qterm == TERMINAL_APPLE){
termname = "Terminal.app"; termname = "Terminal.app";
// no quadrants, no sextants, no rgb, but it does have braille // no quadrants, no sextants, no rgb, but it does have braille
@ -680,7 +664,7 @@ macos_early_matches(void){
int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned utf8, int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned utf8,
unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts, unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts,
int* cursor_y, int* cursor_x, ncsharedstats* stats){ int* cursor_y, int* cursor_x, ncsharedstats* stats){
queried_terminals_e qterm = TERMINAL_UNKNOWN; ti->qterm = TERMINAL_UNKNOWN;
memset(ti, 0, sizeof(*ti)); memset(ti, 0, sizeof(*ti));
// we don't need a controlling tty for everything we do; allow a failure here // we don't need a controlling tty for everything we do; allow a failure here
ti->ttyfd = get_tty_fd(out); ti->ttyfd = get_tty_fd(out);
@ -688,19 +672,19 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
size_t tableused = 0; size_t tableused = 0;
const char* tname = NULL; const char* tname = NULL;
#ifdef __APPLE__ #ifdef __APPLE__
qterm = macos_early_matches(); ti->qterm = macos_early_matches();
#elif defined(__MINGW64__) #elif defined(__MINGW64__)
if(prepare_windows_terminal(ti, &tablelen, &tableused)){ if(prepare_windows_terminal(ti, &tablelen, &tableused)){
return -1; return -1;
} }
qterm = TERMINAL_MSTERMINAL; ti->qterm = TERMINAL_MSTERMINAL;
(void)termtype; (void)termtype;
#elif defined(__linux__) #elif defined(__linux__)
ti->linux_fb_fd = -1; ti->linux_fb_fd = -1;
ti->linux_fbuffer = MAP_FAILED; ti->linux_fbuffer = MAP_FAILED;
// we might or might not program quadrants into the console font // we might or might not program quadrants into the console font
if(is_linux_console(ti->ttyfd)){ if(is_linux_console(ti->ttyfd)){
qterm = TERMINAL_LINUX; ti->qterm = TERMINAL_LINUX;
} }
#endif #endif
#ifndef __MINGW64__ #ifndef __MINGW64__
@ -716,7 +700,7 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
} }
// if we already know our terminal (e.g. on the linux console), there's no // if we already know our terminal (e.g. on the linux console), there's no
// need to send the identification queries. the controls are sufficient. // need to send the identification queries. the controls are sufficient.
bool minimal = (qterm != TERMINAL_UNKNOWN); bool minimal = (ti->qterm != TERMINAL_UNKNOWN);
if(send_initial_queries(ti->ttyfd, minimal)){ if(send_initial_queries(ti->ttyfd, minimal)){
return -1; return -1;
} }
@ -766,8 +750,7 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
{ ESCAPE_CUU, "cuu", }, { ESCAPE_CUU, "cuu", },
{ ESCAPE_CUF, "cuf", }, { ESCAPE_CUF, "cuf", },
{ ESCAPE_CUB, "cub", }, { ESCAPE_CUB, "cub", },
{ ESCAPE_INITC, "initc", }, { ESCAPE_U7, "u7", },
{ ESCAPE_GETM, "getm", },
{ ESCAPE_SMKX, "smkx", }, { ESCAPE_SMKX, "smkx", },
{ ESCAPE_SMXX, "smxx", }, { ESCAPE_SMXX, "smxx", },
{ ESCAPE_RMXX, "rmxx", }, { ESCAPE_RMXX, "rmxx", },
@ -778,10 +761,9 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
{ ESCAPE_IND, "ind", }, { ESCAPE_IND, "ind", },
{ ESCAPE_INDN, "indn", }, { ESCAPE_INDN, "indn", },
{ ESCAPE_CLEAR, "clear", }, { ESCAPE_CLEAR, "clear", },
{ ESCAPE_CSR, "csr", },
{ ESCAPE_OC, "oc", }, { ESCAPE_OC, "oc", },
{ ESCAPE_RMKX, "rmkx", }, { ESCAPE_RMKX, "rmkx", },
{ ESCAPE_U7, "u7", }, { ESCAPE_INITC, "initc", },
{ ESCAPE_MAX, NULL, }, { ESCAPE_MAX, NULL, },
}; };
for(typeof(*strtdescs)* strtdesc = strtdescs ; strtdesc->esc < ESCAPE_MAX ; ++strtdesc){ for(typeof(*strtdescs)* strtdesc = strtdescs ; strtdesc->esc < ESCAPE_MAX ; ++strtdesc){
@ -858,7 +840,7 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
} }
*cursor_x = *cursor_y = -1; *cursor_x = *cursor_y = -1;
unsigned kittygraphs = 0; unsigned kittygraphs = 0;
if(ncinputlayer_init(ti, stdin, &qterm, &appsync_advertised, if(ncinputlayer_init(ti, stdin, &ti->qterm, &appsync_advertised,
cursor_y, cursor_x, stats, &kittygraphs)){ cursor_y, cursor_x, stats, &kittygraphs)){
goto err; goto err;
} }
@ -881,13 +863,13 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
} }
} }
bool invertsixel = false; bool invertsixel = false;
if(apply_term_heuristics(ti, tname, qterm, &tablelen, &tableused, if(apply_term_heuristics(ti, tname, ti->qterm, &tablelen, &tableused,
&invertsixel, nonewfonts)){ &invertsixel, nonewfonts)){
ncinputlayer_stop(&ti->input); ncinputlayer_stop(&ti->input);
goto err; goto err;
} }
build_supported_styles(ti); build_supported_styles(ti);
if(ti->pixel_draw == NULL){ if(ti->pixel_draw == NULL && ti->pixel_draw_late == NULL){
if(kittygraphs){ if(kittygraphs){
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_SELFREF); setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_SELFREF);
} }

@ -7,6 +7,8 @@ extern "C" {
// internal header, not installed // internal header, not installed
#include "version.h"
#include "builddef.h"
#include "input.h" #include "input.h"
#include <stdint.h> #include <stdint.h>
#include <pthread.h> #include <pthread.h>
@ -66,9 +68,7 @@ typedef enum {
ESCAPE_RC, // "rc" pop the cursor off the stack ESCAPE_RC, // "rc" pop the cursor off the stack
ESCAPE_CLEAR, // "clear" clear screen and home cursor ESCAPE_CLEAR, // "clear" clear screen and home cursor
ESCAPE_INITC, // "initc" set up palette entry ESCAPE_INITC, // "initc" set up palette entry
ESCAPE_GETM, // "getm" get mouse events
ESCAPE_U7, // "u7" cursor position report ESCAPE_U7, // "u7" cursor position report
ESCAPE_CSR, // "csr" change scroll region
// Application synchronized updates, not present in terminfo // Application synchronized updates, not present in terminfo
// (https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec) // (https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec)
ESCAPE_BSUM, // Begin Synchronized Update Mode ESCAPE_BSUM, // Begin Synchronized Update Mode
@ -159,6 +159,7 @@ typedef struct tinfo {
int (*pixel_init)(const struct tinfo*, int fd); // called when support is detected int (*pixel_init)(const struct tinfo*, int fd); // called when support is detected
int (*pixel_draw)(const struct tinfo*, const struct ncpile* p, int (*pixel_draw)(const struct tinfo*, const struct ncpile* p,
struct sprixel* s, fbuf* f, int y, int x); struct sprixel* s, fbuf* f, int y, int x);
int (*pixel_draw_late)(const struct tinfo*, struct sprixel* s, int y, int x);
// execute move (erase old graphic, place at new location) if non-NULL // execute move (erase old graphic, place at new location) if non-NULL
int (*pixel_move)(struct sprixel* s, fbuf* f, unsigned noscroll); int (*pixel_move)(struct sprixel* s, fbuf* f, unsigned noscroll);
int (*pixel_scrub)(const struct ncpile* p, struct sprixel* s); int (*pixel_scrub)(const struct ncpile* p, struct sprixel* s);
@ -186,6 +187,7 @@ typedef struct tinfo {
int sprixel_scale_height; // sprixel must be a multiple of this many rows int sprixel_scale_height; // sprixel must be a multiple of this many rows
const char* termname; // terminal name from environment variables/init const char* termname; // terminal name from environment variables/init
char* termversion; // terminal version (freeform) from query responses char* termversion; // terminal version (freeform) from query responses
queried_terminals_e qterm;// detected terminal class
#ifndef __MINGW64__ #ifndef __MINGW64__
struct termios tpreserved;// terminal state upon entry struct termios tpreserved;// terminal state upon entry
#endif #endif
@ -206,6 +208,7 @@ typedef struct tinfo {
bool detected_cursor_inversion; // have we performed inversion testing? bool detected_cursor_inversion; // have we performed inversion testing?
bool inverted_cursor; // does the terminal return inverted coordinates? bool inverted_cursor; // does the terminal return inverted coordinates?
bool bce; // is the bce property advertised? bool bce; // is the bce property advertised?
bool in_alt_screen; // are we in the alternate screen?
} tinfo; } tinfo;
// retrieve the terminfo(5)-style escape 'e' from tdesc (NULL if undefined). // retrieve the terminfo(5)-style escape 'e' from tdesc (NULL if undefined).
@ -276,6 +279,92 @@ grow_esc_table(tinfo* ti, const char* tstr, escape_e esc,
return 0; return 0;
} }
static inline int
ncfputs(const char* ext, FILE* out){
int r;
#ifdef __USE_GNU
r = fputs_unlocked(ext, out);
#else
r = fputs(ext, out);
#endif
return r;
}
static inline int
ncfputc(char c, FILE* out){
#ifdef __USE_GNU
return putc_unlocked(c, out);
#else
return putc(c, out);
#endif
}
// reliably flush a FILE*...except you can't, so far as i can tell. at least
// on glibc, a single fflush() error latches the FILE* error, but ceases to
// perform any work (even following a clearerr()), despite returning 0 from
// that point on. thus, after a fflush() error, even on EAGAIN and friends,
// you can't use the stream any further. doesn't this make fflush() pretty
// much useless? it sure would seem to, which is why we use an fbuf for
// all our important I/O, which we then blit with blocking_write(). if you
// care about your data, you'll do the same.
static inline int
ncflush(FILE* out){
if(ferror(out)){
logerror("Not attempting a flush following error\n");
}
if(fflush(out) == EOF){
logerror("Unrecoverable error flushing io (%s)\n", strerror(errno));
return -1;
}
return 0;
}
static inline int
term_emit(const char* seq, FILE* out, bool flush){
if(!seq){
return -1;
}
if(ncfputs(seq, out) == EOF){
logerror("Error emitting %zub escape (%s)\n", strlen(seq), strerror(errno));
return -1;
}
return flush ? ncflush(out) : 0;
}
static inline int
enter_alternate_screen(FILE* fp, tinfo* ti, bool flush){
if(ti->in_alt_screen){
return 0;
}
const char* smcup = get_escape(ti, ESCAPE_SMCUP);
if(smcup == NULL){
logerror("alternate screen is unavailable");
return -1;
}
if(term_emit(smcup, fp, flush)){
return -1;
}
ti->in_alt_screen = true;
return 0;
}
static inline int
leave_alternate_screen(FILE* fp, tinfo* ti){
if(!ti->in_alt_screen){
return 0;
}
const char* rmcup = get_escape(ti, ESCAPE_RMCUP);
if(rmcup == NULL){
logerror("can't leave alternate screen");
return -1;
}
if(term_emit(rmcup, fp, true)){
return -1;
}
ti->in_alt_screen = false;
return 0;
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

@ -44,7 +44,7 @@ char* notcurses_hostname(void){
} }
#else // windows #else // windows
char lp[MAX_COMPUTERNAME_LENGTH + 1]; char lp[MAX_COMPUTERNAME_LENGTH + 1];
size_t s = sizeof(lp); DWORD s = sizeof(lp);
if(GetComputerNameA(lp, &s)){ if(GetComputerNameA(lp, &s)){
return strdup(lp); return strdup(lp);
} }

@ -42,9 +42,21 @@ struct job {
std::string p; std::string p;
}; };
static inline char
path_separator(void){
#ifdef __MINGW64__
return '\\';
#else
return '/';
#endif
}
// FIXME see above; we ought have std::filesystem // FIXME see above; we ought have std::filesystem
auto path_join(const std::string& dir, const std::string& p) -> std::string { auto path_join(const std::string& dir, const std::string& p) -> std::string {
return dir + p; if(dir.empty()){
return p;
}
return dir + path_separator() + p;
} }
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

@ -11,8 +11,8 @@
#include <libswscale/version.h> #include <libswscale/version.h>
#include <libavformat/version.h> #include <libavformat/version.h>
#include <libavformat/avformat.h> #include <libavformat/avformat.h>
#include "visual-details.h" #include "lib/visual-details.h"
#include "internal.h" #include "lib/internal.h"
struct AVFormatContext; struct AVFormatContext;
struct AVCodecContext; struct AVCodecContext;

@ -1,7 +1,7 @@
#include "builddef.h" #include "builddef.h"
#ifndef USE_OIIO #ifndef USE_OIIO
#ifndef USE_FFMPEG #ifndef USE_FFMPEG
#include "internal.h" #include "lib/internal.h"
static void static void
printbanner(fbuf* f){ printbanner(fbuf* f){

@ -1,7 +1,7 @@
#include "builddef.h" #include "builddef.h"
#ifdef USE_OIIO #ifdef USE_OIIO
#include "visual-details.h" #include "lib/visual-details.h"
#include "internal.h" #include "lib/internal.h"
#include "oiio.h" #include "oiio.h"
int oiio_blit_dispatch(struct ncplane* nc, const struct blitset* bset, int oiio_blit_dispatch(struct ncplane* nc, const struct blitset* bset,

@ -5,7 +5,7 @@
#include <OpenImageIO/imageio.h> #include <OpenImageIO/imageio.h>
#include <OpenImageIO/imagebuf.h> #include <OpenImageIO/imagebuf.h>
#include <OpenImageIO/imagebufalgo.h> #include <OpenImageIO/imagebufalgo.h>
#include "visual-details.h" #include "lib/visual-details.h"
#include "oiio.h" #include "oiio.h"
typedef struct ncvisual_details { typedef struct ncvisual_details {

@ -5,7 +5,7 @@
extern "C" { extern "C" {
#endif #endif
#include "internal.h" #include "lib/internal.h"
int oiio_decode(ncvisual* nc); int oiio_decode(ncvisual* nc);
struct ncvisual_details* oiio_details_init(void); struct ncvisual_details* oiio_details_init(void);

@ -1,5 +1,5 @@
#include "notcurses/direct.h" #include "notcurses/direct.h"
#include "internal.h" #include "lib/internal.h"
extern const ncvisual_implementation local_visual_implementation; extern const ncvisual_implementation local_visual_implementation;

@ -27,7 +27,7 @@ void usage(std::ostream& o, const char* name, int exitcode){
o << " -k: use direct mode (cannot be used with -L or -d)\n"; o << " -k: use direct mode (cannot be used with -L or -d)\n";
o << " -L: loop frames\n"; o << " -L: loop frames\n";
o << " -t seconds: delay t seconds after each file\n"; o << " -t seconds: delay t seconds after each file\n";
o << " -l loglevel: integer between 0 and 7, goes to stderr'\n"; o << " -l loglevel: integer between 0 and 7, goes to stderr\n";
o << " -s scaling: one of 'none', 'hires', 'scale', 'scalehi', or 'stretch'\n"; o << " -s scaling: one of 'none', 'hires', 'scale', 'scalehi', or 'stretch'\n";
o << " -b blitter: one of 'ascii', 'half', 'quad', 'sex', 'braille', or 'pixel'\n"; o << " -b blitter: one of 'ascii', 'half', 'quad', 'sex', 'braille', or 'pixel'\n";
o << " -m margins: margin, or 4 comma-separated margins\n"; o << " -m margins: margin, or 4 comma-separated margins\n";

@ -69,10 +69,7 @@ int main(void){
if(nc == NULL){ if(nc == NULL){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if(notcurses_mouse_enable(nc)){ notcurses_mouse_enable(nc);
notcurses_stop(nc);
return EXIT_FAILURE;
}
ncmultiselector_options sopts; ncmultiselector_options sopts;
memset(&sopts, 0, sizeof(sopts)); memset(&sopts, 0, sizeof(sopts));
sopts.maxdisplay = 10; sopts.maxdisplay = 10;

@ -59,10 +59,7 @@ int main(void){
if(nc == NULL){ if(nc == NULL){
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if(notcurses_mouse_enable(nc)){ notcurses_mouse_enable(nc);
notcurses_stop(nc);
return EXIT_FAILURE;
}
ncselector_options sopts; ncselector_options sopts;
memset(&sopts, 0, sizeof(sopts)); memset(&sopts, 0, sizeof(sopts));
sopts.maxdisplay = 4; sopts.maxdisplay = 4;

@ -1,5 +1,5 @@
#include "main.h" #include "main.h"
#include "visual-details.h" #include "lib/visual-details.h"
#include <vector> #include <vector>
TEST_CASE("Bitmaps") { TEST_CASE("Bitmaps") {
@ -16,8 +16,10 @@ TEST_CASE("Bitmaps") {
SUBCASE("SprixelTermValues") { SUBCASE("SprixelTermValues") {
CHECK(0 < nc_->tcache.cellpixy); CHECK(0 < nc_->tcache.cellpixy);
CHECK(0 < nc_->tcache.cellpixx); CHECK(0 < nc_->tcache.cellpixx);
if(!nc_->tcache.pixel_draw_late){
CHECK(nc_->tcache.pixel_draw); CHECK(nc_->tcache.pixel_draw);
} }
}
SUBCASE("SprixelMinimize") { SUBCASE("SprixelMinimize") {
auto y = 10; auto y = 10;
@ -663,5 +665,55 @@ TEST_CASE("Bitmaps") {
} }
#endif #endif
SUBCASE("BitmapMoveOffscreenDown") {
// first, assemble a visual equivalent to 2x2 cells
auto y = nc_->tcache.cellpixy * 2;
auto x = nc_->tcache.cellpixx * 2;
std::vector<uint32_t> v(x * y * 4, htole(0xffccccff));
auto ncv = ncvisual_from_rgba(v.data(), y, sizeof(decltype(v)::value_type) * x, x);
REQUIRE(nullptr != ncv);
struct ncvisual_options vopts = {
.n = nullptr,
.scaling = NCSCALE_NONE,
.y = 0, .x = 0,
.begy = 0, .begx = 0,
.leny = 0, .lenx = 0,
.blitter = NCBLIT_PIXEL,
.flags = NCVISUAL_OPTION_NODEGRADE,
.transcolor = 0,
};
auto n = ncvisual_render(nc_, ncv, &vopts);
for(int i = 0 ; i <= ncplane_dim_y(n_) ; ++i){
CHECK(0 == ncplane_move_yx(n, i, 0));
CHECK(0 == notcurses_render(nc_));
}
REQUIRE(nullptr != n);
}
SUBCASE("BitmapMoveOffscreenUp") {
// first, assemble a visual equivalent to 2x2 cells
auto y = nc_->tcache.cellpixy * 2;
auto x = nc_->tcache.cellpixx * 2;
std::vector<uint32_t> v(x * y * 4, htole(0xffccccff));
auto ncv = ncvisual_from_rgba(v.data(), y, sizeof(decltype(v)::value_type) * x, x);
REQUIRE(nullptr != ncv);
struct ncvisual_options vopts = {
.n = nullptr,
.scaling = NCSCALE_NONE,
.y = ncplane_dim_y(n_) - 3, .x = 0,
.begy = 0, .begx = 0,
.leny = 0, .lenx = 0,
.blitter = NCBLIT_PIXEL,
.flags = NCVISUAL_OPTION_NODEGRADE,
.transcolor = 0,
};
auto n = ncvisual_render(nc_, ncv, &vopts);
for(int i = ncplane_dim_y(n_) - 3 ; i >= 0 ; --i){
CHECK(0 == ncplane_move_yx(n, i, 0));
CHECK(0 == notcurses_render(nc_));
}
REQUIRE(nullptr != n);
}
CHECK(!notcurses_stop(nc_)); CHECK(!notcurses_stop(nc_));
} }

@ -1,5 +1,5 @@
#include "main.h" #include "main.h"
#include "egcpool.h" #include "lib/egcpool.h"
TEST_CASE("Cell") { TEST_CASE("Cell") {
auto nc_ = testing_notcurses(); auto nc_ = testing_notcurses();

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save