Author: Sean O’Halpin
A wrapper for ncurses 5.x. Tested on Mac OS X 10.4 (Tiger) and Ubuntu 8.04 with ruby 1.8.6 using ruby-ffi (>= 0.2.0) and JRuby 1.1.6.
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.
This is still very much a work-in-progress, so expect some rough edges. Having said that, you can do quite a lot with it as it is. The main things left to be done are tests, access to global variables and various macros.
Below are some very preliminary notes on usage. See the examples directory for real working examples.
ruby 1.8.6:
$ sudo gem install ffi ffi-ncurses
jruby 1.1.6:
$ jruby -S gem install ffi-ncurses
Load the library with:
require 'ffi-ncurses'
FFI::NCurses methods can be called as module methods:
begin stdscr = 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:
require 'ffi-ncurses' include FFI::NCurses begin stdscr = initscr start_color curs_set 0 raw cbreak noecho clear move 10, 10 standout addstr("Hi!") standend refresh getch ensure endwin end
require 'ffi-ncurses' FFI::NCurses.initscr begin ... ensure FFI::NCurses.endwin end
stdscr = FFI::NCurses.initscr FFI::NCurses.start_color FFI::NCurses.curs_set 0 FFI::NCurses.raw FFI::NCurses.cbreak FFI::NCurses.noecho FFI::NCurses.keypad(stdscr, true)
start_color init_pair(1, FFI::NCurses::COLOR_BLACK, FFI::NCurses::COLOR_RED) attr_set FFI::NCurses::A_NORMAL, 1, nil addch(?A) addch(?Z | COLOR_PAIR(1))
FFI::NCurses.curs_set 0
FFI::NCurses.curs_set 1
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 Object => e FFI::NCurses.endwin puts e ensure FFI::NCurses.endwin end
The ncurses mouse API is defined in a separate file. To include it use:
require 'ffi-ncurses/mouse'
You need to specify that you want keypad translation with:
keypad stdscr, FFI::NCurses::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.
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 PDCurses X11 curses lib, use:
RUBY_FFI_NCURSES_LIB=XCurses ruby examples/hello.rb
You can use this to specify ncursesw
for example. Please note that only the bog standard ncurses lib has been in any way tested as of yet.
There are some macros in darwin ncurses.h which I haven’t implemented yet. I’m working on it but if there are any you desperately need let me know.
These global variables are not often used but are required for certain situations (e.g. doing a wrefresh after shelling out and for the get/setsyx macros).
This requires the implementation of find_sym
in JRuby (expected in JRuby 1.1.7).
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.
Things got a bit messy as I switched between the Linux and Mac versions. The examples should be more focussed.
I’m not particularly interested in the ncurses extension libraries for forms and menus. I would rather spend time implementing similar functionality on top of a portable text console library (or porting rbcurses). However, in the interests of completeness, I suppose I ought to at least scope it out.
While researching ncurses on Google, I innocently entered “curses getsx” as a search term. NSFW and definitely not one for “I’m Feeling Lucky”.