nsys-curses-utils 0.1.1

Rust *curses utilities
Documentation
use std;

use log;
use pancurses;
use easycurses;
use env_logger;
#[cfg(target_os = "linux")]
use ncurses;
#[cfg(target_os = "windows")]
use pdcurses;

use curses_utils::*;

const LOG_LEVEL    : log::LevelFilter = log::LevelFilter::Trace;
const LOG_FILENAME : &str             = "example.log";

fn main() {
  use easycurses::{Color, ColorPair, Input};
  { // log init
    let log_file = Box::new (std::fs::File::create (LOG_FILENAME).unwrap());
    env_logger::Builder::new()
      .filter_level (LOG_LEVEL)
      .parse_default_env()
      .target (env_logger::Target::Pipe (log_file))
      .init();
    println!("logging output to {LOG_FILENAME:?}");
    log::info!("example log begin");
  }

  // init curses
  let mut curses = Curses::default();

  // load color + char data
  let image = {
    let colors = image_color_pair_load_bmp24 ("red.bmp", "example.bmp");
    let chars  = image_ascii_load ("example.txt");
    image_chtype (&colors, &chars)
  };
  // load chtype data
  // /!\ note: this function must be called after curses is initialized
  let image2 = image_color_halftone_load_bmp24 ("halftone.bmp");


  curses.log_info();
  // draw function
  let mut iter : u64 = 0;
  let draw = |curses : &mut Curses, iter : &mut u64| {
    use pancurses::ToChtype;
    let (attrs, colors) = curses.win().attrget();
    log::trace!("window colors: {colors:?}");
    //#[expect(clippy::cast_sign_loss)]   // not on linux
    let clear_color = color_pair (colors as pancurses::chtype);
    curses.clear();
    let bg_ch = match *iter % 4 {
      0 => '|'.to_chtype(),
      1 => '/'.to_chtype(),
      2 => '-'.to_chtype(),
      3 => '\\'.to_chtype(),
      _ => unreachable!()
    };
    pancurses_ok!(curses.win().bkgd (bg_ch | attrs | clear_color));
    curses.set_color_pair (ColorPair::new (Color::Black, Color::White));
    pancurses_ok!(curses.draw_box ('#'.to_chtype(), (5,5), (8,11)));
    let rect_color : pancurses::chtype =
      color_pair_attr (Color::Red, Color::White);
    let fill_color : pancurses::chtype =
      color_pair_attr (Color::White, Color::Green);
    curses.draw_rect ('O'.to_chtype() | rect_color, ' '.to_chtype() | fill_color,
      (10,5), (15,15), 1, 2);
    pancurses_ok!(curses.win().attrset (attrs));
    let (row, col) = curses.print_centered ("Welcome\n  \
      'q' or 'Q' to quit\n  \
      'B' to toggle bold\n  \
      'd' to toggle dim\n  \
      'r' for red background\n  \
      'g' for green background\n  \
      'b' for blue background\n  \
      'c' for cyan background\n  \
      'y' for yellow background\n  \
      'm' for magenta background\n  \
      'k' for black background\n  \
      'w' for white background\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", None, None);

    { // debug
      use pancurses::*;
      curses.move_rc (row + 13, col);
      if !curses.print ("test test test test") {
        log::warn!("print failed! (row, col): {:?}", (row + 13, col));
      }
      let fill_color : chtype = color_pair_attr (Color::White, Color::Blue);
      curses.draw_rect (
        ' '.to_chtype() | fill_color,
        ' '.to_chtype() | fill_color,
        (row + 13, col),
        (row + 24, col + 23), 0,0);
      curses.win().bkgdset (' '.to_chtype() | fill_color);

      let mut i = 14;
      macro_rules! display {
        ($e:expr) => {
          curses.move_rc (row + i, col + 1);
          let result = curses.print (
            format!("{:20} {}", [stringify!($e), ":"].concat(), $e));
          if !result {
            log::warn!("print failed! (row, col): {:?}", (row + i, col + 1));
          }
          i += 1;
        }
      }
      macro_rules! bits {
        ($e:expr) => {
          curses.move_rc (row + i, col + 1);
          let result = curses.print (
            format!("{:20} {:032b}", [stringify!($e), ":"].concat(), $e));
          if !result {
            log::warn!("print failed! (row, col): {:?}", (row + i, col + 1));
          }
          i += 1;
        }
      }
      bits!(0xFF00_FF00u32);
      bits!(attrs);
      bits!(colors);
      bits!(b'a');
      bits!('a'.to_chtype());
      bits!(COLOR_BLUE);
      bits!(ATTRIBUTE_MASK);
      bits!(A_NORMAL);
      bits!(A_BLINK);
      bits!(A_BOLD);
      bits!(A_CHARTEXT);
      bits!(A_COLOR);
      bits!(A_DIM);
      bits!(A_ITALIC);
      //bits!(A_INVIS);
      //bits!(A_LEFTLINE);
      //bits!(A_OVERLINE);
      //bits!(A_REVERSE);
      //bits!(A_RIGHTLINE);
      //bits!(A_STANDOUT);
      //bits!(A_STRIKEOUT);
      //bits!(A_UNDERLINE);
      //bits!(ACS_ULCORNER());
      #[cfg(target_os = "linux")]
      {
        display!(ncurses::LINES());
        display!(ncurses::COLS());
      }
      #[cfg(target_os = "windows")]
      #[allow(static_mut_refs)]
      unsafe {
        display!(pdcurses::LINES);
        display!(pdcurses::COLS);
      }

      assert!(i >= 0); // avoid unused assignment warning

      curses.win().bkgdset (' '.to_chtype() | attrs);
    } // debug

    const SHIFTL : i32 = 7;
    let mut i = -4;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_ULCORNER())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_LLCORNER())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_URCORNER())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_LRCORNER())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_RTEE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_LTEE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_BTEE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_TTEE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_HLINE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_VLINE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_PLUS())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_S1())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_S3())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_S7())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_S9())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_CKBOARD())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_DIAMOND())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_DEGREE())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_BULLET())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_LARROW())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_RARROW())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_DARROW())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_UARROW())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_BOARD())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_LANTERN())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_BLOCK())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_LEQUAL())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_GEQUAL())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_NEQUAL())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_PI())); i += 1;
    curses.move_rc (row + i, col.saturating_sub (SHIFTL));
    pancurses_warn_ok!(curses.win().addch (pancurses::ACS_STERLING())); //i += 1;

    curses.draw_image_chtype ((8, 36), &image);
    curses.draw_image_chtype ((20, 36), &image2);

    pancurses_ok!(curses.draw_border_default());

    #[cfg(target_os = "linux")]
    let (lines, cols) = (ncurses::LINES(), ncurses::COLS());
    #[cfg(target_os = "windows")]
    let (lines, cols) = unsafe { (pdcurses::LINES, pdcurses::COLS) };
    // debug
    let s = "bottom line";
    #[expect(clippy::cast_possible_truncation)]
    #[expect(clippy::cast_possible_wrap)]
    curses.move_rc (lines-1, cols - s.len() as i32 - 1);
    assert!(curses.print (s));
    pancurses_ok!(curses.win().mvaddch (lines-1, cols-2,
      b'F' as pancurses::chtype));
    pancurses_ok!(curses.win()
      .mvchgat (lines-1, cols-1, 1, b'a' as pancurses::chtype, 5));

    assert!(curses.refresh());
    *iter += 1;
  };

  draw (&mut curses, &mut iter);

  // main loop
  while let Some (input) = curses.getch_wait() {
    log::debug!("input: {input:?}");
    let (attrs, _color) = curses.win().attrget();
    match input {
      Input::Character ('B') => {
        if attrs & pancurses::A_BOLD > 0 {
          curses.set_bold (false);
        } else {
          curses.set_bold (true);
        }
      }
      Input::Character ('d') => {
        if attrs & pancurses::A_DIM > 0 {
          curses.win().attroff (pancurses::A_DIM);
        } else {
          curses.win().attron (pancurses::A_DIM);
        }
      }
      Input::Character ('q') |
      Input::Character ('Q') => break,
      Input::Character ('r') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Red)),
      Input::Character ('g') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Green)),
      Input::Character ('b') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Blue)),
      Input::Character ('c') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Cyan)),
      Input::Character ('y') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Yellow)),
      Input::Character ('m') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Magenta)),
      Input::Character ('k') =>
        curses.set_color_pair (ColorPair::new (Color::White, Color::Black)),
      Input::Character ('w') =>
        curses.set_color_pair (ColorPair::new (Color::Black, Color::White)),
      _ => {}
    }
    draw (&mut curses, &mut iter);
  }

  log::info!("example log end");
}