From: g0dil Date: Tue, 6 Jan 2009 13:49:42 +0000 (+0000) Subject: Utils/Termlib: Implement terminfo format string interpretation X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=844c117cb04bc73a5b920c2c49efbf14515da3e2;p=senf.git Utils/Termlib: Implement terminfo format string interpretation Utils/Termlib: Decouple terminal and telnet dependent code using new AbstractTerminal API Utils/Termlib: Implement basic terminal navigation commands git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1038 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Utils/Termlib/AbstractTerminal.hh b/Utils/Termlib/AbstractTerminal.hh new file mode 100644 index 0000000..01d72f7 --- /dev/null +++ b/Utils/Termlib/AbstractTerminal.hh @@ -0,0 +1,75 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Terminal public header */ + +#ifndef HH_SENF_Utils_Termlib_AbstractTerminal_ +#define HH_SENF_Utils_Termlib_AbstractTerminal_ 1 + +// Custom includes +#include + +//#include "AbstractTerminal.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace term { + + struct AbstractTerminal + { + struct Callbacks { + virtual ~Callbacks() {} + virtual void cb_init() = 0; + virtual void cb_charReceived(char ch) = 0; + virtual void cb_windowSizeChanged() = 0; + }; + + virtual ~AbstractTerminal() {} + + virtual void setCallbacks(Callbacks & cb) = 0; + + virtual std::string terminalType() = 0; + virtual unsigned width() = 0; + virtual unsigned height() = 0; + + virtual void write(char ch) = 0; + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "AbstractTerminal.cci" +//#include "AbstractTerminal.ct" +//#include "AbstractTerminal.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Termlib/Editor.cc b/Utils/Termlib/Editor.cc new file mode 100644 index 0000000..c523907 --- /dev/null +++ b/Utils/Termlib/Editor.cc @@ -0,0 +1,190 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Editor non-inline non-template implementation */ + +#include "Editor.hh" +//#include "Editor.ih" + +// Custom includes +#include +#include + +//#include "Editor.mpp" +#define prefix_ +///////////////////////////////cc.p//////////////////////////////////////// + +prefix_ senf::term::BaseEditor::BaseEditor(AbstractTerminal & terminal) + : terminal_ (&terminal), + keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)), + timer_ ("senf::term::BaseEditor::keySequenceTimeout", + senf::membind(&BaseEditor::keySequenceTimeout, this)), + column_ (0u) +{ + terminal_->setCallbacks(*this); +} + +prefix_ void senf::term::BaseEditor::newline() +{ + write("\r\n"); + write(tifo_.getString(Terminfo::properties::ClrEol)); + column_ = 0; +} + +prefix_ void senf::term::BaseEditor::toColumn(unsigned c) +{ + if (c >= width()) + c = width(); + if (c > column_) { + if (tifo_.hasProperty(Terminfo::properties::ParmRightCursor)) { + write(tifo_.formatString(Terminfo::properties::ParmRightCursor, c - column_)); + column_ = c; + } + else { + char const * cuf1 (tifo_.getString(Terminfo::properties::CursorRight)); + while (c > column_) { + write(cuf1); + ++column_; + } + } + } + else if (c < column_) { + if (tifo_.hasProperty(Terminfo::properties::ParmLeftCursor)) { + write(tifo_.formatString(Terminfo::properties::ParmLeftCursor, column_ - c)); + column_ = c; + } + else { + char const * cub1 (tifo_.getString(Terminfo::properties::CursorRight)); + while (c < column_) { + write(cub1); + --column_; + } + } + } +} + +prefix_ void senf::term::BaseEditor::insertChar(char ch) +{ + if (column_+1 >= width()) + return; + if (tifo_.hasProperty(Terminfo::properties::InsertCharacter)) + write(tifo_.getString(Terminfo::properties::InsertCharacter)); + else + write(tifo_.formatString(Terminfo::properties::ParmIch, 1)); + write(ch); + ++ column_; +} + +prefix_ void senf::term::BaseEditor::overwriteChar(char ch) +{ + write(ch); + ++ column_; +} + +prefix_ void senf::term::BaseEditor::deleteChar() +{ + if (tifo_.hasProperty(Terminfo::properties::DeleteCharacter)) + write(tifo_.getString(Terminfo::properties::DeleteCharacter)); + else + write(tifo_.formatString(Terminfo::properties::ParmDch, 1)); +} + +prefix_ unsigned senf::term::BaseEditor::currentColumn() + const +{ + return column_; +} + +prefix_ void senf::term::BaseEditor::cb_init() +{ + tifo_.load(terminal_->terminalType()); + keyParser_.load(tifo_); + if (tifo_.hasProperty(Terminfo::properties::KeypadXmit)) + write(tifo_.getString(Terminfo::properties::KeypadXmit)); +} + +prefix_ void senf::term::BaseEditor::cb_charReceived(char c) +{ + inputBuffer_ += c; + timer_.timeout(senf::scheduler::eventTime() + keyTimeout_); + processKeys(); +} + +prefix_ void senf::term::BaseEditor::cb_windowSizeChanged() +{ + if (column_ >= width()) + column_ = width()-1; +} + +prefix_ void senf::term::BaseEditor::keySequenceTimeout() +{ + while (!inputBuffer_.empty()) { + processKeys(); + v_keyReceived(keycode_t(inputBuffer_[0])); + inputBuffer_.erase(0, 1); + } +} + +prefix_ void senf::term::BaseEditor::processKeys() +{ + do { + std::pair result + (keyParser_.lookup(inputBuffer_)); + if (result.first == senf::term::KeyParser::Incomplete) + return; + v_keyReceived(result.first); + inputBuffer_.erase(0, result.second); + } while (! inputBuffer_.empty()); + timer_.disable(); +} + +prefix_ unsigned senf::term::BaseEditor::width() +{ + return terminal_->width(); +} + +prefix_ void senf::term::BaseEditor::write(char ch) +{ + terminal_->write(ch); +} + +prefix_ void senf::term::BaseEditor::write(std::string const & s) +{ + for (std::string::const_iterator i (s.begin()); i != s.end(); ++i) + write(*i); +} + +///////////////////////////////cc.e//////////////////////////////////////// +#undef prefix_ +//#include "Editor.mpp" + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Termlib/Editor.hh b/Utils/Termlib/Editor.hh new file mode 100644 index 0000000..6253423 --- /dev/null +++ b/Utils/Termlib/Editor.hh @@ -0,0 +1,99 @@ +// $Id$ +// +// Copyright (C) 2009 +// Fraunhofer Institute for Open Communication Systems (FOKUS) +// Competence Center NETwork research (NET), St. Augustin, GERMANY +// Stefan Bund +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the +// Free Software Foundation, Inc., +// 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +/** \file + \brief Editor public header */ + +#ifndef HH_SENF_Utils_Termlib_Editor_ +#define HH_SENF_Utils_Termlib_Editor_ 1 + +// Custom includes +#include +#include +#include "AbstractTerminal.hh" +#include "Terminfo.hh" + +//#include "Editor.mpp" +///////////////////////////////hh.p//////////////////////////////////////// + +namespace senf { +namespace term { + + class BaseEditor + : public AbstractTerminal::Callbacks + { + public: + typedef KeyParser::keycode_t keycode_t; + + static unsigned const DEFAULT_KEY_TIMEOUT_MS = 500u; + + explicit BaseEditor(AbstractTerminal & terminal); + + void newline(); ///< Move to beginning of a new, empty line + void toColumn(unsigned c); ///< Move cursor to column \p c + void insertChar(char ch); ///< Insert \p ch at current column, shifting text right + void overwriteChar(char ch); ///< Write \p ch at current column + void deleteChar(); ///< Delete a character a current column + + unsigned currentColumn() const; ///< Return number of current column + + private: + virtual void v_keyReceived(keycode_t key) = 0; + + virtual void cb_init(); + virtual void cb_charReceived(char c); + virtual void cb_windowSizeChanged(); + + void keySequenceTimeout(); + void processKeys(); + + unsigned width(); + void write(char ch); + void write(std::string const & s); + + AbstractTerminal * terminal_; + Terminfo tifo_; + KeyParser keyParser_; + std::string inputBuffer_; + ClockService::clock_type keyTimeout_; + scheduler::TimerEvent timer_; + unsigned column_; + }; + +}} + +///////////////////////////////hh.e//////////////////////////////////////// +//#include "Editor.cci" +//#include "Editor.ct" +//#include "Editor.cti" +#endif + + +// Local Variables: +// mode: c++ +// fill-column: 100 +// comment-column: 40 +// c-file-style: "senf" +// indent-tabs-mode: nil +// ispell-local-dictionary: "american" +// compile-command: "scons -u test" +// End: diff --git a/Utils/Termlib/TelnetTerminal.cc b/Utils/Termlib/TelnetTerminal.cc index 1c244ba..6b28a4f 100644 --- a/Utils/Termlib/TelnetTerminal.cc +++ b/Utils/Termlib/TelnetTerminal.cc @@ -27,55 +27,56 @@ //#include "TelnetTerminal.ih" // Custom includes -#include //#include "TelnetTerminal.mpp" #define prefix_ ///////////////////////////////cc.p//////////////////////////////////////// prefix_ senf::term::TelnetTerminal::TelnetTerminal() - : keyTimeout_ (senf::ClockService::milliseconds(DEFAULT_KEY_TIMEOUT_MS)), - timer_ ("senf::term::TelnetTerminal::keySequenceTimeout", - senf::membind(&TelnetTerminal::keySequenceTimeout, this)) { requestPeerOption(telnetopt::SUPPRESS_GO_AHEAD); requestLocalOption(telnetopt::SUPPRESS_GO_AHEAD); requestLocalOption(telnetopt::ECHO); } -prefix_ void senf::term::TelnetTerminal::v_setupComplete() +prefix_ void senf::term::TelnetTerminal::setCallbacks(AbstractTerminal::Callbacks & cb) +{ + callbacks_ = &cb; +} + +prefix_ std::string senf::term::TelnetTerminal::terminalType() +{ + return telnethandler::TerminalType::terminalType(); +} + +prefix_ unsigned senf::term::TelnetTerminal::width() { - tifo_.load(terminalType()); - keyParser_.load(tifo_); + return telnethandler::NAWS::width(); } -prefix_ void senf::term::TelnetTerminal::v_charReceived(char c) +prefix_ unsigned senf::term::TelnetTerminal::height() +{ + return telnethandler::NAWS::height(); +} + +prefix_ void senf::term::TelnetTerminal::write(char ch) +{ + BaseTelnetProtocol::write(ch); +} + +prefix_ void senf::term::TelnetTerminal::v_setupComplete() { - inputBuffer_ += c; - timer_.timeout(senf::scheduler::eventTime() + keyTimeout_); - processKeys(); + callbacks_->cb_init(); } -prefix_ void senf::term::TelnetTerminal::keySequenceTimeout() +prefix_ void senf::term::TelnetTerminal::v_charReceived(char ch) { - while (!inputBuffer_.empty()) { - processKeys(); - v_keyReceived(keycode_t(inputBuffer_[0])); - inputBuffer_.erase(0, 1); - } + callbacks_->cb_charReceived(ch); } -prefix_ void senf::term::TelnetTerminal::processKeys() +prefix_ void senf::term::TelnetTerminal::v_windowSizeChanged() { - do { - std::pair result - (keyParser_.lookup(inputBuffer_)); - if (result.first == senf::term::KeyParser::Incomplete) - return; - v_keyReceived(result.first); - inputBuffer_.erase(0, result.second); - } while (! inputBuffer_.empty()); - timer_.disable(); + callbacks_->cb_windowSizeChanged(); } ///////////////////////////////cc.e//////////////////////////////////////// diff --git a/Utils/Termlib/TelnetTerminal.hh b/Utils/Termlib/TelnetTerminal.hh index 0514c63..98cab31 100644 --- a/Utils/Termlib/TelnetTerminal.hh +++ b/Utils/Termlib/TelnetTerminal.hh @@ -27,10 +27,8 @@ #define HH_SENF_Utils_Termlib_TelnetTerminal_ 1 // Custom includes -#include -#include #include "Telnet.hh" -#include "Terminfo.hh" +#include "AbstractTerminal.hh" //#include "TelnetTerminal.mpp" ///////////////////////////////hh.p//////////////////////////////////////// @@ -40,31 +38,26 @@ namespace term { class TelnetTerminal : public telnethandler::TerminalType, - public telnethandler::NAWS + public telnethandler::NAWS, + public AbstractTerminal { public: - typedef KeyParser::keycode_t keycode_t; - - static unsigned const DEFAULT_KEY_TIMEOUT_MS = 500u; - TelnetTerminal(); - protected: - virtual void v_setupComplete(); + virtual void setCallbacks(AbstractTerminal::Callbacks & cb); + virtual std::string terminalType(); + virtual unsigned width(); + virtual unsigned height(); + virtual void write(char ch); private: - virtual void v_keyReceived(keycode_t key) = 0; - - virtual void v_charReceived(char c); - void keySequenceTimeout(); - void processKeys(); + virtual void v_setupComplete(); + virtual void v_charReceived(char ch); + virtual void v_windowSizeChanged(); - senf::term::Terminfo tifo_; - senf::term::KeyParser keyParser_; - std::string inputBuffer_; - senf::ClockService::clock_type keyTimeout_; - senf::scheduler::TimerEvent timer_; + AbstractTerminal::Callbacks * callbacks_; }; + }} ///////////////////////////////hh.e//////////////////////////////////////// diff --git a/Utils/Termlib/Terminfo.cc b/Utils/Termlib/Terminfo.cc index 1a7c50f..7ad201f 100644 --- a/Utils/Termlib/Terminfo.cc +++ b/Utils/Termlib/Terminfo.cc @@ -170,6 +170,149 @@ prefix_ senf::term::Terminfo::string_t senf::term::Terminfo::getString(propertie return strings_[p]; } +prefix_ bool senf::term::Terminfo::hasProperty(properties::Boolean p) + const +{ + return getFlag(p); +} + +prefix_ bool senf::term::Terminfo::hasProperty(properties::Numeric p) + const +{ + return getNumber(p) != NoValue; +} + +prefix_ bool senf::term::Terminfo::hasProperty(properties::String p) + const +{ + return getString(p) != 0; +} + +namespace { + + struct Stack + { + std::vector stack; + + void push(senf::term::Terminfo::number_t v) + { + stack.push_back(v); + } + + senf::term::Terminfo::number_t pop() + { + if (stack.empty()) + return 0; + else { + senf::term::Terminfo::number_t v (stack.back()); + stack.pop_back(); + return v; + } + } + + senf::term::Terminfo::number_t popNonzero() + { + senf::term::Terminfo::number_t v (pop()); + return v ? v : 1; + } + }; + +} + +// The following code is taken directly from utio. As far as I understand it is buggy +// and/or only partially implements the string format language. But seems to be enough for +// all the common terminal types ... +prefix_ std::string senf::term::Terminfo::formatString(properties::String p, + number_t arg1, number_t arg2, + number_t arg3, number_t arg4, + number_t arg5, number_t arg6, + number_t arg7, number_t arg8, + number_t arg9) + const +{ + char const * fmt_p (getString(p)); + if (! fmt_p) + return ""; + + std::string const prgstr (fmt_p); + Stack stack; + bool bCondValue (false); + std::string result; + + for (std::string::const_iterator i (prgstr.begin()); i != prgstr.end(); ++i) { + if (*i != '%') { + result += *i; + continue; + } + int width = 0, base = 0; + switch (*++i) { + case '%': result += *i; break; + case 'i': ++arg1; ++arg2; break; + case 'c': result += char(stack.pop()); break; + case 'x': base = 16; continue; + case '0': if (!base) base = 8; + case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': + case '9': if (!base) base = 10; + width = width * base + (*i - '0'); + continue; + case '\\': base = 0; + case '{': continue; + case '\'': if (*(i - 1) == '%') { + if (*(i + 1) != '\\') + width = *++i; + continue; + } + case '}': stack.push(width); break; + // Binary operands are in infix (reversed) order + case '+': stack.push(stack.pop() + stack.pop()); break; + case '-': stack.push(-stack.pop() + stack.pop()); break; + case '*': stack.push(stack.pop() * stack.pop()); break; + case '/': stack.push(stack.pop() / stack.popNonzero()); break; + case 'm': stack.push(stack.pop() % stack.popNonzero()); break; + case '|': stack.push(stack.pop() | stack.pop()); break; + case '&': stack.push(stack.pop() & stack.pop()); break; + case '^': stack.push(stack.pop() ^ stack.pop()); break; + case '>': stack.push(stack.pop() < stack.pop()); break; + case '<': stack.push(stack.pop() > stack.pop()); break; + case '=': stack.push(stack.pop() == stack.pop()); break; + case 'A': stack.push(stack.pop() && stack.pop()); break; + case 'O': stack.push(stack.pop() || stack.pop()); break; + case '!': stack.push(!stack.pop()); break; + case '~': stack.push(~stack.pop()); break; + case 't': bCondValue = stack.pop(); + case 'e': if ((bCondValue = !bCondValue)) // this also supports elsif + --(i = prgstr.begin() + std::min (prgstr.find ("%e", i-prgstr.begin()), + prgstr.find ("%;", i-prgstr.begin()))); + case '?': + case ';': break; + case 'p': + switch (*++i) { + case '1': stack.push(arg1); break; + case '2': stack.push(arg2); break; + case '3': stack.push(arg3); break; + case '4': stack.push(arg4); break; + case '5': stack.push(arg5); break; + case '6': stack.push(arg6); break; + case '7': stack.push(arg7); break; + case '8': stack.push(arg8); break; + case '9': stack.push(arg9); break; + } + break; + case 'd': { + number_t n = stack.pop(); + const std::string::size_type iSize = result.size(); + do { + result += std::string::value_type('0' + (n % 10)); + } while ((n /= 10) || --width > 0); + reverse (result.begin() + iSize, result.end()); + break; } + } + } + + return result; +} + prefix_ void senf::term::Terminfo::dump(std::ostream & os) const { diff --git a/Utils/Termlib/Terminfo.hh b/Utils/Termlib/Terminfo.hh index c34a568..f001df8 100644 --- a/Utils/Termlib/Terminfo.hh +++ b/Utils/Termlib/Terminfo.hh @@ -47,15 +47,6 @@ namespace term { /////////////////////////////////////////////////////////////////////////// // Types - enum Color { - Black, Red, Green, Brown, Blue, Magenta, Cyan, LightGray, DarkGray, LightRed, - LightGreen, Yellow, LightBlue, Pink, LightCyan, White, - Preserve, LastColor = Preserve }; - - enum Attribute { - Standout, Underline, Reverse, Blink, HalfBright, Bold, Invisible, Protect, AltCharset, - Italic, Substript, Superscript, LastAttribute }; - enum { NoValue = -1 }; struct properties @@ -166,6 +157,17 @@ namespace term { bool getFlag(properties::Boolean p) const; number_t getNumber(properties::Numeric p) const; string_t getString(properties::String p) const; + bool hasProperty(properties::Boolean p) const; + bool hasProperty(properties::Numeric p ) const; + bool hasProperty(properties::String p ) const; + std::string formatString(properties::String p, + number_t arg1=NoValue, number_t arg2=NoValue, + number_t arg3=NoValue, number_t arg4=NoValue, + number_t arg5=NoValue, number_t arg6=NoValue, + number_t arg7=NoValue, number_t arg8=NoValue, + number_t arg9=NoValue) const; + + /////////////////////////////////////////////////////////////////////////// void dump(std::ostream & os) const; diff --git a/Utils/Termlib/Terminfo.test.cc b/Utils/Termlib/Terminfo.test.cc index 8ef55b3..37b7c2c 100644 --- a/Utils/Termlib/Terminfo.test.cc +++ b/Utils/Termlib/Terminfo.test.cc @@ -74,6 +74,10 @@ BOOST_AUTO_UNIT_TEST(terminfo) Pair(senf::term::KeyParser::keycode_t('b'), 1u) ); BOOST_CHECK_EQUAL( kp.lookup(""), Pair(senf::term::KeyParser::keycode_t('\0'), 0u) ); + + BOOST_CHECK_EQUAL( ifo.formatString(senf::term::Terminfo::properties::CursorAddress, + 9, 12), + "\e[10;13H" ); } ///////////////////////////////cc.e//////////////////////////////////////// diff --git a/Utils/Termlib/telnetServer.cc b/Utils/Termlib/telnetServer.cc index da4c15a..a02711f 100644 --- a/Utils/Termlib/telnetServer.cc +++ b/Utils/Termlib/telnetServer.cc @@ -28,10 +28,11 @@ // Custom includes #include -#include "TelnetTerminal.hh" #include "../../Scheduler/Scheduler.hh" #include "../Logger.hh" #include "../../Socket/Protocols/INet.hh" +#include "TelnetTerminal.hh" +#include "Editor.hh" //#include "telnetServer.mpp" #define prefix_ @@ -39,34 +40,64 @@ namespace { - class MyTelnet : public senf::term::TelnetTerminal + class MyEditor + : public senf::term::BaseEditor { public: - explicit MyTelnet(Handle handle) : senf::term::BaseTelnetProtocol(handle) {} - + MyEditor(senf::term::AbstractTerminal & terminal) + : BaseEditor(terminal) {} + private: virtual void v_keyReceived(keycode_t key) { SENF_LOG(("Key " << senf::term::KeyParser::describe(key))); + if (key >= ' ' && key < 256) + insertChar(key); + else + switch (key) { + case '\r': + newline(); + break; + case senf::term::KeyParser::Left: + { + unsigned c (currentColumn()); + if (c > 0) + toColumn(c-1); + break; + } + case senf::term::KeyParser::Backspace: + { + unsigned c (currentColumn()); + if (c > 0) { + toColumn(c-1); + deleteChar(); + } + break; + } + case senf::term::KeyParser::Delete: + deleteChar(); + break; + } } + }; + + class MyTelnet + : public senf::term::TelnetTerminal + { + public: + explicit MyTelnet(Handle handle) + : senf::term::BaseTelnetProtocol(handle), + editor_(*this) {} + virtual void v_eof() { SENF_LOG(("EOF")); delete this; } - virtual void v_setupComplete() - { - TelnetTerminal::v_setupComplete(); - SENF_LOG(("Terminal type is '" << terminalType() << "', window size is " - << width() << "x" << height())); - } - - virtual void v_windowSizeChanged() - { - SENF_LOG(("New window size: " << width() << "x" << height())); - } + private: + MyEditor editor_; }; typedef senf::TCPv4ServerSocketHandle ServerHandle; diff --git a/senfscons/senfutil.py b/senfscons/senfutil.py index af58a20..d9f67ed 100644 --- a/senfscons/senfutil.py +++ b/senfscons/senfutil.py @@ -9,10 +9,12 @@ from SCons.Script import * def SetupForSENF(env): env.Append( LIBS = [ 'senf', 'readline', 'rt', '$BOOSTREGEXLIB', - '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB' ], + '$BOOSTIOSTREAMSLIB', '$BOOSTSIGNALSLIB', + '$BOOSTFSLIB' ], BOOSTREGEXLIB = 'boost_regex', BOOSTIOSTREAMSLIB = 'boost_iostreams', BOOSTSIGNALSLIB = 'boost_signals', + BOOSTFSLIB = 'boost_filesystem', CXXFLAGS = [ '-Wno-long-long', '${"$CXXFLAGS_"+(final and "final" or "debug")}', '${profile and ("-g","-pg") or None}' ],