/nim-notcurses

Nim wrapper for Notcurses: blingful TUIs and character graphics

Primary LanguageNimOtherNOASSERTION

nim-notcurses

License: Apache License: MIT API: experimental Builds (GitHub Actions) codecov

Nim wrapper for Notcurses: blingful TUIs and character graphics.

This package provides a Nim API and a lower-level wrapper for Notcurses' raw C API.


🚧 changes in master since v3.0.9 involve a complete overhaul of nim-notcurses and are not yet included in any tagged version.

That overhaul is not complete, but in its current state is still more usable than previous versions. What follows includes forward-looking statements.


Installation

Check requirements, then add notcures to a project's .nimble file

requires "notcurses#head"

🚧 These instructions will be revised once changes in master are included in a tagged version.

choosenim is a convenient way to install the Nim compiler and tools, if you don't have them installed already.

Usage

API

import notcurses

or import its core

import notcurses/core

💡 The core does not link against Notcurses' multimedia stack, as explained in its FAQs.

Initialization

TUI mode is the default and facilitates high-performance, non-scrolling, full-screen applications

let nc = Nc.init

Shutdown

nc.stop

CLI mode

CLI mode supports scrolling output for shell utilities, but with the full power of Notcurses

import notcurses
# or: import notcurses/core

let nc = Nc.init NcOpts.init [InitFlags.CliMode]

nc.stop

Direct mode

Direct mode makes a limited subset of Notcurses available for manipulating typical scrolling or file-backed output

import notcurses/direct
# or: import notcurses/direct/core

let ncd = Ncd.init

ncd.stop

💡 It's generally recommended to use CLI mode instead of Direct mode. See Notcurses' FAQs, and for more context see dankamongmen/notcurses#1853 and dankamongmen/notcurses#1834.

ABI

If you don't fancy the Nim API provided by this package, you can import its wrapper for Notcurses' raw C API and work with that directly, or use it to build an API to your liking

import notcurses/abi
# or: import notcurses/abi/core

var opts = notcurses_options()

let nc = notcurses_init(addr opts, stdout)
if nc.isNil: raise (ref Defect)(msg: "notcurses_init failed")

# or: let nc = notcurses_core_init(addr opts, stdout)
# or: if nc.isNil: raise (ref Defect)(msg: "notcurses_core_init failed")

if notcurses_stop(nc) < 0: raise (ref Defect)(msg: "notcurses_stop failed")

Direct mode

import notcurses/abi/direct
# or: import notcurses/abi/direct/core

let flags = 0'u64

let ncd = ncdirect_init(nil, stdout, flags)
if ncd.isNil: raise (ref Defect)(msg: "ncdirect_init failed")

# or: let ncd = ncdirect_core_init(nil, stdout, flags)
# or: if ncd.isNil: raise (ref Defect)(msg: "ncdirect_core_init failed")

if ncdirect_stop(ncd) < 0: raise (ref Defect)(msg: "ncdirect_stop failed")

Examples

See the modules in examples. To build and run the tui1 example do

$ nim c -r examples/tui1.nim

You can use additional compiler options, cf. requirements

$ nim c --passC:"-I${HOME}/repos/notcurses/include" \
        --passL:"-L${HOME}/repos/notcurses/build" \
        --passL:"-rpath ${HOME}/repos/notcurses/build" \
        -r examples/tui1.nim

To build all the examples do

$ extras/build_examples.sh

Additional options are supported

$ extras/build_examples.sh -d:release --passC ...

Tests

Unit tests for nim-notcurses can be run with nimble

$ nimble test

You can use additional nimble and compiler options

$ nimble --verbose test --passC ...

The test suites are rather thin at present but will be expanded eventually.

Coverage

Coverage data is collected with lcov and uploaded to codecov.

Most of Notcurses' facilities expect a proper terminal. Also, given the nature of the library, visual inspection of program output and interaction with it as it runs are the most practical means to ensure it's functioning as intended. For those reasons coverage runs are peformed locally, i.e. a CI pipeline isn't suitable.

Currently, most of the coverage data is generated by building and running the examples, with unit tests filling in a few gaps

$ extras/coverage.sh

Additional options are supported

$ extras/coverage.sh --passC ...

Upload to codecov is not attempted if environment variable CODECOV_TOKEN is empty or not set.

