libnotcurses-sys is a low-level Rust wrapper for the notcurses C library

Overview

Crate API MSRV: 1.48.0

libnotcurses-sys is a low-level Rust wrapper for the notcurses C library

This library is built with several layers of zero-overhead abstractions over the C functions and pointers accessed through FFI.

How to use this library

There are basically two ways: The Rust way, and the C way. (Or a mix of both).

1. The Rust way

Where you use the more safely wrapped types, with its methods and constructors, and painless error handling, like this:

use libnotcurses_sys::*;

fn main() -> NcResult<()> {
    let mut nc = Nc::with_flags(NCOPTION_NO_ALTERNATE_SCREEN)?;
    let plane = nc.stdplane();
    plane.putstr("hello world")?;
    nc.render()?;
    nc.stop()?;
    Ok(())
}

Although you still have to manually call the stop() method for Nc and NcDirect objects, and the destroy() method for the rest of types that allocate, (like NcPlane, NcMenu…) at the end of their scope, since the Drop trait is not implemented for any wrapping type in libnotcurses-sys.

But they do implement methods and use NcResult as the return type, for handling errors in the way we are used to in Rust.

For the types that don't allocate, most are based on primitives like i32, u32, u64… without a name in the C library. In Rust they are type aliased (e.g.: NcChannel, NcChannelPair, NcRgb, NcColor…), to leverage type checking, and they implement methods through traits (e.g. NcChannelMethods must be in scope to use the NcChannel methods.

2. The C way

You can always use the C API functions directly if you prefer, in a very similar way as the C library is used.

It requires the use of unsafe, since most functions are wrapped directly by bindgen marked as such.

Error handling is done this way by checking the returned NcIntResult, or in case of receiving a pointer, by comparing it to null_mut().

Example

use core::ptr::{null, null_mut};
use std::process::exit;

use libnotcurses_sys::*;

fn main() {
    let options = ffi::notcurses_options {
        termtype: null(),
        loglevel: 0,
        margin_t: 0,
        margin_r: 0,
        margin_b: 0,
        margin_l: 0,
        flags: NCOPTION_NO_ALTERNATE_SCREEN,
    };
    unsafe {
        let nc = notcurses_init(&options, null_mut());
        if nc == null_mut() {
            exit(1);
        }
        let plane = notcurses_stdplane(nc);
        let cols = ncplane_putstr(&mut *plane, "hello world");
        if cols < NCRESULT_OK {
            notcurses_stop(nc);
            exit(cols.abs());
        }
        if notcurses_stop(nc) < NCRESULT_OK {
            exit(2);
        }
    }
}

The notcurses C API docs

