#ifndef TERMINALH #define TERMINALH #include //for ostream and << #include //for ostringstream #include //for div and rand #include //for sqrt #include #include //for class std::iterator #include //for fill, iterator_category, random_access_iterator_tag using namespace std; extern "C" { #include "term.h" } #include "except.h" template class terminal { public: typedef CHAR value_type; typedef size_t size_type; typedef ptrdiff_t difference_type; private: const CHAR _background; const size_type _xmax; const size_type _ymax; public: //Need forward declaration of class proxy here, //because the name proxy is mentioned several times in class iterator. class proxy; class iterator: public std::iterator { friend class terminal; friend class proxy; const terminal *const t; size_type i; //distance from begin to this iterator size_type x() const {return i % t->xmax();} size_type y() const {return i / t->xmax();} public: /* An iterator can be off the screen, as long as we do not attempt to dereference it. (For example, the iterator returned by terminal::end is off the screen.) Therefore the constructor for class iterator does not check that x and y are legal. */ iterator(const terminal& initial_t, size_type x, size_type y) : t(&initial_t), i(y * t->xmax() + x) {} iterator& operator+=(const difference_type& d) { i += d; return *this; } iterator& operator-=(const difference_type& d) { i -= d; return *this; } //Microsoft won't let operator+ be a friend. const iterator operator+(difference_type d) const { iterator it = *this; //Construct a copy of *this. return it += d; //return it.operator+=(d); } const iterator operator-(difference_type d) const { iterator it = *this; return it -= d; } iterator& operator++() {return *this += 1;} iterator& operator--() {return *this -= 1;} const iterator operator++(int) { const iterator old = *this; ++*this; //(*this).operator++(); return old; } const iterator operator--(int) { const iterator old = *this; --*this; return old; } iterator& operator=(const iterator& other) { if (t != other.t) { ostringstream ost; ost << "= with 2 different terminals"; throw except(ost); } i = other.i; return *this; } /* Return the horizontal component of a difference_type. Assuming an 80-character line, the horizontal component of -81 would be -1 the horizontal component of -79 would be 1 the horizontal component of 81 would be 1 the horizontal component of 79 would be -1 */ difference_type dx(difference_type d) const { const difference_type xm = t->xmax(); difference_type diff = (d + xm / 2) % xm; if (diff < 0) { diff += xm; } return diff - xm / 2; } /* Return the vertical component of a difference_type. Assuming an 80-character line, the vertical component of -81 would be -1 the vertical component of -79 would be -1 the vertical component of 81 would be 1 the vertical component of 79 would be 1 */ difference_type dy(difference_type d) const { const difference_type xm = t->xmax(); div_t di = div(d + xm / 2, xm); if (di.rem < 0) { --di.quot; } return di.quot; } /* Return true if it + d would stay on the screen. Return false if adding d to it would cause it to wrap around from the left edge of the screen to right edge or vice versa. */ bool in_range(difference_type d = 0) const { const size_type myx = static_cast(x()) + dx(d); const size_type myy = static_cast(y()) + dy(d); return myx < t->xmax() && myy < t->ymax(); } friend difference_type difference(const iterator& it1, const iterator& it2) { if (it1.t != it2.t) { ostringstream ost; ost << "difference with 2 different terminals"; throw except(ost); } return it2.x() - it1.x() + it1.t->xmax() * (it2.y() - it1.y()); } friend double dist(const iterator& it1, const iterator& it2) { const difference_type d = difference(it1, it2); const difference_type ddx = it1.dx(d); const difference_type ddy = it1.dy(d); return sqrt(static_cast(ddx * ddx + ddy * ddy)); } //Return -1 if d is negative, 1 if d is positive, 0 if d is 0. //They're like the three return values of the C strcmp function. friend difference_type signum(difference_type d) { return d < 0 ? -1 : d > 0; } //Return the difference_type that would take one step //from it1 to it2. friend difference_type step(const iterator& it1, const iterator& it2) { const difference_type d = difference(it1, it2); const terminal *const t = it1.t; return signum(it1.dx(d)) + t->xmax() * signum(it1.dy(d)); } friend difference_type operator-(const iterator& it1, const iterator& it2) { if (it1.t != it2.t) { ostringstream ost; ost << "- with 2 different terminals"; throw except(ost); } return it1.i - it2.i; } friend bool operator==(const iterator& it1,const iterator& it2){ return it1.t == it2.t && it1.i == it2.i; } friend bool operator<(const iterator& it1, const iterator& it2){ if (it1.t != it2.t) { ostringstream ost; ost << "< with 2 different terminals"; throw except(ost); } return it1.i < it2.i; } friend bool operator<=(const iterator& it1,const iterator& it2){ if (it1.t != it2.t) { ostringstream ost; ost << "<= with 2 different terminals"; throw except(ost); } return it1.i <= it2.i; } friend bool operator!=(const iterator& it1,const iterator& it2){ return !(it1 == it2); } friend bool operator>(const iterator& it1, const iterator& it2){ return it2 < it1; } friend bool operator>=(const iterator& it1,const iterator& it2){ return it2 <= it1; } friend ostream& operator<<(ostream& ost, const iterator& it) { return ost << "(" << it.x() << ", " << it.y() << ")"; } const proxy operator*() const { return proxy(*this); } const proxy operator[](const difference_type& d) const { return *(*this + d); } }; class proxy { bool b; //true if this proxy refers to a CHAR in a terminal const iterator it; //used only if b == true mutable CHAR c; //used only if b == false proxy(const iterator& initial_it) : b(true), it(initial_it), c('A') { if (!it.in_range()) { ostringstream ost; ost << "location " << it << " off screen whose size is (" << it.t->xmax() << ", " << it.t->ymax() << ")"; throw except(ost); } } public: proxy(const proxy& another) : b(false), it(another.it), c(another) {} const proxy& operator=(CHAR c) const { if (b) { term_put(it.x(), it.y(), c); } else { this->c = c; } return *this; } operator CHAR() const { return b ? CHAR(term_get(it.x(), it.y())) : c; } const proxy& operator+=(int i) const { return *this = static_cast(*this) + i; } const proxy& operator-=(int i) const { return *this = static_cast(*this) - i; } const proxy& operator++() const {return *this += 1;} const proxy& operator--() const {return *this -= 1;} const proxy operator++(int) const { const proxy old = *this; ++*this; //(*this).operator++(); return old; } const proxy operator--(int) const { const proxy old = *this; --*this; //(*this).operator--(); return old; } bool operator==(const CHAR& c) const { return static_cast(*this) == c; } bool operator<(const CHAR& c) const { return static_cast(*this) < c; } friend const proxy iterator::operator*() const; }; terminal(CHAR initial_background = ' ') : _background(initial_background), _xmax((term_construct(), term_xmax())), _ymax(term_ymax()) { if (background() != static_cast(' ')) { fill(begin(), end(), background()); } } ~terminal() { fill(begin(), end(), ' '); term_destruct(); } CHAR background() const {return _background;} size_type xmax() const {return _xmax;} size_type ymax() const {return _ymax;} size_type size() const {return xmax() * ymax();} iterator begin() const {return iterator(*this, 0, 0);} iterator end() const {return iterator(*this, 0, ymax());} static char key() {return term_key();} static void wait(int milliseconds) {term_wait(milliseconds);} static void beep() {term_beep();} difference_type rand() const { return (std::rand() % 3 - 1) * xmax() + std::rand() % 3 - 1; } difference_type right() const {return 1;} difference_type down() const {return xmax();} difference_type left() const {return -right();} difference_type up() const {return -down();} typedef map keypad_t; typedef keypad_t::value_type pair_t; keypad_t keypad() const { static const pair_t a[] = { pair_t('h', left()), pair_t('j', down()), pair_t('k', up()), pair_t('l', right()) }; static const size_t n = sizeof a / sizeof a[0]; return keypad_t(a, a + n); } }; #endif