ffi-ncurses¶ ↑
Author: Sean O’Halpin
A pure-ruby wrapper for {invisible-island.net/ncurses/ ncursesw 5.x} using the {github.com/ffi/ffi ffi} library.
All of the core ncursesw functions are supported along with the Panel library. The main things left to do are support for the Menu and Form libraries.
The API is a transliteration of the C API rather than an attempt to provide an idiomatic Ruby object-oriented API. The intent is to provide a ‘close to the metal’ wrapper around the ncurses library upon which you can build your own abstractions.
Please note that this documentation does not cover individual ncursesw methods. For that you’ll need to refer to existing ncursesw documentation. For example, to find out about addstr
, use:
$ man addstr
One benefit of using a minimal wrapper approach is that you can use existing examples and man pages to find out how to use the library. To get an overview of ncurses, use:
$ man ncurses
Having said that, this release also includes a wrapper that emulates the existing Ncurses library API. To use it, substitute
require 'ffi-ncurses/ncurses'
for
require 'ncurses'
or create a file called ‘ncurses.rb’ on your +$LOAD_PATH+ ($:) that requires ffi-ncurses/ncurses.rb
.
Below you’ll find some very preliminary notes on usage. See the examples directory for real working examples, which among other things show how to input and output UTF-8, deal with pointers and handle wide characters.
This version of ffi-ncurses defaults to loading ncursesw, the ‘wide character’ version which supports UTF-8 and double width characters.
Tested on Ubuntu 10.04 with ruby1.8.7 and 1.9.2 using ffi (>= 0.6.3) and JRuby 1.6.4 (head). A previous version of the library was tested on Mac OS X 10.04. Please let me know if anything has stopped working.
Rubinius is not supported as its FFI does not provide the required API, especially for dealing with buffers and pointers.
This is still very much a work-in-progress, so expect some rough edges (and please {github.com/seanohalpin/ffi-ncurses/issues report them}). Having said that, you can do quite a lot with it as it is.
Install¶ ↑
$ [sudo] gem install ffi-ncurses
Usage¶ ↑
Load the library with:
require 'ffi-ncurses'
FFI::NCurses methods can be called as module methods:
begin FFI::NCurses.initscr FFI::NCurses.clear FFI::NCurses.addstr("Hello world!") FFI::NCurses.refresh FFI::NCurses.getch ensure FFI::NCurses.endwin end
or as included methods:
include FFI::NCurses begin initscr cbreak noecho curs_set 0 clear move 10, 10 standout addstr("Hi!") standend refresh getch ensure endwin end
Set up screen¶ ↑
require 'ffi-ncurses' FFI::NCurses.initscr begin ... ensure FFI::NCurses.endwin end
Typical initialization¶ ↑
FFI::NCurses.initscr FFI::NCurses.start_color FFI::NCurses.curs_set 0 FFI::NCurses.raw FFI::NCurses.noecho FFI::NCurses.keypad(FFI::NCurses.stdscr, true)
Colours¶ ↑
start_color init_pair(1, FFI::NCurses::COLOR_BLACK, FFI::NCurses::COLOR_RED) attr_set FFI::NCurses::A_NORMAL, 1, nil addch("A"[0].ord) # works in both 1.8.7 and 1.9.x addch("Z"[0].ord | COLOR_PAIR(1))
See examples/color.rb
for an example of use.
Cursor¶ ↑
Turn cursor off¶ ↑
FFI::NCurses.curs_set 0
Turn cursor on¶ ↑
FFI::NCurses.curs_set 1
Windows¶ ↑
require 'ffi-ncurses' include FFI::NCurses begin initscr win = newwin(6, 12, 15, 15) box(win, 0, 0) inner_win = newwin(4, 10, 16, 16) waddstr(inner_win, (["Hello!"] * 5).join(' ')) wrefresh(win) wrefresh(inner_win) ch = wgetch(inner_win) delwin(win) rescue => e FFI::NCurses.endwin raise ensure FFI::NCurses.endwin end
Panels¶ ↑
See examples/panel_simple.rb
for how to use panels.
Mouse handling¶ ↑
NOTE: In previous versions of ffi-ncurses, the ncurses mouse API was included separately. You now no longer need to require 'ffi-ncurses/mouse'
to get mouse support.
To use the mouse with ffi-ncurses, you first need to specify that you want keypad translation with:
keypad stdscr, true
otherwise your program will receive the raw mouse escape codes, instead of KEY_MOUSE
mouse event codes.
Specify which events you want to handle with:
mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, nil)
and set up a mouse event structure to receive the returned values:
mouse_event = FFI::NCurses::MEVENT.new
Receiving mouse events is a two-stage process: first, you are notified that a mouse event has taken place through a special key code, then you retrieve the event using getmouse
. For example:
ch = getch case ch when FFI::NCurses::KEY_MOUSE if getmouse(mouse_event) == FFI::NCurses::OK
The mouse event contains the button state (bstate
) and x, y coordinates. You can test for the button state using:
if mouse_event[:bstate] & FFI::NCurses::BUTTON1_PRESSED
or
if FFI::NCurses.BUTTON_PRESS(mouse_event[:bstate], 1)
The possible button states are: PRESS
, RELEASE
, CLICK
, DOUBLE_CLICK
and TRIPLE_CLICK
.
See examples/mouse.rb
for a complete example.
Specifying which curses library to use¶ ↑
You can specify which variant of curses you want to use by setting the environment variable RUBY_FFI_NCURSES_LIB
to the one you want. For example, to use the PDCurses X11 curses lib, use:
RUBY_FFI_NCURSES_LIB=XCurses ruby examples/example.rb
You could also use this to specify ncursesw-dbg for example to get access to the trace
functions.
Examples¶ ↑
-
examples/acs_chars.rb
-
How to display box drawing characters
-
examples/attributes.rb
-
How to set attributes
-
examples/color.rb
-
How to initialize and use colour
-
examples/cursor.rb
-
How to turn the cursor on and off
-
examples/doc-eg1.rb
-
Example 1 from the documentation
-
examples/doc-eg2.rb
-
Example 2 from the documentation
-
examples/doc-eg3.rb
-
Example 3 from the documentation
-
examples/example.rb
-
An example showing off the main features of ncurses
-
examples/getkey.rb
-
How to get Unicode input
-
examples/getsetsyx.rb
-
Shows how to use the getsetyx function
-
examples/globals.rb
-
Display ncurses global variables
-
examples/hello.rb
-
Hello world
-
examples/hellowide.rb
-
Hello world using wide characters
-
examples/keys.rb
-
How to use the getch function. See getkey for a more general solution
-
examples/mouse.rb
-
How to use the mouse
-
examples/multiterm.rb
-
How to display on more than one tty
-
examples/newterm.rb
-
How to use
newterm
so you can pipe stdin into an ncurses program -
examples/panel_simple.rb
-
How to use panels
-
examples/printw-variadic.rb
-
How to call the
printw
method -
examples/ripoffline.rb
-
An example of the
ripoffline
method -
examples/softkeys.rb
-
How to set up soft keys (function key labels)
-
examples/stdscr.rb
-
Shows that
initscr
returns same value asstdscr
-
examples/temp_leave.rb
-
How to temporarily shell out from an ncurses program
-
examples/viewer.rb
-
A simple file viewer (lesser than less) that shows how to use pads and pop up windows
-
examples/wacs_chars.rb
-
How to display wide (Unicode) box drawing characters
-
examples/windows.rb
-
Move a window about the screen
Issues¶ ↑
Please report any issues on the {github.com/seanohalpin/ffi-ncurses/issues github issues} page.
Trivia¶ ↑
While researching ncurses on Google, I innocently entered “curses getsx” as a search term. NSFW and definitely not one for “I’m Feeling Lucky”.
TO DO¶ ↑
Tests¶ ↑
This is tricky - I’m not sure exactly how to properly test a wrapper for a library like ncurses. I certainly don’t want to test ncurses! Instead, I want to ensure my wrapper faithfully reproduces the functionality of the platform’s ncurses lib. To that end, I’m experimenting with a simple DSL to generate both C and Ruby versions of a test. With that I can generate equivalent programs and compare the output. However, this is not really ready for prime time yet.
Tidy up internals and examples¶ ↑
Things got a bit messy as I switched between the Linux and Mac versions. The examples should be more focussed.
Credits¶ ↑
Thanks to rahul and manveru for their support!