Skip to content

Instantly share code, notes, and snippets.

@cordarei
Created August 22, 2012 05:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cordarei/3422547 to your computer and use it in GitHub Desktop.
Save cordarei/3422547 to your computer and use it in GitHub Desktop.
Simple(?) C++ wrapper around libncurses! Check out the awesome NetHack ripoff^W clone^W homage in main()!
/*
* Compile with "g++ -std=c++0x main.cc -lncurses && ./a.out" to check it out!
*/
#include <curses.h>
#undef getch
#undef border
#undef hline
#undef addch
#include <exception>
#include <memory>
#include <iostream>
#include <sstream>
#undef assert
namespace nc {
/* Exceptions and Error Checking */
struct nc_exception : public std::exception {
const char *what() const throw() { return "nc::nc_exception"; }
};
struct failed_assert : public std::exception {
const char *what() const throw() { return "failed assertion"; }
};
inline void check(int ret) {
if (ERR == ret) {
throw nc_exception();
}
}
inline void assert(bool cond) {
if (!cond) throw failed_assert();
}
}
namespace nc {
namespace geom {
/* Useful Geometric Functionality */
struct coord {
size_t row;
size_t col;
coord(size_t r, size_t c) : row(r), col(c) {}
};
bool operator<(const coord& left, const coord& right) {
return (left.row < right.row) ||
(left.row == right.row && left.col < right.col);
}
bool operator==(const coord& left, const coord& right) {
return left.row == right.row && left.col == right.col;
}
struct rect {
coord tl;
coord br;
rect(coord ul, coord lr) : tl(ul), br(lr) { assert(ul < br); }
size_t width() const { return br.col - tl.col; }
size_t height() const { return br.row - tl.row; }
};
bool operator==(const rect& left, const rect& right) {
return left.tl == right.tl && left.br == right.br;
}
}
using namespace geom;
/* Create & store a border specification */
struct border_t {
typedef int char_type;
char_type l;
char_type r;
char_type t;
char_type b;
char_type ul;
char_type ur;
char_type ll;
char_type lr;
border_t(char_type c)
: l(c), r(c), t(c), b(c), ul(c), ur(c), ll(c), lr(c) {}
border_t(char_type v, char_type h, char_type c)
: l(v), r(v), t(h), b(h), ul(c), ur(c), ll(c), lr(c) {}
border_t(char_type v, char_type h,
char_type c1, char_type c2, char_type c3, char_type c4)
: l(v), r(v), t(h), b(h), ul(c1), ur(c2), ll(c3), lr(c4) {}
border_t(char_type vl, char_type vr, char_type ht, char_type hb,
char_type c1, char_type c2, char_type c3, char_type c4)
: l(vl), r(vr), t(ht), b(hb), ul(c1), ur(c2), ll(c3), lr(c4) {}
static border_t blank() {
return border_t(' ');
}
static border_t line() {
return border_t(
ACS_VLINE,
ACS_VLINE,
ACS_HLINE,
ACS_HLINE,
ACS_ULCORNER,
ACS_URCORNER,
ACS_LLCORNER,
ACS_LRCORNER
);
}
};
struct attr {
attr() : _att(A_NORMAL) {}
attr& color(short idx) {
_att = (_att & ~A_COLOR) | COLOR_PAIR(idx);
return *this;
}
attr& reverse(bool set) {
if (set) { _att |= A_REVERSE; }
else { _att &= ~A_REVERSE; }
return *this;
}
attr& reverse() { return reverse(!(_att & A_REVERSE)); }
attr_t _att;
};
struct window {
/* Types for storing a pointer to a WINDOW and releasing it */
private:
struct deleter {
bool owns;
deleter() : owns(true) {}
deleter(bool o) : owns(o) {}
void operator()(WINDOW *p) {
if (owns && (p)) {
delwin(p);
}
}
};
typedef std::unique_ptr<WINDOW, deleter> window_ptr;
/* Creation */
public:
window(WINDOW *p, bool take_ownership=true)
: _p(p, deleter(take_ownership)) {
get_attr();
}
static window create(size_t height, size_t width, size_t row, size_t col) {
return window(newwin(height, width, row, col), true);
}
static window create(rect bounds) {
return create(bounds.height(), bounds.width(), bounds.tl.row, bounds.tl.col);
}
/* Accessors */
size_t width() const { return getmaxx(ptr()); }
size_t height() const { return getmaxy(ptr()); }
coord beg() const { return coord(getbegy(ptr()), getbegx(ptr())); }
coord cur() const { return coord(getcury(ptr()), getcurx(ptr())); }
/* Drawing to the screen */
void print(const std::string& s) {
set_attr();
check(::wprintw(ptr(), s.c_str()));
}
void print(const std::string& s, size_t row, size_t col) {
set_attr();
check(::mvwprintw(ptr(), row, col, s.c_str()));
}
void box(int v, int h) {
set_attr();
check(::box(ptr(), v, h));
}
void border(border_t b) {
set_attr();
check(::wborder(ptr(),
b.l, b.r, b.t, b.b, b.ul, b.ur, b.ll, b.lr));
}
void hline(int ch, coord start, size_t len) {
set_attr();
check(mvwhline(ptr(), start.row, start.col, ch, len));
}
void fill(int ch) {
for (size_t y = 1; y < height() - 1; ++y)
hline(ch, coord(y, 1), width() - 2);
}
void addch(int ch, coord pos) {
set_attr();
check(mvwaddch(ptr(), pos.row, pos.col, ch));
}
void addch(int ch) {
set_attr();
check(waddch(ptr(), ch));
}
void refresh() { check(::wrefresh(ptr())); }
/* Manipulate window attributes to be applied on next draw command */
attr &attrs() { return _att; }
/* Input */
int getch() { return ::wgetch(ptr()); }
void keypad(bool enable_keypad) {
check(::keypad(ptr(), enable_keypad));
}
/* Allow derived types access to pointer */
protected:
WINDOW *ptr() { return _p.get(); }
const WINDOW *ptr() const { return _p.get(); }
private:
void set_attr() { wattrset(ptr(), _att._att); }
void get_attr() { _att._att = ptr()->_attrs; }
private:
window_ptr _p;
attr _att;
};
struct attr_restorer {
window &ref;
attr saved;
attr_restorer(window &win) : ref(win), saved(win.attrs()) {}
~attr_restorer() { ref.attrs() = saved; }
};
enum class color_t { black, red, green, yellow, blue, magenta, cyan, white };
struct color_pair {
typedef short index_type;
index_type idx;
color_t fg;
color_t bg;
color_pair() : idx(0), fg(color_t::black), bg(color_t::black) {}
color_pair(index_type i, color_t f, color_t b) : idx(i), fg(f), bg(b) {}
};
struct colors {
enum { max_pairs = 8 };
void add_pair(color_t fg, color_t bg) {
assert(_count < max_pairs);
size_t idx = _count++;
_pairs[idx] = color_pair(static_cast<short>(idx+1), fg, bg);
}
size_t size() const { return _count; }
color_pair& operator[](size_t idx) {
assert( idx > 0 && idx <= _count);
return _pairs[idx-1];
}
const color_pair& operator[](size_t idx) const {
assert(idx > 0 && idx <= _count);
return _pairs[idx-1];
}
colors() : _count(0) {}
private:
size_t _count;
color_pair _pairs[max_pairs];
};
enum class input_mode { line, cbreak, raw };
/* Initialize NCurses system, manage global term settings etc. */
struct standard_screen : public window {
standard_screen(
nc::input_mode im = nc::input_mode::cbreak,
bool kp = true,
bool e = false) : window(::initscr(), false) {
input_mode(im);
keypad(kp);
echo(e);
if (::has_colors()) {
::start_color();
}
}
void colors(nc::colors cs) {
_colors = cs;
for (size_t i = 1; i <= cs.size(); ++i) {
::init_pair(cs[i].idx, static_cast<short>(cs[i].fg), static_cast<short>(cs[i].bg));
}
}
void input_mode(nc::input_mode im) {
switch (im) {
case nc::input_mode::line:
::noraw();
::nocbreak();
break;
case nc::input_mode::cbreak:
::cbreak();
break;
case nc::input_mode::raw:
::raw();
break;
}
}
void echo(bool enable) {
if (enable) {
::echo();
} else {
::noecho();
}
}
~standard_screen() {
check(::endwin());
}
private:
nc::colors _colors;
};
}
int main(int argc, char **argv)
{
try {
nc::standard_screen scr(nc::input_mode::raw);
if (!has_key(0403)) {
scr.print("No UP key :(");
scr.getch();
return 0;
}
nc::colors cs;
cs.add_pair(nc::color_t::red, nc::color_t::black);
cs.add_pair(nc::color_t::blue, nc::color_t::black);
cs.add_pair(nc::color_t::green, nc::color_t::black);
cs.add_pair(nc::color_t::yellow, nc::color_t::black);
scr.colors(cs);
size_t screen_width = scr.width();
size_t screen_height = scr.height();
//Create 3 windows:
// - At top, ~10 rows tall, shows player statistics
// - Main window, shows world (HEIGHT - 10 - 3)
// - Bottom: status bar-kind of thing
nc::window stats(nc::window::create(10, screen_width, 0, 0));
stats.attrs().color(1);
stats.border(nc::border_t::line());
// Show some dummy statistics
stats.print("Str: 1", 1, 5);
stats.print("Dex: 1", 1, 16);
stats.print("Spd: 1", 2, 5);
stats.print("Mag: 1", 2, 16);
nc::window main(nc::window::create(screen_height-13, screen_width, 10, 0));
main.keypad(true);
main.attrs().color(2);
main.border(nc::border_t::line());
main.fill('#');
size_t midx = screen_width / 2;
size_t midy = screen_height / 2;
for (size_t y = midy - 15; y < midy + 15; ++y)
main.hline('.', nc::coord(y, midx-30), 60);
nc::coord player(midy, midx);
main.attrs().color(4);
main.addch('@', player);
nc::window bar(nc::window::create(3, screen_width, screen_height-3, 0));
bar.attrs().color(3);
bar.border(nc::border_t::line());
{
std::stringstream ss;
ss << "Screen is " << screen_height << "x" << screen_width
<< " "
<< "Player at: (" << player.row << "," << player.col << ")";
bar.print(ss.str(), 1, 4);
}
stats.refresh();
main.refresh();
bar.refresh();
int ch = 0;
while ((ch = main.getch()) != 'q') {
nc::coord last = player;
switch (ch) {
case KEY_UP:
player = nc::coord(player.row - 1, player.col);
break;
case KEY_DOWN:
player = nc::coord(player.row + 1, player.col);
break;
case KEY_LEFT:
player = nc::coord(player.row, player.col - 1);
break;
case KEY_RIGHT:
player = nc::coord(player.row, player.col + 1);
break;
default:
bar.print("Pressed: ", 1, 50);
bar.addch(ch);
break;
}
main.attrs().color(2);
main.addch('.', last);
main.attrs().color(4);
main.addch('@', player);
main.refresh();
{
std::stringstream ss;
ss << "Screen is " << screen_height << "x" << screen_width
<< " "
<< "Player at: (" << player.row << "," << player.col << ")";
bar.print(ss.str(), 1, 4);
bar.refresh();
}
}
return 0;
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return -1;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment