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

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

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

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

@ -35,6 +35,7 @@ jobs:
mingw-w64-ucrt-x86_64-libunistring
mingw-w64-ucrt-x86_64-ncurses
mingw-w64-ucrt-x86_64-toolchain
mingw-w64-ucrt-x86_64-rust
- uses: actions/checkout@v2
@ -85,10 +86,9 @@ jobs:
#notcurses-pydemo > /dev/null
#ncdirect-pydemo > /dev/null
#- name: rust wrappers
#mingw-w64-ucrt-x86_64-rust
#run: |
#cd rust
#rustc --version
- name: rust wrappers
run: |
cd rust
rustc --version
#cargo build
#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
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"
HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
LANGUAGES C CXX)
@ -24,6 +24,7 @@ cmake_dependent_option(
"${BUILD_TESTING}" 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_POC "Build small, uninstalled proof-of-concept binaries" ON)
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}')")
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_CXX_FLAGS_DEBUG " -Og")
if("${USE_COVERAGE}")
@ -61,6 +65,9 @@ if("${USE_COVERAGE}")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
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")
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
elseif(APPLE)
@ -73,34 +80,7 @@ set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
else()
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
endif()
# global compiler flags
add_compile_definitions(_FORTIFY_SOURCE=2)
add_compile_options(-Wall -Wextra -W -Wshadow -Wformat -Wformat-security
-fexceptions -fstrict-aliasing)
message(STATUS "Requested multimedia engine: ${USE_MULTIMEDIA}")
message(STATUS "Requested build mode: ${CMAKE_BUILD_TYPE}")
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
# into ncurses. accept either, and may god have mercy on our souls.
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)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND OpenImageIO)
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})
find_package(doctest 2.3.5)
@ -141,6 +146,16 @@ endif()
find_library(unistring unistring REQUIRED)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND libunistring)
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)
if("${USE_QRCODEGEN}")
check_include_file("qrcodegen/qrcodegen.h" HAVE_QRCODEGEN_H)
@ -187,6 +202,7 @@ set_target_properties(notcurses-core-static PROPERTIES
OUTPUT_NAME notcurses-core
)
target_include_directories(notcurses-core
BEFORE
PRIVATE
include
src
@ -196,6 +212,7 @@ target_include_directories(notcurses-core
"${ZLIB_INCLUDE_DIRS}"
)
target_include_directories(notcurses-core-static
BEFORE
PRIVATE
include
src
@ -210,6 +227,7 @@ target_link_libraries(notcurses-core
"${TERMINFO_LIBRARIES}"
"${LIBM}"
"${unistring}"
"${gpm}"
PUBLIC
Threads::Threads
"${LIBRT_LIBRARIES}"
@ -220,6 +238,7 @@ target_link_libraries(notcurses-core-static
"${TERMINFO_STATIC_LIBRARIES}"
"${LIBM}"
"${unistring}"
"${gpm}"
PUBLIC
Threads::Threads
"${LIBRT_LIBRARIES}"
@ -257,19 +276,19 @@ set_target_properties(notcurses-static PROPERTIES
OUTPUT_NAME notcurses
)
target_include_directories(notcurses
BEFORE
PRIVATE
include
src
src/lib
"${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include"
"${TERMINFO_INCLUDE_DIRS}"
)
target_include_directories(notcurses-static
BEFORE
PRIVATE
include
src
src/lib
"${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include"
"${TERMINFO_INCLUDE_DIRS}"
@ -395,16 +414,19 @@ set_target_properties(
OUTPUT_NAME "notcurses++")
set(NCPP_INCLUDE_DIRS
include
"include"
"src"
"${PROJECT_BINARY_DIR}/include"
"${TERMINFO_INCLUDE_DIRS}"
)
target_include_directories(notcurses++
BEFORE
PRIVATE ${NCPP_INCLUDE_DIRS}
)
target_include_directories(notcurses++-static
BEFORE
PRIVATE ${NCPP_INCLUDE_DIRS}
)
@ -537,7 +559,6 @@ target_compile_definitions(notcurses-info
target_include_directories(notcurses-info
PRIVATE
src
src/lib
include
"${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include"
@ -646,12 +667,12 @@ if(${USE_DOCTEST})
file(GLOB TESTSRCS CONFIGURE_DEPENDS src/tests/*.cpp)
add_executable(notcurses-tester ${TESTSRCS})
target_include_directories(notcurses-tester
BEFORE
PRIVATE
include
src
"${CMAKE_REQUIRED_INCLUDES}"
"${PROJECT_BINARY_DIR}/include"
src/lib
"${TERMINFO_INCLUDE_DIRS}"
)
target_link_libraries(notcurses-tester

@ -12,7 +12,7 @@ prepackaged on many distributions. Otherwise, acquire the current source via
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
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:
`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
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_DOCTEST`: build `notcurses-tester` with Doctest, requires `BUILD_TESTING`
* `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_PANDOC`: build man pages with pandoc
* `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
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,
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.
* Added `ncplane_scrollup()` and `ncplane_scrollup_child()`.
* 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)
* 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
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
[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)
@ -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
* (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+
* (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) 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+

@ -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`.|
| [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. |
| [mintty](https://github.com/mintty/mintty) | ? | ? | ? | ? | ? |
| [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` | |
| rxvt | ✅ | ? |? | | Seems unmaintained; many forks exist. |
@ -118,7 +119,7 @@ Color Erase) capability, but Notcurses never relies on `bce` behavior.
### WezTerm
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

@ -126,9 +126,6 @@ typedef struct notcurses_options {
// the environment variable TERM is used. Failure to open the terminal
// definition will result in failure to initialize Notcurses.
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
// default, nothing is printed to stderr once fullscreen service begins.
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
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
to which graphics are being printed, printing to stderr will corrupt the output.
Setting `loglevel` to a value higher than `NCLOGLEVEL_SILENT` will cause
diagnostics to be printed to `stderr`: you could ensure `stderr` is redirected
if you make use of this functionality.
It's probably wise to export `NCOPTION_NO_ALTERNATE_SCREEN` to the user (e.g. via
command line option or environment variable). Developers and motivated users
might appreciate the ability to manipulate `loglevel` and `renderfp`. The
remaining options are typically of use only to application authors.
It's probably wise to export `NCOPTION_NO_ALTERNATE_SCREEN` to the user (e.g.
via command line option or environment variable). Motivated users might
appreciate the ability to manipulate `loglevel`. The remaining options are
typically of use only to application authors.
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
@ -828,6 +839,9 @@ int ncplane_resize_realign(struct ncplane* n);
struct ncplane* ncplane_parent(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,
// will duplicate all content, and will start with the same rendering state.
struct ncplane* ncplane_dup(struct ncplane* n, void* opaque);

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

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

@ -38,7 +38,7 @@ PROJECT_NAME = Notcurses
# could be handy for archiving the generated documentation or if some version
# 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
# for a project that appears at the top of each page and should give viewer a

@ -33,7 +33,7 @@
</div>
<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>
<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/>
<h3>Executables (section 1)</h3>
<a href="ncls.1.html">ncls</a>—list files, displaying multimedia along with them<br/>

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

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

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

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

@ -1,6 +1,6 @@
% notcurses-demo(1)
% nick black <nickblack@linux.com>
% v2.3.13
% v2.3.17
# NAME
@ -9,7 +9,7 @@ notcurses-demo - Show off some Notcurses features
# SYNOPSIS
**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***
# DESCRIPTION

@ -1,6 +1,6 @@
% notcurses-info(1)
% nick black <nickblack@linux.com>
% v2.3.13
% v2.3.17
# 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
available, the maximum number of color registers and maximum Sixel
geometry are reported. If the iTerm2 protocol or Linux framebuffer graphics
are available, that is reported. If the Kitty graphics protocol is
detected, that will be reported with "rgba graphics are available"; if
Kitty's animation support is also present, that will be reported with
"rgba pixel animation support".
geometry are reported. If Linux framebuffer graphics are available, that is
reported. If the Kitty graphics protocol is detected, that will be reported
with "rgba graphics are available"; if Kitty's animation support is also
present, that will be reported with "rgba pixel animation support".
The final eleven lines, only printed when in a UTF8 locale, show various
Unicode glyphs. The first four lines include the quadrant, sextant, and

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

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

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

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

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

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

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

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

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

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

@ -1,6 +1,6 @@
% notcurses_init(3)
% nick black <nickblack@linux.com>
% v2.3.13
% v2.3.17
# NAME
@ -21,8 +21,8 @@ notcurses_init - initialize a notcurses instance
#define NCOPTION_NO_FONT_CHANGES 0x0080ull
typedef enum {
NCLOGLEVEL_SILENT, // default. print nothing once fullscreen service begins
NCLOGLEVEL_PANIC, // print diagnostics immediately related to crashing
NCLOGLEVEL_SILENT, // print nothing once fullscreen service begins
NCLOGLEVEL_PANIC, // default. print diagnostics before we crash/exit
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_WARNING, // you probably don't want what's happening to happen
@ -34,7 +34,6 @@ typedef enum {
typedef struct notcurses_options {
const char* termtype;
FILE* renderfp;
ncloglevel_e loglevel;
int margin_t, margin_r, margin_b, margin_l;
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
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
disable the cursor, write the update, move the cursor back to this location,
and finally make the cursor visible. **notcurses_cursor_yx** retrieves the

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,6 +1,6 @@
% notcurses_stdplane(3)
% nick black <nickblack@linux.com>
% v2.3.13
% v2.3.17
# 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***);**
**int notcurses_enter_alternate_screen(struct notcurses* ***nc***);**
**int notcurses_leave_alternate_screen(struct notcurses* ***nc***);**
# DESCRIPTION
**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
**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
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.
# SEE ALSO
**notcurses(3)**,
**notcurses_init(3)**,
**notcurses_plane(3)**,
**notcurses_stop(3)**
**notcurses_stop(3)**,
**terminfo(5)**

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

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

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

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

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

@ -10,25 +10,31 @@ extern "C" {
// 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.
#if 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 // Non-Windows, UNIX-common
#ifndef __MINGW64__ // All but Windows
#include <poll.h>
#include <netinet/in.h>
#include <termios.h>
#if defined(__linux__) || defined(__gnu_hurd__) // Linux/Hurd
#endif
#if defined(__linux__) // Linux
#include <byteswap.h>
#define htole(x) (__bswap_32(htonl(x)))
#elif defined(__APPLE__) // macOS
#include <libkern/OSByteOrder.h>
#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>
#define htole(x) (bswap32(htonl(x)))
#endif
#endif
#ifdef __cplusplus
} // extern "C"

@ -898,9 +898,7 @@ typedef struct notcurses_options {
// the environment variable TERM is used. Failure to open the terminal
// definition will result in failure to initialize notcurses.
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;
FILE* renderfp; // deprecated, must be NULL, will be removed for ABI3 FIXME
// Progressively higher log levels result in more logging to stderr. By
// default, nothing is printed to stderr once fullscreen service begins.
ncloglevel_e loglevel;
@ -946,6 +944,18 @@ API ALLOC struct notcurses* notcurses_core_init(const notcurses_options* opts, F
// Destroy a Notcurses context.
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'.
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));
// Get the plane to which the plane 'n' is bound, if any.
API struct ncplane* ncplane_parent(struct ncplane* n);
API const struct ncplane* ncplane_parent_const(const struct ncplane* n);
API struct ncplane* ncplane_parent(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'.
static inline int

@ -68,7 +68,6 @@ Notcurses_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
PyObject *main_fd_object = NULL;
const char *term_type = NULL;
PyObject *render_fd_object = NULL;
int log_level = {0};
const char *margins_str = 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,
&PyLong_Type, &main_fd_object,
&term_type, &PyLong_Type, &render_fd_object, &log_level,
&term_type, &PyLong_Type, &log_level,
&margins_str,
&PyLong_Type, &margin_top, &PyLong_Type, &margin_right, &PyLong_Type, &margin_bottom, &PyLong_Type, &margin_left,
&flags));
@ -91,20 +90,6 @@ Notcurses_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
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;
if (NULL != margins_str)

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

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

@ -67,7 +67,6 @@ use libnotcurses_sys::*;
fn main() {
let options = ffi::notcurses_options {
termtype: null(),
renderfp: null_mut(),
loglevel: 0,
margin_t: 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
fn main() {
let plib = pkg_config::Config::new()
.atleast_version("2.3.13")
.atleast_version("2.3.17")
.probe("notcurses")
.unwrap();

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

@ -68,8 +68,8 @@ impl NcOptions {
) -> Self {
Self {
termtype: null(),
renderfp: null_mut(),
loglevel,
renderfp: null_mut(),
margin_t: margin_t as i32,
margin_r: margin_r 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;
return 0;
}
#define INFINITE 0xffffffff // FIXME
pid_t waitpid(pid_t pid, int* state, int options){
(void)options; // FIXME honor WNOHANG
pid_t waitpid(pid_t pid, int* state, int options){ // FIXME
(void)options;
(void)pid;
(void)state;
/*
WaitForSingleObject(pid, INFINITE);
long unsigned pstate;
GetExitCodeProcess(pid, &pstate);
*state = pstate;
CloseHandle(pid);
return pid;
*/
return 0;
}
#else // not windows
#include <time.h>

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

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

@ -582,6 +582,10 @@ int demo_render(struct notcurses* nc){
clock_gettime(CLOCK_MONOTONIC, &ts);
if(plot){
if(!plot_hidden){
struct ncplane* pixelp = ncplane_boundlist(ncuplot_plane(plot));
if(pixelp){
ncplane_move_top(pixelp);
}
ncplane_move_top(ncuplot_plane(plot));
}
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 (cont.)", .start = 0x12200, },
{ .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 (cont.)", .start = 0x1d600, },
{ .name = "Sutton SignWriting", .start = 0x1d800, },

@ -2,7 +2,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <notcurses/notcurses.h>
#include "internal.h" // internal headers
#include "lib/internal.h" // internal headers
static inline wchar_t
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;
}
@ -291,10 +295,10 @@ unicodedumper(struct ncplane* n, const char* indent){
uint64_t lr = NCCHANNELS_INITIALIZER(0xff, 0xff, 0xff, 0xdB, 0x18, 0x8E);
uint64_t ul = 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_stain(n, y - 2, 79, ul, ur, ll, lr);
ncplane_cursor_move_yx(n, y - 15, 0);
ncplane_stain(n, y - 1, 79, ul, ur, ll, lr);
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");
ncplane_set_styles(n, NCSTYLE_NONE);
}
@ -309,7 +313,6 @@ display_logo(struct ncplane* n, const char* path){
if(ncv == NULL){
return -1;
}
// FIXME ought be exactly 4:1
if(ncvisual_resize(ncv, 3 * cpixy, 24 * cpixx)){
ncvisual_destroy(ncv);
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 & 0x01000000) ? "" : "not ");
finish_line(n);
if(!ti->pixel_draw){
if(!ti->pixel_draw && !ti->pixel_draw_late){
ncplane_printf(n, "%sno bitmap graphics detected", indent);
}else{ // we do have support; draw one
if(ti->color_registers){
@ -348,12 +351,8 @@ tinfo_debug_bitmaps(struct ncplane* n, const tinfo* ti, const char* indent){
}else{
ncplane_printf(n, "%ssixel colorregs: %u", indent, ti->color_registers);
}
#ifdef __linux__
}else if(ti->linux_fb_fd >= 0){
}else if(ti->pixel_draw_late){
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){
ncplane_printf(n, "%srgba pixel graphics support", indent);
}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, "ccc", ti->caps.can_change_colors);
tinfo_debug_cap(n, "rgb", ti->caps.rgb);
tinfo_debug_cap(n, "csr", get_escape(ti, ESCAPE_CSR));
finish_line(n);
ncplane_putstr(n, indent);
tinfo_debug_cap(n, "utf8", ti->caps.utf8);
@ -441,7 +439,13 @@ int main(int argc, const char** argv){
return EXIT_FAILURE;
}
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);
tinfo_debug_caps(stdn, &nc->tcache, indent);
tinfo_debug_styles(nc, stdn, indent);

@ -227,7 +227,7 @@ int input_demo(ncpp::NotCurses* nc) {
popts.minchannels = popts.maxchannels = 0;
ncchannels_set_fg_rgb8(&popts.minchannels, 0x40, 0x50, 0xb0);
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);
if(!plot){
return EXIT_FAILURE;
@ -338,9 +338,7 @@ int main(void){
notcurses_options nopts{};
nopts.flags = NCOPTION_INHIBIT_SETLOCALE;
NotCurses nc(nopts);
if(!nc.mouse_enable()){
return EXIT_FAILURE;
}
nc.mouse_enable(); // might fail if no mouse is available
int ret = input_demo(&nc);
if(!nc.stop() || ret){
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
if(!tcache->pixel_draw && setid == NCBLIT_PIXEL){
if(!tcache->pixel_draw && !tcache->pixel_draw_late && setid == NCBLIT_PIXEL){
if(may_degrade){
setid = NCBLIT_3x2;
}else{

@ -531,6 +531,11 @@ ncdirect_dump_plane(ncdirect* n, const ncplane* np, int xoff){
if(fbuf_finalize(&f, n->ttyfp)){
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;
}
//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){
if(n->tcache.pixel_draw){
if(n->tcache.pixel_draw || n->tcache.pixel_draw_late){
return 1;
}
return 0;
@ -1367,7 +1372,10 @@ int ncdirect_stream(ncdirect* n, const char* filename, ncstreamcb streamer,
if(v->sprite){
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(n->tcache.pixel_remove){
fbuf f = {};

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

@ -388,7 +388,7 @@ ncsubproc* ncsubproc_createvpe(ncplane* n, const ncsubproc_options* opts,
if(ret->pid == 0){
#ifdef __linux__
execvpe(bin, arg, env);
#elif defined(__APPLE__)
#elif defined(__APPLE__) || defined(__gnu_hurd__)
(void)env;
execvp(bin, arg); // FIXME env?
#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
typedef struct esctrie {
uint32_t special; // composed key terminating here
struct esctrie** trie; // if non-NULL, next level of radix-128 trie
uint32_t special; // composed key terminating here
bool shift, ctrl, alt;
} esctrie;
static esctrie*
@ -124,6 +125,9 @@ create_esctrie_node(int special){
if(e){
e->special = special;
e->trie = NULL;
e->shift = 0;
e->ctrl = 0;
e->alt = 0;
}
return e;
}
@ -145,14 +149,12 @@ input_free_esctrie(esctrie** eptr){
}
}
// multiple input escapes might map to the same input
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
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);
logerror("not an escape: %s (0x%x)\n", esc, special);
return -1;
}
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
// an example, see "kend" and "kc1" in st ("simple term" from suckless) :/.
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{
(*cur)->special = special;
(*cur)->shift = shift;
(*cur)->ctrl = ctrl;
(*cur)->alt = alt;
}
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);
}
if(esc && esc->special != NCKEY_INVALID){
ni->shift = esc->shift;
ni->ctrl = esc->ctrl;
ni->alt = esc->alt;
return esc->special;
}
if(csi){
@ -633,7 +641,9 @@ prep_special_keys(ncinputlayer* nc){
static const struct {
const char* tinfo;
uint32_t key;
bool shift, ctrl, alt;
} keys[] = {
{ .tinfo = "kcbt", .key = '\t', .shift = true, },
{ .tinfo = "kcub1", .key = NCKEY_LEFT, },
{ .tinfo = "kcuf1", .key = NCKEY_RIGHT, },
{ .tinfo = "kcuu1", .key = NCKEY_UP, },
@ -720,84 +730,66 @@ prep_special_keys(ncinputlayer* nc){
{ .tinfo = "kext", .key = NCKEY_EXIT, },
{ .tinfo = "kprt", .key = NCKEY_PRINT, },
{ .tinfo = "krfr", .key = NCKEY_REFRESH, },
{ .tinfo = "kDC", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC1", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC2", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC3", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC4", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC5", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC6", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDC7", .key = NCKEY_DEL, }, // FIXME plus modifier
{ .tinfo = "kDN", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN1", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN2", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN3", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN4", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN5", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN6", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kDN7", .key = NCKEY_DOWN, }, // FIXME plus modifier
{ .tinfo = "kEND", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND1", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND2", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND3", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND4", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND5", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND6", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kEND7", .key = NCKEY_END, }, // FIXME plus modifier
{ .tinfo = "kHOM", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM1", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM2", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM3", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM4", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM5", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM6", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kHOM7", .key = NCKEY_HOME, }, // FIXME plus modifier
{ .tinfo = "kIC", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC1", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC2", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC3", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC4", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC5", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC6", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kIC7", .key = NCKEY_INS, }, // FIXME plus modifier
{ .tinfo = "kLFT", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT1", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT2", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT3", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT4", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT5", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT6", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kLFT7", .key = NCKEY_LEFT, }, // FIXME plus modifier
{ .tinfo = "kNXT", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kNXT2", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kNXT3", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kNXT4", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kNXT5", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kNXT6", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kNXT7", .key = NCKEY_PGDOWN, }, // FIXME plus modifier
{ .tinfo = "kPRV", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .tinfo = "kPRV2", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .tinfo = "kPRV3", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .tinfo = "kPRV4", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .tinfo = "kPRV5", .key = NCKEY_PGUP, }, // FIXME plus modifier
{ .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 = "kDC", .key = NCKEY_DEL, .shift = 1, },
{ .tinfo = "kDC3", .key = NCKEY_DEL, .alt = 1, },
{ .tinfo = "kDC4", .key = NCKEY_DEL, .alt = 1, .shift = 1, },
{ .tinfo = "kDC5", .key = NCKEY_DEL, .ctrl = 1, },
{ .tinfo = "kDC6", .key = NCKEY_DEL, .ctrl = 1, .shift = 1, },
{ .tinfo = "kDC7", .key = NCKEY_DEL, .alt = 1, .ctrl = 1, },
{ .tinfo = "kDN", .key = NCKEY_DOWN, .shift = 1, },
{ .tinfo = "kDN3", .key = NCKEY_DOWN, .alt = 1, },
{ .tinfo = "kDN4", .key = NCKEY_DOWN, .alt = 1, .shift = 1, },
{ .tinfo = "kDN5", .key = NCKEY_DOWN, .ctrl = 1, },
{ .tinfo = "kDN6", .key = NCKEY_DOWN, .ctrl = 1, .shift = 1, },
{ .tinfo = "kDN7", .key = NCKEY_DOWN, .alt = 1, .ctrl = 1, },
{ .tinfo = "kEND", .key = NCKEY_END, .shift = 1, },
{ .tinfo = "kEND3", .key = NCKEY_END, .alt = 1, },
{ .tinfo = "kEND4", .key = NCKEY_END, .alt = 1, .shift = 1, },
{ .tinfo = "kEND5", .key = NCKEY_END, .ctrl = 1, },
{ .tinfo = "kEND6", .key = NCKEY_END, .ctrl = 1, .shift = 1, },
{ .tinfo = "kEND7", .key = NCKEY_END, .alt = 1, .ctrl = 1, },
{ .tinfo = "kHOM", .key = NCKEY_HOME, .shift = 1, },
{ .tinfo = "kHOM3", .key = NCKEY_HOME, .alt = 1, },
{ .tinfo = "kHOM4", .key = NCKEY_HOME, .alt = 1, .shift = 1, },
{ .tinfo = "kHOM5", .key = NCKEY_HOME, .ctrl = 1, },
{ .tinfo = "kHOM6", .key = NCKEY_HOME, .ctrl = 1, .shift = 1, },
{ .tinfo = "kHOM7", .key = NCKEY_HOME, .alt = 1, .ctrl = 1, },
{ .tinfo = "kIC", .key = NCKEY_INS, .shift = 1, },
{ .tinfo = "kIC3", .key = NCKEY_INS, .alt = 1, },
{ .tinfo = "kIC4", .key = NCKEY_INS, .alt = 1, .shift = 1, },
{ .tinfo = "kIC5", .key = NCKEY_INS, .ctrl = 1, },
{ .tinfo = "kIC6", .key = NCKEY_INS, .ctrl = 1, .shift = 1, },
{ .tinfo = "kIC7", .key = NCKEY_INS, .alt = 1, .ctrl = 1, },
{ .tinfo = "kLFT", .key = NCKEY_LEFT, .shift = 1, },
{ .tinfo = "kLFT3", .key = NCKEY_LEFT, .alt = 1, },
{ .tinfo = "kLFT4", .key = NCKEY_LEFT, .alt = 1, .shift = 1, },
{ .tinfo = "kLFT5", .key = NCKEY_LEFT, .ctrl = 1, },
{ .tinfo = "kLFT6", .key = NCKEY_LEFT, .ctrl = 1, .shift = 1, },
{ .tinfo = "kLFT7", .key = NCKEY_LEFT, .alt = 1, .ctrl = 1, },
{ .tinfo = "kNXT", .key = NCKEY_PGDOWN, .shift = 1, },
{ .tinfo = "kNXT3", .key = NCKEY_PGDOWN, .alt = 1, },
{ .tinfo = "kNXT4", .key = NCKEY_PGDOWN, .alt = 1, .shift = 1, },
{ .tinfo = "kNXT5", .key = NCKEY_PGDOWN, .ctrl = 1, },
{ .tinfo = "kNXT6", .key = NCKEY_PGDOWN, .ctrl = 1, .shift = 1, },
{ .tinfo = "kNXT7", .key = NCKEY_PGDOWN, .alt = 1, .ctrl = 1, },
{ .tinfo = "kPRV", .key = NCKEY_PGUP, .shift = 1, },
{ .tinfo = "kPRV3", .key = NCKEY_PGUP, .alt = 1, },
{ .tinfo = "kPRV4", .key = NCKEY_PGUP, .alt = 1, .shift = 1, },
{ .tinfo = "kPRV5", .key = NCKEY_PGUP, .ctrl = 1, },
{ .tinfo = "kPRV6", .key = NCKEY_PGUP, .ctrl = 1, .shift = 1, },
{ .tinfo = "kPRV7", .key = NCKEY_PGUP, .alt = 1, .ctrl = 1, },
{ .tinfo = "kRIT", .key = NCKEY_RIGHT, .shift = 1, },
{ .tinfo = "kRIT3", .key = NCKEY_RIGHT, .alt = 1, },
{ .tinfo = "kRIT4", .key = NCKEY_RIGHT, .alt = 1, .shift = 1, },
{ .tinfo = "kRIT5", .key = NCKEY_RIGHT, .ctrl = 1, },
{ .tinfo = "kRIT6", .key = NCKEY_RIGHT, .ctrl = 1, .shift = 1, },
{ .tinfo = "kRIT7", .key = NCKEY_RIGHT, .alt = 1, .ctrl = 1, },
{ .tinfo = "kUP", .key = NCKEY_UP, .shift = 1, },
{ .tinfo = "kUP3", .key = NCKEY_UP, .alt = 1, },
{ .tinfo = "kUP4", .key = NCKEY_UP, .alt = 1, .shift = 1, },
{ .tinfo = "kUP5", .key = NCKEY_UP, .ctrl = 1, },
{ .tinfo = "kUP6", .key = NCKEY_UP, .ctrl = 1, .shift = 1, },
{ .tinfo = "kUP7", .key = NCKEY_UP, .alt = 1, .ctrl = 1, },
{ .tinfo = NULL, .key = NCKEY_INVALID, }
}, *k;
for(k = keys ; k->tinfo ; ++k){
@ -807,15 +799,15 @@ prep_special_keys(ncinputlayer* nc){
continue;
}
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;
}
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;
}
}
if(ncinputlayer_add_input_escape(nc, CSIPREFIX, NCKEY_CSI)){
if(ncinputlayer_add_input_escape(nc, CSIPREFIX, NCKEY_CSI, 0, 0, 0)){
return -1;
}
return 0;
@ -1688,10 +1680,10 @@ void ncinput_extract_clrs(ncinputlayer* ni){
if(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;
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;
if(ni->inputbuf_write_at == sizeof(ni->inputbuf) / sizeof(*ni->inputbuf)){
ni->inputbuf_write_at = 0;

@ -10,6 +10,9 @@ extern "C" {
#include "compat/compat.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 <term.h>
#include <time.h>
@ -29,10 +32,11 @@ extern "C" {
#ifndef __MINGW64__
#include <langinfo.h>
#endif
#include "termdesc.h"
#include "egcpool.h"
#include "sprite.h"
#include "fbuf.h"
#include "lib/termdesc.h"
#include "lib/egcpool.h"
#include "lib/sprite.h"
#include "lib/fbuf.h"
#include "lib/gpm.h"
#define API __attribute__((visibility("default")))
#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
FILE* ttyfp; // FILE* for writing rasterized data
FILE* renderfp; // debugging FILE* to which renderings are written
tinfo tcache; // terminfo cache
pthread_mutex_t pilelock; // guards pile list, locks resize in render
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* 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 summarize_stats(notcurses* nc);
@ -730,9 +713,14 @@ sprite_scrub(const notcurses* n, const ncpile* p, sprixel* s){
static inline int
sprite_draw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
int y, int x){
if(!ti->pixel_draw){
return 0;
}
int offy, offx;
ncplane_yx(s->n, &offy, &offx);
//sprixel_debug(s, stderr);
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
@ -749,6 +737,9 @@ sprite_redraw(const tinfo* ti, const ncpile* p, sprixel* s, fbuf* f,
bool noscroll = !ti->sixel_maxy_pristine;
return ti->pixel_move(s, f, noscroll);
}else{
if(!ti->pixel_draw){
return 0;
}
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);
// 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
term_bg_palindex(const notcurses* nc, fbuf* f, unsigned pal){
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"
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
// mouse protocol (mainly so copy-and-paste can still be performed).
#define XTSHIFTESCAPE "\x1b[>1s"
@ -1195,7 +1157,10 @@ mouse_enable(FILE* out){
}
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 ";"
/*SET_FOCUS_EVENT_MOUSE ";" */SET_SGR_MODE_MOUSE "l");
}
@ -1736,8 +1701,8 @@ typedef struct ncvisual_implementation {
API extern ncvisual_implementation visual_implementation;
static inline char
path_seperator(void){
#if defined _WIN32 || defined __CYGWIN__
path_separator(void){
#ifdef __MINGW64__
return '\\';
#else
return '/';
@ -1753,7 +1718,7 @@ prefix_data(const char* base){
char* path = (char*)malloc(len); // cast for C++ includers
if(path){
memcpy(path, NOTCURSES_SHARE, dlen);
path[dlen] = path_seperator();
path[dlen] = path_separator();
strcpy(path + dlen + 1, base);
}
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){
(void)p;
(void)s;
return 0;
//return sixel_scrub(p, s);
return sixel_scrub(p, s);
}
#ifdef __linux__
@ -172,9 +169,7 @@ int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
return 1;
}
int fbcon_draw(const tinfo* ti, const struct ncpile *p, sprixel* s, fbuf* f, int y, int x){
(void)p;
(void)f; // we don't write to the stream
int fbcon_draw(const tinfo* ti, sprixel* s, int y, int x){
int wrote = 0;
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
@ -688,11 +683,9 @@ int fbcon_rebuild(sprixel* s, int ycell, int xcell, uint8_t* auxvec){
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)p;
(void)s;
(void)f;
(void)y;
(void)x;
return 0;

@ -28,6 +28,26 @@ void notcurses_version_components(int* major, int* minor, int* patch, int* 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
// any preexisting styling) and shutdown (to not affect further programs).
int reset_term_attributes(const tinfo* ti, fbuf* f){
@ -70,7 +90,7 @@ notcurses_stop_minimal(void* vnc){
if(nc->tcache.pixel_shutdown){
ret |= nc->tcache.pixel_shutdown(f);
}
ret |= mouse_disable(f);
ret |= mouse_disable(&nc->tcache, f);
ret |= reset_term_attributes(&nc->tcache, f);
if(nc->tcache.ttyfd >= 0){
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
if(rows){
*rows = tcache->default_rows;
}
if(cols){
*cols = tcache->default_cols;
}
/*
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){
*rows = tcache->default_rows;
}
if(cols){
*cols = tcache->default_cols;
}
//}
#endif
if(tcache->sixel_maxy_pristine){
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 ?
14 % nc->tcache.caps.colors : 0x2080e0);
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->lenx, nc->tcache.cellpixx,
nc->stdplane->leny * nc->tcache.cellpixy,
nc->stdplane->lenx * nc->tcache.cellpixx,
(unsigned long)sizeof(struct crender),
nc->tcache.caps.colors);
nc->stdplane->lenx * nc->tcache.cellpixx);
}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,
bprefix(nc->stats.s.fbbytes, 1, prefixbuf, 0),
(unsigned long)sizeof(struct crender),
nc->tcache.caps.colors);
bprefix(nc->stats.s.fbbytes, 1, prefixbuf, 0));
}
const char* 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);
fbuf_putc(f, 'r');
term_fg_rgb8(&nc->tcache, f, 0x60, 0xe0, 0x60);
@ -935,8 +963,10 @@ init_banner(const notcurses* nc, fbuf* f){
fbuf_putc(f, 'b');
term_fg_palindex(nc, f, nc->tcache.caps.colors <= 256 ?
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__
"", // name is contained in __VERSION__
#else
@ -947,7 +977,6 @@ init_banner(const notcurses* nc, fbuf* f){
#endif
#endif
__VERSION__,
sizeof(nccell),
#ifdef __BYTE_ORDER__
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
"LE",
@ -1030,7 +1059,7 @@ recursive_lock_init(pthread_mutex_t *lock){
}
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 0;
@ -1086,7 +1115,6 @@ notcurses* notcurses_core_init(const notcurses_options* opts, FILE* outfp){
reset_stats(&ret->stats.s);
reset_stats(&ret->stashed_stats);
ret->ttyfp = outfp;
ret->renderfp = opts->renderfp;
memset(&ret->rstate, 0, sizeof(ret->rstate));
memset(&ret->palette_damage, 0, sizeof(ret->palette_damage));
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)){
const char* smcup = get_escape(&ret->tcache, ESCAPE_SMCUP);
if(smcup){
if(term_emit(smcup, ret->ttyfp, false)){
if(enter_alternate_screen(ret->ttyfp, &ret->tcache, false)){
free_plane(ret->stdplane);
goto err;
}
@ -2234,7 +2262,7 @@ ncplane* ncplane_above(ncplane* n){
}
int notcurses_mouse_enable(notcurses* n){
if(mouse_enable(n->ttyfp)){
if(mouse_enable(&n->tcache, n->ttyfp)){
return -1;
}
return 0;
@ -2247,15 +2275,13 @@ int notcurses_mouse_disable(notcurses* n){
if(fbuf_init_small(&f)){
return -1;
}
if(mouse_disable(&f)){
if(mouse_disable(&n->tcache, &f)){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, n->ttyfp) != 1 || fflush(n->ttyfp) == EOF){
fbuf_free(&f);
if(fbuf_finalize(&f, n->ttyfp) < 0){
return -1;
}
fbuf_free(&f);
return 0;
}
@ -2393,6 +2419,10 @@ const ncplane* ncplane_parent_const(const ncplane* n){
return n->boundto;
}
ncplane* ncplane_boundlist(ncplane* n){
return n->blist;
}
void ncplane_set_resizecb(ncplane* n, int(*resizecb)(ncplane*)){
if(n == notcurses_stdplane(ncplane_notcurses(n))){
return;

@ -8,6 +8,7 @@
// common elements of type-parameterized plots
typedef struct ncplot {
ncplane* ncp;
ncplane* pixelp; // only used for NCBLIT_PIXEL
/* sloutcount-element circular buffer of samples. the newest one (rightmost)
is at slots[slotstart]; they get older as you go back (and around).
elements. slotcount is max(columns, rangex), less label room. */
@ -43,7 +44,174 @@ typedef struct nc##X##plot { \
ncplot 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){ \
if(ncp->plot.bset->geom == NCBLIT_PIXEL){ \
return redraw_pixelplot_##T(ncp); \
} \
ncplane_erase(ncp->plot.ncp); \
const int scale = ncp->plot.bset->width; \
int dimy, dimx; \
@ -52,8 +220,8 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
/* each transition is worth this much change in value */ \
const size_t states = ncp->plot.bset->height + 1; \
/* 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). */ \
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){ \
@ -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 */ \
for(int x = finalx ; x >= startx ; --x){ \
/* 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]; \
/* load it retaining the same ordering we have in the actual array */ \
for(int i = scale - 1 ; i >= 0 ; --i){ \
@ -140,8 +308,8 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
ncplane_set_channels(ncp->plot.ncp, channels); \
size_t egcidx = 0, sumidx = 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* */ \
times the number of intervals per position plus the starting offset, \
we're going to print *something* */ \
for(int i = 0 ; i < scale ; ++i){ \
sumidx *= states; \
if(intervalbase < gvals[i]){ \
@ -164,10 +332,10 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
/* printf(stderr, "y: %d i(scale): %d gvals[%d]: %ju egcidx: %zu sumidx: %zu interval: %f intervalbase: %ju\n", y, i, i, gvals[i], egcidx, sumidx, interval, intervalbase); */ \
} \
/* if we're not UTF8, we can only arrive here via NCBLIT_1x1 (otherwise \
we would have errored out during construction). even then, however, \
we need handle ASCII differently, since it can't print full block. \
in ASCII mode, sumidx != 0 means swap colors and use space. in all \
modes, sumidx == 0 means don't do shit, since we erased earlier. */ \
we would have errored out during construction). even then, however, \
we need handle ASCII differently, since it can't print full block. \
in ASCII mode, sumidx != 0 means swap colors and use space. in all \
modes, sumidx == 0 means don't do shit, since we erased earlier. */ \
/* if(sumidx)fprintf(stderr, "dimy: %d y: %d x: %d sumidx: %zu egc[%zu]: %lc\n", dimy, y, x, sumidx, sumidx, egc[sumidx]); */ \
if(sumidx){ \
if(notcurses_canutf8(ncplane_notcurses(ncp->plot.ncp))){ \
@ -218,7 +386,7 @@ int redraw_plot_##T(nc##X##plot* ncp){ \
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, \
const T trueminy, const T truemaxy){ \
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 && miny){ \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
if(opts->rangex < 0){ \
logerror("Supplied negative independent range %d\n", opts->rangex); \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
if(maxy < miny){ \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
/* DETECTMAXONLY can't be used without domain detection */ \
if(opts->flags & NCPLOT_OPTION_DETECTMAXONLY && (miny != maxy)){ \
logerror("Supplied DETECTMAXONLY without domain detection"); \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
const notcurses* notc = ncplane_notcurses(n); \
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); \
if(bset == NULL){ \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
int sdimy, sdimx; \
ncplane_dim_yx(n, &sdimy, &sdimx); \
if(sdimx <= 0){ \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
int dimx = sdimx; \
ncpp->plot.title = strdup(opts->title ? opts->title : ""); \
ncpp->plot.rangex = opts->rangex; \
/* if we're sizing the plot based off the plane dimensions, scale it by the \
plot geometry's width for all calculations */ \
const int scaleddim = dimx * bset->width; \
const int scaledprefixlen = PREFIXCOLUMNS * bset->width; \
const int scaleddim = dimx * (bset->geom == NCBLIT_PIXEL ? ncplane_notcurses(n)->tcache.cellpixx : 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){ \
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); \
if(ncpp->slots == NULL){ \
ncplane_destroy(n); \
return false; \
return NULL; \
} \
memset(ncpp->slots, 0, slotsize); \
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.slotx = 0; \
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 the x is within the current window, find the proper slot and update it. \
@ -382,6 +550,7 @@ static void
ncplot_destroy(ncplot* n){
free(n->title);
ncplane_destroy(n->ncp);
ncplane_destroy(n->pixelp);
}
/* 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
ncuplot* ncuplot_create(ncplane* n, const ncplot_options* opts, uint64_t miny, uint64_t maxy){
ncuplot* ret = malloc(sizeof(*ret));
@ -457,10 +640,17 @@ ncuplot* ncuplot_create(ncplane* n, const ncplot_options* opts, uint64_t miny, u
return NULL;
}
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);
return NULL;
}
if(bset->geom == NCBLIT_PIXEL){
if(create_pixelp(&ret->plot, n)){
ncuplot_destroy(ret);
return NULL;
}
}
return ret;
}
@ -499,10 +689,17 @@ ncdplot* ncdplot_create(ncplane* n, const ncplot_options* opts, double miny, dou
return NULL;
}
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);
return NULL;
}
if(bset->geom == NCBLIT_PIXEL){
if(create_pixelp(&ret->plot, n)){
ncdplot_destroy(ret);
return NULL;
}
}
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){
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);
if(s->invalidated == SPRIXEL_MOVED){
if(p != nc->last_pile){
@ -975,19 +975,18 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){
while( (s = *parent) ){
//fprintf(stderr, "YARR HARR HARR SPIRXLE %u STATE %d\n", s->id, s->invalidated);
if(s->invalidated == SPRIXEL_INVALIDATED){
int y, x;
ncplane_yx(s->n, &y, &x);
//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);
//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);
int r = sprite_draw(&nc->tcache, p, s, f, nc->margin_t, nc->margin_l);
if(r < 0){
return -1;
}else if(r > 0){
bytesemitted += r;
nc->rstate.hardcursorpos = true;
}
bytesemitted += r;
nc->rstate.hardcursorpos = true;
}else if(s->invalidated == SPRIXEL_LOADED){
if(nc->tcache.pixel_commit){
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)){
return -1;
}
@ -1013,6 +1012,34 @@ rasterize_sprixels(notcurses* nc, ncpile* p, fbuf* f){
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:
// * render -- build up a flat framebuffer from a set of ncplanes
// * 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;
}
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);
if(nc->renderfp){
fprintf(nc->renderfp, "%s\n", (const char*)nc->rstate.f.buf);
}
if(ret < 0){
return ret;
}
@ -1558,11 +1583,9 @@ int ncdirect_set_bg_rgb(ncdirect* nc, unsigned rgb){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1){
fbuf_free(&f);
if(fbuf_finalize(&f, nc->ttyfp) < 0){
return -1;
}
fbuf_free(&f);
ncchannels_set_bg_rgb(&nc->channels, rgb);
return 0;
}
@ -1583,11 +1606,9 @@ int ncdirect_set_fg_rgb(ncdirect* nc, unsigned rgb){
fbuf_free(&f);
return -1;
}
if(fwrite(f.buf, f.used, 1, nc->ttyfp) != 1){
fbuf_free(&f);
if(fbuf_finalize(&f, nc->ttyfp) < 0){
return -1;
}
fbuf_free(&f);
ncchannels_set_fg_rgb(&nc->channels, rgb);
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_selfref(sprixel* s, int ycell, int xcell);
// 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 sixel_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 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);
@ -178,8 +176,6 @@ int sixel_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
fbuf* f, int y, int x);
int kitty_draw(const tinfo* ti, const struct ncpile *p, sprixel* s,
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 sixel_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 kitty_blit(struct ncplane* nc, int linesize, const void* data,
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 leny, int lenx, const struct blitterargs* bargs);
int kitty_blit_selfref(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs);
int fbcon_blit(struct ncplane* nc, int linesize, const void* data,
int leny, int lenx, const struct blitterargs* bargs);
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 fbcon_scroll(const struct ncpile* p, tinfo* ti, int rows);
typedef enum {

@ -53,6 +53,7 @@ setup_sixel_bitmaps(tinfo* ti, int fd, bool invert80){
ti->pixel_init = sixel_init;
}
ti->pixel_draw = sixel_draw;
ti->pixel_draw_late = NULL;
ti->pixel_scrub = sixel_scrub;
ti->pixel_wipe = sixel_wipe;
ti->pixel_remove = NULL;
@ -66,26 +67,6 @@ setup_sixel_bitmaps(tinfo* ti, int fd, bool invert80){
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
// lacked animation, and must thus redraw the complete image every time it
// 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_remove = kitty_remove;
ti->pixel_draw = kitty_draw;
ti->pixel_draw_late = NULL;
ti->pixel_commit = kitty_commit;
ti->pixel_move = kitty_move;
ti->pixel_scroll = NULL;
@ -109,10 +91,12 @@ setup_kitty_bitmaps(tinfo* ti, int fd, kitty_graphics_e level){
if(level == KITTY_ANIMATION){
ti->pixel_wipe = kitty_wipe_animation;
ti->pixel_rebuild = kitty_rebuild_animation;
ti->sixel_maxy_pristine = 0;
set_pixel_blitter(kitty_blit_animated);
}else{
ti->pixel_wipe = kitty_wipe_selfref;
ti->pixel_rebuild = kitty_rebuild_selfref;
ti->sixel_maxy_pristine = 0;
set_pixel_blitter(kitty_blit_selfref);
}
}
@ -124,7 +108,8 @@ static inline void
setup_fbcon_bitmaps(tinfo* ti, int fd){
ti->pixel_rebuild = fbcon_rebuild;
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_scrub = fbcon_scrub;
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.rgb = true;
setup_iterm_bitmaps(ti, ti->ttyfd);
}else if(qterm == TERMINAL_APPLE){
termname = "Terminal.app";
// 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,
unsigned noaltscreen, unsigned nocbreak, unsigned nonewfonts,
int* cursor_y, int* cursor_x, ncsharedstats* stats){
queried_terminals_e qterm = TERMINAL_UNKNOWN;
ti->qterm = TERMINAL_UNKNOWN;
memset(ti, 0, sizeof(*ti));
// we don't need a controlling tty for everything we do; allow a failure here
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;
const char* tname = NULL;
#ifdef __APPLE__
qterm = macos_early_matches();
ti->qterm = macos_early_matches();
#elif defined(__MINGW64__)
if(prepare_windows_terminal(ti, &tablelen, &tableused)){
return -1;
}
qterm = TERMINAL_MSTERMINAL;
ti->qterm = TERMINAL_MSTERMINAL;
(void)termtype;
#elif defined(__linux__)
ti->linux_fb_fd = -1;
ti->linux_fbuffer = MAP_FAILED;
// we might or might not program quadrants into the console font
if(is_linux_console(ti->ttyfd)){
qterm = TERMINAL_LINUX;
ti->qterm = TERMINAL_LINUX;
}
#endif
#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
// 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)){
return -1;
}
@ -766,8 +750,7 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
{ ESCAPE_CUU, "cuu", },
{ ESCAPE_CUF, "cuf", },
{ ESCAPE_CUB, "cub", },
{ ESCAPE_INITC, "initc", },
{ ESCAPE_GETM, "getm", },
{ ESCAPE_U7, "u7", },
{ ESCAPE_SMKX, "smkx", },
{ ESCAPE_SMXX, "smxx", },
{ ESCAPE_RMXX, "rmxx", },
@ -778,10 +761,9 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
{ ESCAPE_IND, "ind", },
{ ESCAPE_INDN, "indn", },
{ ESCAPE_CLEAR, "clear", },
{ ESCAPE_CSR, "csr", },
{ ESCAPE_OC, "oc", },
{ ESCAPE_RMKX, "rmkx", },
{ ESCAPE_U7, "u7", },
{ ESCAPE_INITC, "initc", },
{ ESCAPE_MAX, NULL, },
};
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;
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)){
goto err;
}
@ -881,13 +863,13 @@ int interrogate_terminfo(tinfo* ti, const char* termtype, FILE* out, unsigned ut
}
}
bool invertsixel = false;
if(apply_term_heuristics(ti, tname, qterm, &tablelen, &tableused,
if(apply_term_heuristics(ti, tname, ti->qterm, &tablelen, &tableused,
&invertsixel, nonewfonts)){
ncinputlayer_stop(&ti->input);
goto err;
}
build_supported_styles(ti);
if(ti->pixel_draw == NULL){
if(ti->pixel_draw == NULL && ti->pixel_draw_late == NULL){
if(kittygraphs){
setup_kitty_bitmaps(ti, ti->ttyfd, KITTY_SELFREF);
}

@ -7,6 +7,8 @@ extern "C" {
// internal header, not installed
#include "version.h"
#include "builddef.h"
#include "input.h"
#include <stdint.h>
#include <pthread.h>
@ -66,9 +68,7 @@ typedef enum {
ESCAPE_RC, // "rc" pop the cursor off the stack
ESCAPE_CLEAR, // "clear" clear screen and home cursor
ESCAPE_INITC, // "initc" set up palette entry
ESCAPE_GETM, // "getm" get mouse events
ESCAPE_U7, // "u7" cursor position report
ESCAPE_CSR, // "csr" change scroll region
// Application synchronized updates, not present in terminfo
// (https://gitlab.com/gnachman/iterm2/-/wikis/synchronized-updates-spec)
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_draw)(const struct tinfo*, const struct ncpile* p,
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
int (*pixel_move)(struct sprixel* s, fbuf* f, unsigned noscroll);
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
const char* termname; // terminal name from environment variables/init
char* termversion; // terminal version (freeform) from query responses
queried_terminals_e qterm;// detected terminal class
#ifndef __MINGW64__
struct termios tpreserved;// terminal state upon entry
#endif
@ -206,6 +208,7 @@ typedef struct tinfo {
bool detected_cursor_inversion; // have we performed inversion testing?
bool inverted_cursor; // does the terminal return inverted coordinates?
bool bce; // is the bce property advertised?
bool in_alt_screen; // are we in the alternate screen?
} tinfo;
// 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;
}
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
}
#endif

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

@ -42,9 +42,21 @@ struct job {
std::string p;
};
static inline char
path_separator(void){
#ifdef __MINGW64__
return '\\';
#else
return '/';
#endif
}
// FIXME see above; we ought have std::filesystem
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;

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

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

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

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

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

@ -1,5 +1,5 @@
#include "notcurses/direct.h"
#include "internal.h"
#include "lib/internal.h"
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 << " -L: loop frames\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 << " -b blitter: one of 'ascii', 'half', 'quad', 'sex', 'braille', or 'pixel'\n";
o << " -m margins: margin, or 4 comma-separated margins\n";

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

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

@ -1,5 +1,5 @@
#include "main.h"
#include "visual-details.h"
#include "lib/visual-details.h"
#include <vector>
TEST_CASE("Bitmaps") {
@ -16,7 +16,9 @@ TEST_CASE("Bitmaps") {
SUBCASE("SprixelTermValues") {
CHECK(0 < nc_->tcache.cellpixy);
CHECK(0 < nc_->tcache.cellpixx);
CHECK(nc_->tcache.pixel_draw);
if(!nc_->tcache.pixel_draw_late){
CHECK(nc_->tcache.pixel_draw);
}
}
SUBCASE("SprixelMinimize") {
@ -663,5 +665,55 @@ TEST_CASE("Bitmaps") {
}
#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_));
}

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

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

Loading…
Cancel
Save