💡 Quality of coverage data varies with the version of the Nim compiler, the newer the better. Also, owing to Nim's dead code elimination, which can't be disabled, coverage numbers can be misleading: a module may have many unmapped lines and still report 100%. To get a true sense of the state of coverage, look at the reports for modules in notcurses/abi: only lines pertaining to proc and func are generally of interest, and any that are not highlighted are not covered, i.e. those procedures are not being called via modules in notcurses/api.

Requirements

Notcurses needs to be installed with your package manager 📦, or you can compile it from source.

For example, on macOS, you could install it with brew

$ brew install notcurses

BYO Notcurses 🍻

Building Notcurses is simple, but make sure to have its requirements installed.

For example, on Linux or macOS, you could do it like this

$ git clone --depth 1 https://github.com/dankamongmen/notcurses.git \
            --branch v3.0.9 \
            "${HOME}/repos/notcurses"
$ cd "${HOME}/repos/notcurses"
$ mkdir build && cd build
$ cmake .. -DCMAKE_BUILD_TYPE=Release
$ make -j16

On Windows + MSYS2, add -G"MSYS Makefiles" to the cmake command.

💡 make install should not be run after make unless you want to install your own Notcurses build system-wide.

Compiler options 👑

If you build Notcurses yourself and don't install it system-wide, then its headers and libraries will not be in locations known to your compiler and linker. In that case, use --passC and --passL with nim c.

For example, on macOS, if your own build of Notcurses is in ${HOME}/repos/notcurses/build, you would use

$ nim c --passC:"-I${HOME}/repos/notcurses/include" \
        --passL:"-L${HOME}/repos/notcurses/build" \
        --passL:"-rpath ${HOME}/repos/notcurses/build" \
        ...

On Linux and Windows, drop the -rpath option.

💡 Be careful to not have a system-wide installation of Notcurses while attempting to link against your own non-installed build of it, else you may experience mysterious difficulties 😵

Windows

Support for Microsoft Windows is a bit anemic at present because Windows Terminal and Notcurses' support for it are works in progress.

All of the examples can be built and run on Windows + MSYS2, but

  • Programs should be built in an MSYS2 shell and run in Windows Terminal in an MSYS2 shell. They can also be run in Mintty, but rendering problems will be more likely and more severe. It's easy to configure Windows Terminal to launch an MSYS2 shell, see these instructions.
  • DLLs for Notcurses must be in your MSYS2 shell's path at runtime, e.g.
    export PATH="${HOME}/repos/notcurses/build:${PATH}"
  • Windows' system locale needs to be set to UTF-8:
    Language settings -> Administrative language settings -> Change system locale -> Beta: Use Unicode UTF-8 for worldwide language support
  • Expect to encounter rendering problems and for performance to be lackluster.

💡 Programs using nim-notcurses build correctly in GitHub Actions on Windows + MSYS2, but are known to not run successfully in that CI environment if they initialize Notcurses.

Goals

  • Provide a comprehensive wrapper for Notcurses' raw C API, and keep it up-to-date as Notcurses evolves.
  • Offer a higher-level API built atop the lower-level wrapper, allowing Notcurses to be readily leveraged with the language features of Nim.

The high-level API is referred to as a Nim API elsewhere in this document and repository, for want of a better description.

In the future, it may be desirable to split this project into two packages: notcurses and notcurses_abi. The latter would be a dependency of the former. At present, it makes sense to develop the wrappers together as a single package.

Non-goals: provide an extensible widgets library, a text-based windowing system, or other advanced facilities that could be built with nim-notcurses. Such ideas can be explored in projects that have this package as a dependency.

Versioning

nim-notcurses follows the version number of Notcurses

💡 It's recommended to only use versions > 3.0.9 of nim-notcurses because its earlier versions were too raw and unproven.

🚧 At present, that means installing nim-notcurses per the latest commits on its master branch, cf. installation. These instructions will be revised once changes in master are included in a tagged version.

Following v3.0.9 the implementation of nim-notcurses was overhauled, with Notcurses' examples and demo ported to the Nim API and tested to compile and run correctly on various platforms.

Stability

Notcurses' ABI is stable per major version, and this package's low-level wrapper will likewise be stable once changes in master are included in a tagged version, cf. versioning.

The Nim API (high-level wrapper) provided by this package is currently marked as experimental. Until it is marked as stable, it may be subject to breaking changes across patch and minor versions.

License

nim-notcurses is licensed and distributed under either of

at your option. The contents of this repository may not be copied, modified, or distributed except according to those terms.

Dependency license

Notcurses is licensed under the Apache License, Version 2.0: LICENSE-NOTCURSES.