Comments
  • Rust documentation fails to build in docs.rs

    Rust documentation fails to build in docs.rs

    Status: https://docs.rs/crate/libnotcurses-sys/

    The build log shows that it can't compile libnotcurses-sys, since it expects it to be already installed, and it isn't.

    If the notcurses package were already in Ubuntu 20.04 (the version used by the Build environment for Rust crates, then we could open an issue asking to include it.

    More info:

    Until then maybe we could also modify build.rs to download & compile notcurses manually, like in this example: build.rs & Cargo.toml

    bug enhancement 
    opened by joseluis 31
  • Implement selector widget and add poc

    Implement selector widget and add poc

    Also:

    • Move poc/example to poc/src/bin so that each file compiles by default.
    • Fix the poc/menu description reporting: there was a mistake in string convention

    Some quickstart could be added to the README like:

    git clone https://github.com/dankamongmen/libnotcurses-sys; cd libnotcurses-sys
    cd example/poc
    cargo build
    

    notcurses_poc_screenshot

    opened by tinmarino 15
  • Escape key not detected the first time

    Escape key not detected the first time

    On fetching input, Escape key event doesn't seem to be detected the first time. It takes two key-presses to detect Escape key for the first time after which it is detected at each key press.

    Reproducible example -

    use libnotcurses_sys::{widgets::*, *};
    use env_logger;
    use log::info;
    use nix::poll::{ poll, PollFd, PollFlags };
    
    fn main() -> NcResult<()> {
        env_logger::init();
        reader()
    }
    
    fn reader() -> NcResult<()> {
        let mut nc = unsafe { Nc::new()? };
        let mut stp = unsafe { nc.stdplane() };
        
        let input_fd = PollFd::new(
            unsafe { c_api::notcurses_inputready_fd(nc as *mut Nc) },
            PollFlags::POLLIN);
    
        let mut input_details = NcInput::new_empty();
        loop {
            if let Ok(_) = poll(&mut [input_fd], -1) {
                let recorded_input = nc.get_nblock(Some(&mut input_details))?;
                info!("Recieved {:?} {:?}", recorded_input, input_details);
            }
        }
    
        unsafe { nc.stop()? };
        Ok(())
    }
    

    Viewing the logs RUST_LOG=trace cargo run would reveal that each other keyboard input (characters, Tab etc) is detected on one key-press, only Escape key takes two.

    The behavior is the same if I don't use polling and simply loop delayed get(). My Escape key appears to be registering perfectly for other applications so I doubt it is the issue.

    Terminals - Alacritty, XTerm.

    Please confirm this if this behavior is reproducible and is a bug.

    opened by CyanideForBreakfast 6
  • Unable to debug `NcPlane::gradient`

    Unable to debug `NcPlane::gradient`

    I've been meaning to draw empty cells across a line, yet am unable to debug why gradient function is throwing error. Here's a simple reproducible example.

    use libnotcurses_sys::*;
    
    fn main() -> NcResult<()> {
        let nc = unsafe { Nc::with_flags(NcOptions::SUPPRESS_BANNERS)? };
        let splane = unsafe { nc.stdplane() };
    
        let header_bg_channel = NcChannel::from_rgb8(
                57,
                100,
                200,
            );
        let header_fg_channel = NcChannel::from_rgb8(
                200,
                200, 
                200
            );
    
        let header_combined_channel = NcChannels::combine(header_fg_channel, header_bg_channel);
        splane.gradient(
            Some(0),
            Some(0),
            Some(1),
            None,
            " ",
            0,
            header_combined_channel,
            header_combined_channel,
            header_combined_channel,
            header_combined_channel,
        )?;
        nc.render()?;
        Ok(())
    }
    

    In some rare cases, it does render with garbage values filled in. Am I doing something wrong or making some invalid assumptions?

    OS: Arch Terminals: Xterm, Alacritty, Kitty

    bug 
    opened by CyanideForBreakfast 4
  • Clarify the version situation

    Clarify the version situation

    The situation

    At the moment this library is in a complicated situation in regard to its version number.

    It is currently not following semver, but the version is tied to the one the notcurses C library, as an indication of the API that is supposed to be compatible with. Any breaking changes that this library makes outside of the ones from the notcurses C lib API are not reflected anywhere in the version number. This is not ideal.

    For example, some methods need to be declared unsafe in #12, and this doesn't have anything with the notcurses C lib.

    I found some discusions regarding the problem of using semver for wrappers/binding libs:

    • https://stackoverflow.com/questions/16418183/what-are-the-best-practices-to-do-semver-semantic-versioning-of-a-wrapper-libr
    • https://github.com/semver/semver/issues/352

    Several thoughts:

    • It makes most sense to have a semver version number for the development of this library, independently of any other library.
    • The release of notcurses 3.0 is near. We could upgrade to 3.0 in sync with it and at the same time we adopt semver. The problem is if we keep the rapid development and iteration cycle of the bindings it would mean increasing the major version number very often.
    • Ideally we should be in a major 0 version right now (Major version zero (0.y.z) is for initial development. Anything MAY change at any time. The public API SHOULD NOT be considered stable.) (semver.md)
    • I'd really like to be able to evolve API of the Rust bindings until I feel it's stable enough, (I don't like to rush to 1.0)
    • Maybe we could treat the current version 2.X as development version. Or maybe even upgrade to 3.X and treat that one as the develoment version, signaling the change by bumping. And keep upgrading the version number from then on, detached from the version number increases of the notcurses C lib, as if it where a 0.X version (and indicate that in the docs). I like this option the most.
    • The compatibility with which notcurses C lib specific version should also be clearly indicated in the docs.
    documentation enhancement 
    opened by joseluis 4
  • update for ncvisual_blit() introduction

    update for ncvisual_blit() introduction

    ncvisual_render() has been deprecated, and will be removed for ABI3. ncvisual_blit() has replaced it, with slightly different semantics. this was necessary to respect pile logic. you'll want to reflect this.

    it might also be wise to check out the dankamongmen/3.0.0 branch where all ABI3 work is happening.

    w00t w00t!

    bug 
    opened by dankamongmen 4
  • Cargo build failure on M1 mac

    Cargo build failure on M1 mac

    With a new application (cargo init hello_world), and notcurses = 3.0.1 as a dependency in cargo.toml, and notcurses 3.0.8 installed via homebrew, running cargo build produced the following error:

    Compiling libnotcurses-sys v3.6.0
    error[E0308]: mismatched types
        --> $HOME/.cargo/registry/src/github.com-1ecc6299db9ec823/libnotcurses-sys-3.6.0/src/plane/methods.rs:1610:59
         |
    1610 |             unsafe { c_api::ncpile_render_to_buffer(self, &mut buf, &mut len.try_into().unwrap()) },
         |                                                           ^^^^^^^^ expected `i8`, found `u8`
         |
         = note:    expected raw pointer `*mut *mut i8`
                 found mutable reference `&mut *mut u8`
    
    opened by tpmoney 3
  • doc tests stopped working

    doc tests stopped working

    doc tests worked in the past, but for a while they've been failing.

    I've createda minimal example to investigate the issue:

    In this example, it hangs in the running 1 test line, until a key is pressed, then it fails continues:

    $ cargo test
    
    running 0 tests
    test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
       Doc-tests issue-test
    running 1 test
    test src/lib.rs - (line 13) ... FAILED
    failures:
    ---- src/lib.rs - (line 13) stdout ----
    Test executable failed (terminated by signal).
    failures:
        src/lib.rs - (line 10)
    test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 2.71s
    error: test failed, to rerun pass '--doc'
    

    Running it with strace (via cargo-with, installable by running cargo install cargo-with ) like this:

    $ cargo with "strace -s1000" -- test
    

    produces the following output strace.txt

    bug 
    opened by joseluis 3
  • make sure the features are on par & publish the first release

    make sure the features are on par & publish the first release

    Before publishing the first release we should make sure the features are on par with a given version on the original library:

    pending changes against v2.4.2:

    • [x] ncsubproc
    • [x] tabbed widget
    • [x] ncdirect_stream
    • [x] ncdirect_styles
    • [x] ncdirect_supported_styles
    • [x] ncpile_render_to_buffer
    • [x] ncpile_render_to_file
    • [x] nccells_ascii_box
    • [x] nccells_heavy_box
    • [x] nccells_light_box
    • [x] notcurses_hostname
    • [x] notcurses_accountname
    • [x] notcurses_enter_alternate_screen
    • [x] notcurses_leave_alternate_screen
    • [x] ncvisual_subtitle_plane
    • [x] ncvisual_from_palidx
    • [x] ncplane_boundlist
    • [x] ncplane_cursor_move_rel
    • [x] ncplane_erase_region
    • [x] ncplane_scrollup
    • [x] ncplane_scrollup_child
    • [x] ncstrwidth_valid
    • [x] notcurses_getvec
    • [x] NCOPTION_DRAIN_INPUT & NCDIRECT_OPTION_DRAIN_INPUT
    enhancement 
    opened by joseluis 3
  • Lifetime doubt with `NcPlane::new_child`

    Lifetime doubt with `NcPlane::new_child`

    Hi, before I ask, let me tell that I'm myself a beginner in Rust.

    Doubt

    NcPlane::new_child has the following signature -

    pub fn new_child<'parent, 'plane, 'opts>(parent: &'parent mut NcPlane, options: &'opts NcPlaneOptions) ->
    NcResult<&'plane mut NcPlane>
    

    The lifetime 'plane is problematic here, in my opinion, as the compiler has no way to "tie" this to another existing lifetime (like 'opts or 'parent). This leads to assign the reference to the newly created child plane, an unknown lifetime. And because of this is it tricky to use this plane as a field of struct since constraining the reference with the lifetime constraints of the struct is not viable.

    If I recall correctly, all children planes would be destroyed on destruction of parent and hence it makes sense to constraint the child reference to live as long as parent ('parent lifetime), instead of an arbitrary lifetime ('plane).

    So, I want to ask how should I go about it? Because I'm using a child plane as a field in a struct, something along the lines of -

    struct Widget<'a> {
      plane: &'a mut NcPlane
      ... other fields
    }
    

    And assigning Widget { plane : NcPlane::new_child(parent_plane, ...) } just leads to compilation complications as we nest structs and all. Am I doing something wrong?

    opened by CyanideForBreakfast 2
  • [rust] create a didactic example encompassing most of the ncplane functionality

    [rust] create a didactic example encompassing most of the ncplane functionality

    After asking a dumb question in dankamongmen/notcurses#1883 about planes, I'm opening this issue in order to create an practical example that can fix the holes in my understanding.

    opened by joseluis 2
  • set up github & drone workflows

    set up github & drone workflows

    after the split from the original repo we no longer have automatic testing, we should fix that.

    pending configurations:

    • [ ] .drone.yml
    • [ ] .github/workflows/
      • [ ] macos_test.yml
      • [x] ubuntu_test.yml
      • [ ] windows_test.yml

    feature branches

    enhancement 
    opened by joseluis 2
  • Rust build failures on some architectures

    Rust build failures on some architectures

    I've submitted rust-libnotcurses-sys for building on Fedora Rawhide, and am running into a problem with regards to a size_t argument. This type has implementation-defined size. Here's a failed build; they can all be found linked from here.

      --> src/bindings.rs:24:5
       |
    24 |     __va_list_tag,
       |     ^^^^^^^^^^^^^ no `__va_list_tag` in `bindings::ffi`
    error[E0432]: unresolved import `crate::ffi::__va_list_tag`
     --> src/plane/reimplemented.rs:5:5
      |
    5 | use crate::ffi::__va_list_tag;
      |     ^^^^^^^^^^^^^^^^^^^^^^^^^ no `__va_list_tag` in `bindings::ffi`
    error[E0308]: mismatched types
       --> src/notcurses/methods.rs:495:75
        |
    495 |         error![unsafe { crate::notcurses_render_to_buffer(self, &mut buf, &mut len) }]
        |                                                                           ^^^^^^^^ expected `u32`, found `u64`
        |
        = note:    expected raw pointer `*mut u32`
                found mutable reference `&mut u64`
    error[E0308]: mismatched types
       --> src/plane/reimplemented.rs:167:55
        |
    167 |     unsafe { crate::ncplane_putnstr_yx(plane, -1, -1, size, cstring![gclustarr]) }
        |                                                       ^^^^ expected `u32`, found `u64`
        |
    help: you can convert a `u64` to a `u32` and panic if the converted value doesn't fit
        |
    167 |     unsafe { crate::ncplane_putnstr_yx(plane, -1, -1, size.try_into().unwrap(), cstring![gclustarr]) }
        |                                                       ^^^^^^^^^^^^^^^^^^^^^^^^
    error[E0308]: mismatched types
      --> src/signal.rs:15:23
       |
    15 |         Self { __val: [0; 16] }
       |                       ^^^^^^^ expected an array with a fixed size of 32 elements, found one with 16 elements
    
    opened by dankamongmen 65
Releases(v3.7.0)
Owner
nick black
i make computers go fast.
nick black
Zero-cost high-level lua 5.3 wrapper for Rust

td_rlua This library is a high-level binding for Lua 5.3. You don't have access to the Lua stack, all you can do is read/write variables (including ca

null 47 May 4, 2022
High-level Rust bindings to Perl XS API

Perl XS for Rust High-level Rust bindings to Perl XS API. Example xs! { package Array::Sum; sub sum_array(ctx, array: AV) { array.iter().map(|

Vickenty Fesunov 59 Oct 6, 2022
Tyrade: a pure functional language for type-level programming in Rust

A pure functional language for type-level programming in Rust

Will Crichton 286 Jan 1, 2023
High-level memory-safe binding generator for Flutter/Dart <-> Rust

flutter_rust_bridge: High-level memory-safe binding generator for Flutter/Dart <-> Rust Want to combine the best between Flutter, a cross-platform hot

fzyzcjy 2.1k Dec 31, 2022
Facilitating high-level interactions between Wasm modules and JavaScript

wasm-bindgen Facilitating high-level interactions between Wasm modules and JavaScript. Guide | API Docs | Contributing | Chat Built with ?? ?? by The

Rust and WebAssembly 5.9k Jan 8, 2023
Objective-C Runtime bindings and wrapper for Rust.

Objective-C Runtime bindings and wrapper for Rust. Documentation: http://ssheldon.github.io/rust-objc/objc/ Crate: https://crates.io/crates/objc Messa

Steven Sheldon 336 Jan 2, 2023
Javascript wrapper bindings for diamond types

Diamond JS wrapper library This is a javascript / WASM wrapper around diamond types. This code is currently experimental WIP. Do not trust this for an

Seph Gentle 14 Jun 16, 2022
Unstable wrapper API for winapi's Console Functions

maulingmonkey-console-winapi-wrappers Unstable wrapper API for winapi's Console Functions Quickstart # Cargo.toml [dependencies] maulingmonkey-console

null 3 Nov 26, 2021
Inkwell - It's a New Kind of Wrapper for Exposing LLVM (Safely)

Inkwell(s) It's a New Kind of Wrapper for Exposing LLVM (Safely) Inkwell aims to help you pen your own programming languages by safely wrapping llvm-s

Daniel Kolsoi 1.5k Dec 31, 2022
Rust library for build scripts to compile C/C++ code into a Rust library

A library to compile C/C++/assembly into a Rust library/application.

Alex Crichton 1.3k Dec 21, 2022
Rust Attribute-Based Encryption library rabe's C FFI binding , support CP-ABE and KP-ABE encrypt and decrypt, submodule of Rabe.Core c# library.

Rabe-ffi Rust Attribute-Based Encryption library rabe's C FFI binding , support CP-ABE and KP-ABE encrypt and decrypt, submodule of Rabe.Core c# libra

Aya0wind 2 Oct 10, 2022
Rust library to interface with Lua

hlua This library is a high-level binding for Lua 5.2. You don't have access to the Lua stack, all you can do is read/write variables (including callb

Pierre Krieger 488 Dec 26, 2022
A minimalist and safe ECS library for rust!

The full ECS (Entity-Component-System) library. Support an Open Source Developer! ♥️ Composed of two smaller libraries: world_dispatcher: the System p

Joël Lupien 124 Dec 19, 2022
A library for functional programming in Rust

It contains purely functional data structures to supplement the functional programming needs alongside with the Rust Standard Library.

Jason Shin 1.1k Dec 30, 2022
A rust library containing typings and utility functions dealing with the Public specification of the Internet Computer.

IC Types Contributing Please follow the guidelines in the CONTRIBUTING.md document. Goal This library contains typings and utility functions dealing w

DFINITY 5 Nov 28, 2022
Lineiform is a meta-JIT library for Rust interpreters

Lineiform Lineiform is a meta-JIT library for Rust interpreters. Given an interpreter that uses closure generation, it allows an author to add minimal

null 112 Jan 4, 2023
A minimal library for building compiled Node.js add-ons in Rust via Node-API

A minimal library for building compiled Node.js add-ons in Rust via Node-API

Node-API (N-API) for Rust 3.1k Dec 29, 2022
Stack unwinding library in Rust

Unwinding library in Rust and for Rust This library serves two purposes: Provide a pure Rust alternative to libgcc_eh or libunwind. Provide easier unw

Gary Guo 51 Nov 4, 2022
witgen is a library to generate .wit files for WebAssembly in Rust

witgen witgen is a library to help you generate wit definitions in a wit file for WebAssembly. Using this lib in addition to wit-bindgen will help you

Coenen Benjamin 28 Nov 9, 2022