From: g0dil Date: Fri, 9 Jan 2009 13:54:35 +0000 (+0000) Subject: Utils/Termlib: Documentation X-Git-Url: http://g0dil.de/git?a=commitdiff_plain;h=733d6e35453c9df1b281b24d2b0af8538ca9ccf7;p=senf.git Utils/Termlib: Documentation git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1053 270642c3-0616-0410-b53a-bc976706d245 --- diff --git a/Utils/Termlib/AbstractTerminal.hh b/Utils/Termlib/AbstractTerminal.hh index f38a4b5..22b3e0c 100644 --- a/Utils/Termlib/AbstractTerminal.hh +++ b/Utils/Termlib/AbstractTerminal.hh @@ -35,24 +35,44 @@ namespace senf { namespace term { + /** \brief Abstract terminal interface + + This abstract interface base class provides an abstract interface to a terminal. There are + two parts to this interface: + + \li The interface which allows the terminal user to get information about the terminal + \li The interface which allows the terminal to send messages to the terminal user + + The first part is implemented by providing abstract virtual members in AbstractTerminal. To + allow the terminal to send messages to the terminal user, the terminal user implements the + AbstractTerminal::Callbacks interface. The terminal user must register himself with the + AbstractTerminal by calling setCallbacks(). Afterwards, the AbstractTerminal implementation + will send calls to the terminal user via the AbstractTerminal::Callbacks API. + */ struct AbstractTerminal { + /** \brief AbstractTerminal callbacks + + \see AbastractTerminal + */ struct Callbacks { virtual ~Callbacks() {} - virtual bool cb_init() = 0; - virtual void cb_charReceived(char ch) = 0; - virtual void cb_windowSizeChanged() = 0; + virtual bool cb_init() = 0; ///< Called after terminal initialization is complete + /**< This member may return \c false. In this case, the + terminal setup is considered to have failed. */ + virtual void cb_charReceived(char ch) = 0; ///< Called whenever a char is received + virtual void cb_windowSizeChanged() = 0; ///< Called when the terminal window is changed }; virtual ~AbstractTerminal() {} - virtual void setCallbacks(Callbacks & cb) = 0; + virtual void setCallbacks(Callbacks & cb) = 0; /// Register terminal callbacks - virtual std::string terminalType() = 0; - virtual unsigned width() = 0; - virtual unsigned height() = 0; + virtual std::string terminalType() = 0; ///< Get the terminal type + virtual unsigned width() = 0; ///< Get current terminal window width + virtual unsigned height() = 0; ///< Get current terminal window height - virtual void write(char ch) = 0; + virtual void write(char ch) = 0; ///< Write character to terminal }; }} diff --git a/Utils/Termlib/Doxyfile b/Utils/Termlib/Doxyfile index b7b820c..77c744b 100644 --- a/Utils/Termlib/Doxyfile +++ b/Utils/Termlib/Doxyfile @@ -4,6 +4,7 @@ PROJECT_NAME = libTermlib GENERATE_TAGFILE = doc/Console.tag EXAMPLE_PATH = . EXCLUDE = telnetServer.cc +ALPHABETICAL_INDEX = NO TAGFILES = \ "$(TOPDIR)/Socket/doc/Socket.tag" \ diff --git a/Utils/Termlib/Editor.hh b/Utils/Termlib/Editor.hh index e173a29..8646d4a 100644 --- a/Utils/Termlib/Editor.hh +++ b/Utils/Termlib/Editor.hh @@ -41,6 +41,22 @@ namespace senf { namespace term { + /** \brief Provide editor support terminal functionality + + This base class utilizes an arbitrary AbstractTerminal and provides terminfo based + terminal navigation and key parsing. + + All navigation is relative to the current line. The display area can then be extended + downwards up to a maximum of height() lines. Resetting this extended display area will + return to a one line area containing only the top line. + + All navigation is restricted to a width() x height() area. The last column may not be + written to since auto-margin terminals will move the cursor to the next line when writing to + that column. + + This base class calls v_keyReceived() which must be defined in a derived class whenever a + key is fully parsed. + */ class BaseEditor : public AbstractTerminal::Callbacks { @@ -69,12 +85,16 @@ namespace term { unsigned height(); ///< Return current screen height protected: - virtual bool cb_init(); + virtual bool cb_init(); ///< Called when terminal is initialized virtual void cb_windowSizeChanged(); + ///< Called whenever the terminal window size changes +#ifndef DOXYGEN private: - virtual void v_keyReceived(keycode_t key) = 0; +#endif + virtual void v_keyReceived(keycode_t key) = 0; ///< Called whenever a key is received + private: virtual void cb_charReceived(char c); void keySequenceTimeout(); @@ -93,7 +113,88 @@ namespace term { unsigned displayHeight_; unsigned line_; }; + + /** \brief Single line interactive text editor + + LineEditor implements a single-line input widget on an arbitrary AbstractTerminal. + + \li It supports all the customary editing functions + \li It is possible to arbitrarily assign functions to keys via a key map + \li LineEditor has builtin TAB completion support + \li The LineEditor has a built-in history + \li The LineEditor supports an arbitrary auxiliary display area below the input line + \li The LineEditor has hide() / show() support to allow editing to be temporarily + interrupted. + + The LineEditor will query the user for an input line. When the user accepts a line, + LineEditor will call a user callback function. After the callback has been called, the + editor is disabled. To accept a new input line, call show(). + + \section editor_keymap The editor key map + + Keys are defined in the keymap using defineKey(). The default bindings are: + + + + + + + + + + + + + + + + + +
\c Return bindings::accept
\c Right bindings::forwardChar
\c Left bindings::backwardChar
\c Up bindings::prevHistory
\c Down bindings::nextHistory
\c Backspace bindings::backwardDeleteChar
\c Delete bindings::deleteChar
\c Home bindings::beginningOfLine
\c End bindings::endOfLine
\c Ctrl-K bindings::deleteToEndOfLine
\c Ctrl-A bindings::beginningOfLine
\c Ctrl-E bindings::endOfLine
\c Ctrl-D bindings::deleteChar
\c Ctrl-C bindings::restartEdit
\c Ctrl-L bindings::clearScreen
+ + See the senf::term::bindings namespace for a list of all default provided key binding + functions. + + + \section editor_complete Completion suppoprt + + Completion support is provided by senf::term::bindings::complete(). To use the completer, + you need to implement a completion function and pass it as second argument to + bindings::complete(): + + \code + void myCompleter(senf::term::LineEditor & editor, unsigned b, unsigned e, + std::vector & completions) + { + // Get text to complete + std::string text (editor.text().substr(b, e-b)); + + // Return possible completions in 'completions' array + completions.push_back( ... ); + } + + senf::term::LineEditor editor (...); + editor.defineKey(senf::term::KeyParser::TAB, + boost::bind(&senf::term::bindings::complete, _1, &myCompleter)); + \endcode + + When \c myCompleter is a class member, use senf::membind() and pass this instead of \c + &myCompleter to \c boost::bind() and thus to senf::term::bindings::complete. + + + \section editor_auxarea The aux display area + + The auxiliary display area is accessed using auxDisplay() and clearAuxDisplay(). The aux + display area is \e cleared \e before each new key is processed. Therefore it is only + temporary. The aux display area however will survive hide() / show(). + + \section editor_hideshow Temporarily disabling the editor + + Calling hide() will temporarily disable the editor. All editor display will be + removed. Calling show() will redisplay the editor in it's current state including the aux + display area. + */ class LineEditor : public BaseEditor { @@ -101,56 +202,95 @@ namespace term { /////////////////////////////////////////////////////////////////////////// // Types - typedef boost::function KeyBinding; + typedef boost::function KeyBinding; ///< Type of a key binding function typedef boost::function AcceptCallback; + ///< Callback function type static unsigned const MAX_HISTORY_SIZE = 1024u; /////////////////////////////////////////////////////////////////////////// LineEditor(AbstractTerminal & terminal, AcceptCallback cb); + ///< Create a LineEditor + /**< \parm[in] terminal abstract terminal interface + \parm[in] cb callback to call for complete input + line */ /////////////////////////////////////////////////////////////////////////// - void prompt(std::string const & text); + void prompt(std::string const & text); ///< Set prompt string void set(std::string const & text, unsigned pos = 0u); + ///< Set edit buffer contents + /**< The edit buffer contents will be replaced by \a + text. The cursor will be placed at position \a pos + within this text. */ - // Overall edit control - void show(); - void hide(); - void accept(); - void clear(); - void redisplay(); - void forceRedisplay(); - - // Cursor and display movement - void gotoChar(unsigned n); - void scrollTo(unsigned n); + ///\name Overall edit control + ///\{ + + void show(); ///< Enable editor widget + void hide(); ///< Disable editor widget + void accept(); ///< Accept current user input and call the accept callback + void clear(); ///< Clear editor buffer + void redisplay(); ///< Mark the editor buffer for redisplay + void forceRedisplay(); ///< Redisplay the editor buffer \e now + + ///\} + + ///\name Cursor and display movement + ///\{ + + void gotoChar(unsigned n); ///< Move cursor to position \a n + void scrollTo(unsigned n); ///< Move positon \n to beginning of display line + + ///\} + + ///\name Text manipulation + ///\{ + + void deleteChar(unsigned n=1); ///< Delete \a n characters at point + void insert(char ch); ///< Insert \a ch at point + void insert(std::string const & text); ///< Insert \a text at point - // Text manipulation - void deleteChar(unsigned n=1); - void insert(char ch); - void insert(std::string const & text); + ///\} - // History - void pushHistory(std::string const & text); - void prevHistory(); - void nextHistory(); + ///\name History + ///\{ + + void pushHistory(std::string const & text); ///< Add string \a text to history + void prevHistory(); ///< Switch to previous history entry + void nextHistory(); ///< Switch to next history entry + + ///\} + + ///\name Aux Display + ///\{ - // Aux Display void auxDisplay(int line, std::string const & text); - unsigned maxAuxDisplayHeight(); - void clearAuxDisplay(); + ///< Display \a text on aux display line \a lilne + unsigned maxAuxDisplayHeight(); ///< Get maximum height of the aux display area + void clearAuxDisplay(); ///< Clear the aux display area + + ///\} + + ///\name Get information + ///\{ - // Get information - std::string const & text(); - unsigned point(); - unsigned displayPos(); - keycode_t lastKey(); + std::string const & text(); ///< Get current editor buffer contents + unsigned point(); ///< Get current cursor position + unsigned displayPos(); ///< Get current display position + keycode_t lastKey(); ///< Get last key code received + + ///\} + + ///\name Key bindings + ///\{ - // Key bindings void defineKey(keycode_t key, KeyBinding binding); - void unsetKey(keycode_t key); + ///< Bind key \a key to \a binding + void unsetKey(keycode_t key); ///< Remove all bindings for \a key + + ///\} private: virtual bool cb_init(); @@ -177,24 +317,33 @@ namespace term { AuxDisplay auxDisplay_; }; +/** \brief LineEditor key bindings + */ namespace bindings { - void selfInsertCommand (LineEditor & editor); - void forwardChar (LineEditor & editor); - void backwardChar (LineEditor & editor); - void accept (LineEditor & editor); - void backwardDeleteChar (LineEditor & editor); - void deleteChar (LineEditor & editor); - void beginningOfLine (LineEditor & editor); - void endOfLine (LineEditor & editor); - void deleteToEndOfLine (LineEditor & editor); - void restartEdit (LineEditor & editor); - void prevHistory (LineEditor & editor); - void nextHistory (LineEditor & editor); - void clearScreen (LineEditor & editor); + void selfInsertCommand (LineEditor & editor); ///< Insert key as literal character + void forwardChar (LineEditor & editor); ///< Move one char forward + void backwardChar (LineEditor & editor); ///< Move one char backwards + void accept (LineEditor & editor); ///< Accept input line + void backwardDeleteChar (LineEditor & editor); ///< Delete char before cursor + void deleteChar (LineEditor & editor); ///< Delete char at cursor + void beginningOfLine (LineEditor & editor); ///< Move to beginning of line + void endOfLine (LineEditor & editor); ///< Move to end of line + void deleteToEndOfLine (LineEditor & editor); ///< Delete from cursor to end of line + void restartEdit (LineEditor & editor); ///< Clear edit buffer and restart edit + void prevHistory (LineEditor & editor); ///< Move to previous history entry + void nextHistory (LineEditor & editor); ///< Move to next history entry + void clearScreen (LineEditor & editor); ///< Clear screen and redisplay editor typedef boost::function &)> Completer; void complete (LineEditor & editor, Completer completer); + ///< Complete text at cursor + /**< This function calls \a completer to find the list of + possible completions for the text between \a b and \a e + (as passed to the completer). The completer must add + all possible completions to the completions vector. + + \see \ref editor_complete */ } diff --git a/Utils/Termlib/Mainpage.dox b/Utils/Termlib/Mainpage.dox new file mode 100644 index 0000000..8ca4408 --- /dev/null +++ b/Utils/Termlib/Mainpage.dox @@ -0,0 +1,58 @@ +// $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. + +/** \mainpage Terminal utility functions + + The Termlib module provides several facilities to help working with terminal I/O + + \autotoc + + \section termlib_sect_telnet Telnet protocol implementation + \seechapter \ref telnet_group + + This is a partial implementation of the telnet protokoll. It provides enough functionality to + provide terminal access. + + \section termlib_sect_terminfo Terminfo database access + \seechapter \ref terminfo_group + + The senf::term::Terminfo class provides access to the termios database. + + \section termlib_sect_editor Simple line-oriented text editor + \seechapter senf::term::LineEditor + + Using the termios functionality, this class implements simple line editing + + */ + + +// 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" +// mode: flyspell +// mode: auto-fill +// End: diff --git a/Utils/Termlib/Telnet.cc b/Utils/Termlib/Telnet.cc index d962808..23e6984 100644 --- a/Utils/Termlib/Telnet.cc +++ b/Utils/Termlib/Telnet.cc @@ -55,7 +55,9 @@ prefix_ senf::term::BaseTelnetProtocol::BaseTelnetProtocol() pendingRequests_ (0u), requestTimeout_ (ClockService::milliseconds(DEFAULT_REQUEST_TIMEOUT_MS)), timeout_ ("senf::term::BaseTelnetProtocol::timeout", 0) -{} +{ + SENF_ASSERT( false ); +} prefix_ void senf::term::BaseTelnetProtocol::write(std::string const & s) { diff --git a/Utils/Termlib/Telnet.hh b/Utils/Termlib/Telnet.hh index 4cc6af4..e127a95 100644 --- a/Utils/Termlib/Telnet.hh +++ b/Utils/Termlib/Telnet.hh @@ -39,13 +39,99 @@ namespace senf { namespace term { - /** \brief Telnet server + /** \defgroup telnet_group Telnet protocol - \see - RFC 854 The Telnet protocol \n - RFC 855 Telnet option specifications + The telnet protocol implementation is based on the relevant RFCs: + + \li RFC 854 The Telnet protocol + \li RFC 855 Telnet option specifications + + BaseTelnetProtocol provides the basic protocol implementation with generic telnet option + support. Several other classes provide additional support for further telnet options: + + \li telnethandler::TerminalType provides support for the TERMINAL_TYPE option + \li telnethandler::NAWS provides support for the NAWS (Negotiation About Window Size) option + + There are some symbolic constants for telnet option in the senf::term::telnetopt namespace. + */ + + /** \brief Telnet protocol implementation + + This class implements the basic telnet protocol. It provides option parsing and negotiation + and implements the necessary character quoting. + + This class is a base class. Several virtual function hooks are provided which will be called + by the protocol implementation. + + The telnet protocol implementation is symmetric: At this level, there is no difference + between a telnet client and a telnet server. + + This telnet protocol base class integrates with the SENF Scheduler, it relies on the + scheduler main loop to be active. + + \section telnethandlers Telnet option handlers + + To support complex telnet option which require subnegotiation, TelnetHandler's are + provided. A telnet handler is a class derived from TelnetHandler and then inherited into the + target telnet implementation class: + + \code + class MyTelnet + : public senf::term::telnethandler::TerminalType, + public senf::term::telnethandler::NAWS + { + MyTelnet(Handle handle) : BaseTelnetProtocol(handle) {} + + // ... + }; + \endcode + + It is very important, the BaseTelnetProtocol is \e always inherited by \c public \c virtual + inheritance. This allows the BaseTelnetProtocol constructor to be called by the most derived + class directly as above. + + Each terminal handler may provide an additional API and/or additional virtual function + callbacks. + + To implement a new terminal handler, derive from TelnetHandler and register the + handler. BaseTelnetProtocol will automatically request the corresponding option to be + enabled and will call the handlers \c v_init() member as soon as the peer as asserted the + option. + + Whenever a subnegotiation for the registered handler is received, the handlers + \c v_handleOptionParameters() member is called. + + \code + class MyTelnetHandler + : public senf::term::BaseTelnetProtocol::TelnetHandler + { + public: + // This constant is MANDATORY and gives the option code which this handler services + static option_type const OPTION_CODE = MY_OPTION_CODE; + + void frobble() { // ... } + + protected: + MyTelnetHandler() { registerHandler(this); } + + private: + virtual void v_init() + { + sendOptionParameters(OPTION_CODE, "my special subnegotiation"); + incrementRequestCounter(); + } + + virtual void v_handleOptionParameters(std::string const & data) + { + if (data == "another special subnegotiation") + decrementRequestCounter(); + } + }; + \endcode \todo SYNCH handling + + \ingroup telnet_group */ class BaseTelnetProtocol { @@ -56,68 +142,101 @@ namespace term { ConnectedCommunicationPolicy, StreamFramingPolicy, ReadablePolicy, - WriteablePolicy>::policy> Handle; + WriteablePolicy>::policy> Handle; ///< Type of socket handle required - typedef unsigned char option_type; + typedef unsigned char option_type; ///< Type of telnet option numbers struct TelnetHandler; - void write(std::string const & s); - void write(char c); - - Handle handle(); - - void sendNOP(); - void sendBRK(); - void sendIP(); - void sendAO(); - void sendAYT(); - void sendEC(); - void sendEL(); - void sendGA(); + void write(std::string const & s); ///< Send string to peer + /**< The string will be correctly quoted and newline chars + will be sent as CR/LF pairs. */ + void write(char c); ///< Send single character to peer + /**< The character will be correctly quoted and newline + chars will be sent as CR/LF pairs. */ + + Handle handle(); ///< Get socket handle + + void sendNOP(); ///< Send NOP to peer + void sendBRK(); ///< Send BReaK to peer + void sendIP(); ///< Send InterruptProcess to peer + void sendAO(); ///< Send AbortOutput to peer + void sendAYT(); ///< Send AreYouThere to peer + void sendEC(); ///< Send EraseCharacter to peer + void sendEL(); ///< Send EraseLine to peer + void sendGA(); ///< Send GoAhead to peer void sendOptionParameters(option_type option, std::string const & data); + ///< Send extended option parameter to peer + /**< This will send \a data as extended option parameter of + option \a option to peer via a subnegotiation. */ void requestLocalOption(option_type option, bool enabled = true); + ///< Request option to be enabled here + /**< This will send a WILL \a option request to the peer */ void acceptLocalOption(option_type option, bool enabled = true); + ///< Accept a request for an option to be enabled here + /**< If the peer sends a DO \a option request, the request + will be granted */ void requestPeerOption(option_type option, bool enabled = true); + ///< Request peer to enable an option + /**< This will send a DO \a option request to the peer */ void acceptPeerOption(option_type option, bool enabled = true); + ///< Accept a request by the peer to enable an option + /**< If the peer sends a WILL \a option request, the request + will be ganted */ - bool localOption(option_type option); - bool peerOption(option_type option); + bool localOption(option_type option); ///< \c true, if \a option locally enabled + bool peerOption(option_type option); ///< \c true, if \a option enabled in peer protected: - explicit BaseTelnetProtocol(Handle handle); - BaseTelnetProtocol(); + explicit BaseTelnetProtocol(Handle handle); ///< Construct telnet protocol handler + BaseTelnetProtocol(); ///< Provided for TelnetHandler mixins only + virtual ~BaseTelnetProtocol(); template void registerHandler(Handler * h, bool request=true); + ///< Register a TelnetHandler - void incrementRequestCounter(); - void decrementRequestCounter(); - bool requestsPending(); + void incrementRequestCounter(); ///< Increment request counter + /**< This member may be called by a telnet handler to wait + for additional negotiations. It must be paired with a + corresponding decrementRequestCounter() call when that + negotiation is received. */ + void decrementRequestCounter(); ///< Decrement request counter + /**< \see inrementRequestCounter() */ + bool requestsPending(); ///< \c true, if there are pending requests #ifndef DOXYGEN private: #endif - virtual void v_setupComplete() = 0; - virtual void v_charReceived(char c) = 0; - virtual void v_eof() = 0; - - virtual void v_handleNOP(); - virtual void v_handleBRK(); - virtual void v_handleIP(); - virtual void v_handleAO(); - virtual void v_handleAYT(); - virtual void v_handleEC(); - virtual void v_handleEL(); - virtual void v_handleGA(); + virtual void v_setupComplete() = 0; ///< Called, when no further requests are pending + /**< This callback will be called, when no further + negotiations are to be expected. The default + negotiation timeout is 500ms. If no reply is received + in this time, the request is abandoned and + v_setupComplete() is called. + + mixin TelnetHandler's may additionally increment the + request counter to wait for specific + subnegotiations. v_setupComplete() will be called, when + all these negotiations are complete or have timed + out. */ + virtual void v_charReceived(char c) = 0; ///< Called whenever a data character is received + virtual void v_eof() = 0; ///< Called on input EOF + + virtual void v_handleNOP(); ///< Called, when the peer sends a NOP + virtual void v_handleBRK(); ///< Called, when the peer sends a BReaK + virtual void v_handleIP(); ///< Called, when the peer sends an InterruptProcess + virtual void v_handleAO(); ///< Called, when the peer sends an AbortOutput + virtual void v_handleAYT(); ///< Called, when the peer sends an AreYouThere + virtual void v_handleEC(); ///< Called, when the peer sends an EraseCharacter + virtual void v_handleEL(); ///< Called, when the peer sends an EraseLine + virtual void v_handleGA(); ///< Called, when the peer sends a GoAhead -#ifdef DOXYGEN private: -#endif void handleChar(char c); void handleNormalChar(char c); void handleCommand(char c); @@ -212,15 +331,25 @@ namespace term { friend class TelnetHandler; }; + /** \brief Telnet handler base class + + \see BaseTelnetProtocol + */ struct BaseTelnetProtocol::TelnetHandler : public virtual BaseTelnetProtocol { virtual ~TelnetHandler(); - virtual void v_init() = 0; + virtual void v_init() = 0; ///< Called, after option has been enabled virtual void v_handleOptionParameters(std::string const & data) = 0; + ///< Called, whenever a subnegotiation is received }; - // See http://www.iana.org/assignments/telnet-options for a list of options + /** \brief Telnet option codes + + See http://www.iana.org/assignments/telnet-options for a list of options + + \ingroup telnet_group + */ namespace telnetopt { BaseTelnetProtocol::option_type const ECHO = 1u; } namespace telnetopt { BaseTelnetProtocol::option_type const TRANSMIT_BINARY = 0u; } namespace telnetopt { BaseTelnetProtocol::option_type const SUPPRESS_GO_AHEAD = 3u; } @@ -228,16 +357,32 @@ namespace term { namespace telnetopt { BaseTelnetProtocol::option_type const NAWS = 31u; } namespace telnetopt { BaseTelnetProtocol::option_type const LINEMODE = 34u; } +/** \brief Telnet handlers + + \ingroup telnet_group */ namespace telnethandler { + /** \brief Implement TERMINAL_TYPE option + + This telnet handler implements the TERMINAL_TYPE option. The handler automatically requests + the first terminal type during initialization. Further terminal types may then be reqeusted + by calling nextTerminalType(). + + The last received terminal type will be returned by the terminalType() member. + + This implementation only provides server support (querying the terminal type). + + \see BaseTelnetProtocol for how to integrate this handler \n + RFC 1091 Telnet Terminal-Type Option + */ class TerminalType : public BaseTelnetProtocol::TelnetHandler { public: static option_type const OPTION_CODE = telnetopt::TERMINAL_TYPE; - void nextTerminalType(); - std::string const & terminalType() const; + void nextTerminalType(); ///< Request another terminal type + std::string const & terminalType() const; ///< Return current terminal type protected: TerminalType(); @@ -249,21 +394,38 @@ namespace telnethandler { std::string type_; }; + /** \brief Implement NAWS (Negotiation About Window Size) option + + This telnet handler implements the NAWS option. The client terminals window size will be + requested during initialization. The current window size may always be accessed using the + width() and height() members. + + Whenever the window size is changed, the v_windowSizeChanged() function is called. This + function must be implemented in a derived class. + + \see BaseTelnetProtocol for how to integrate this handler \n + RFC 1073 Telnet Window Size Option + */ class NAWS : public BaseTelnetProtocol::TelnetHandler { public: static option_type const OPTION_CODE = telnetopt::NAWS; - unsigned width() const; - unsigned height() const; + unsigned width() const; ///< Get current client window width + unsigned height() const; ///< Get current client window height protected: NAWS(); +# ifndef DOXYGEN private: - virtual void v_windowSizeChanged() = 0; +# endif + virtual void v_windowSizeChanged() = 0; ///< Called, whenever window size changes + /**< This member is called for every window size change \e + after initialization. */ + private: virtual void v_init(); virtual void v_handleOptionParameters(std::string const & data); diff --git a/Utils/Termlib/TelnetTerminal.hh b/Utils/Termlib/TelnetTerminal.hh index ed2be42..8d31583 100644 --- a/Utils/Termlib/TelnetTerminal.hh +++ b/Utils/Termlib/TelnetTerminal.hh @@ -36,6 +36,18 @@ namespace senf { namespace term { + /** \brief AbstractTerminal interface implementation based on telnet + + This class provides a telnet server implementation implementing the AbstractTerminal + interface. + + TelnetTerminal provides one additional callback which needs to be implemented in a derived + class: v_setupFailed(). This member will be called, when not all required telnet options are + supported by the telnet client. In this case, the communication will be switched back into + line-oriented mode and v_setupFailed() is called. + + \ingroup telnet_group + */ class TelnetTerminal : public telnethandler::TerminalType, public telnethandler::NAWS, @@ -44,15 +56,23 @@ namespace term { public: TelnetTerminal(); + ///\name AbstractTerminal interface implementation + ///\{ virtual void setCallbacks(AbstractTerminal::Callbacks & cb); virtual std::string terminalType(); virtual unsigned width(); virtual unsigned height(); virtual void write(char ch); + ///\} + protected: + +# ifndef DOXYGEN private: - virtual void v_setupFailed() = 0; +# endif + virtual void v_setupFailed() = 0; ///< Called when the telnet setup has failed + private: virtual void v_setupComplete(); virtual void v_charReceived(char ch); virtual void v_windowSizeChanged(); diff --git a/Utils/Termlib/Terminfo.hh b/Utils/Termlib/Terminfo.hh index 616f374..2413e66 100644 --- a/Utils/Termlib/Terminfo.hh +++ b/Utils/Termlib/Terminfo.hh @@ -41,16 +41,44 @@ namespace senf { namespace term { + /** \defgroup terminfo_group Terminfo + + This facility provides access to the terminfo database. It partially re-implements the + terminfo suppoprt in \c libncurses. + + \li The senf::term::Terminfo class provides basic terminfo property access and supports terminfo string + formatting + \li The senf::term::KeyParser class uses the properties found in a Terminfo instance to + parser keyboard escape sequences into key codes + + The terminfo implementation is based on the \c utio library by Mike Sharov + found at http://sourceforge.net/projects/utio. + */ + + /** \brief Terminfo database entry + + This class reads a single terminfo database entry and allows to access the terminfo + properties. + + \ingroup terminfo_group + */ class Terminfo { public: /////////////////////////////////////////////////////////////////////////// // Types + /** \brief NoValue constant + This value represents the absence of a numeric property value */ enum { NoValue = -1 }; + /** \brief Terminfo property constants + + This class provides a namespace for all Terminfo property constants + */ struct properties { + /** \brief Boolean terminfo properties */ enum Boolean { AutoLeftMargin, AutoRightMargin, NoEscCtlc, CeolStandoutGlitch, EatNewlineGlitch, EraseOverstrike, GenericType, HardCopy, HasMetaKey, HasStatusLine, InsertNullGlitch, @@ -63,8 +91,11 @@ namespace term { NoCorrectlyWorkingCr, GnuHasMetaKey, LinefeedIsNewline, HasHardwareTabs, ReturnDoesClrEol }; + /** \brief Boolean property names + \hideinitializer */ static char const * const BooleanNames[]; - + + /** \brief Numeric terminfo properties */ enum Numeric { Columns, InitTabs, Lines, LinesOfMemory, MagicCookieGlitch, PaddingBaudRate, VirtualTerminal, WidthStatusLine, NumLabels, LabelHeight, LabelWidth, MaxAttributes, @@ -75,8 +106,11 @@ namespace term { MagicCookieGlitchUl, CarriageReturnDelay, NewLineDelay, BackspaceDelay, HorizontalTabDelay, NumberOfFunctionKeys }; + /** \brief Numeric property names + \hideinitializer */ static char const * const NumericNames[]; - + + /** \brief String terminfo properties */ enum String { BackTab, Bell, CarriageReturn, ChangeScrollRegion, ClearAllTabs, ClearScreen, ClrEol, ClrEos, ColumnAddress, CommandCharacter, CursorAddress, CursorDown, @@ -142,35 +176,43 @@ namespace term { AcsLlcorner, AcsUrcorner, AcsLrcorner, AcsLtee, AcsRtee, AcsBtee, AcsTtee, AcsHline, AcsVline, AcsPlus, MemoryLock, MemoryUnlock, BoxChars1 }; + /** \brief String property names + \hideinitializer */ static char const * const StringNames[]; }; - typedef boost::int16_t number_t; - typedef char const* string_t; + typedef boost::int16_t number_t; ///< Numeric terminfo property type + typedef char const* string_t; ///< String terminfo property type /////////////////////////////////////////////////////////////////////////// Terminfo(); - explicit Terminfo(std::string const & term); - void load(std::string const & term); + explicit Terminfo(std::string const & term); ///< Load terminfo entry \a term + void load(std::string const & term); ///< Load terminfo entry \a 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; + bool getFlag(properties::Boolean p) const; ///< Get boolean property value + number_t getNumber(properties::Numeric p) const; ///< Get numeric property value + string_t getString(properties::String p) const; ///< Get string property value + /**< If the property does not exist, 0 is returned */ + bool hasProperty(properties::Boolean p) const; ///< \c true, if boolean property \a p exists + bool hasProperty(properties::Numeric p ) const; ///< \c true, if numeric property \a p exists + bool hasProperty(properties::String p ) const; ///< \c true, if string property \a p exists + 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; + ///< Format string property value + /**< Formats the string property \a p containing special + terminfo codes. Terminfo supports up to 9 parameters. */ /////////////////////////////////////////////////////////////////////////// - void dump(std::ostream & os) const; + void dump(std::ostream & os) const; ///< Dump a description of the terminfo entry + /** \brief Invalid, incomplete or non-existent terminfo entry exception */ struct InvalidTerminfoException : public senf::Exception { InvalidTerminfoException() : senf::Exception("Unreadable terminfo file") {} }; @@ -190,13 +232,23 @@ namespace term { StringPool stringPool_; }; + /** \brief Parse key escape sequences + The KeyParser will read the relevant properties from a terminfo entry and then provides + functions to parser keyboard escape sequences. + + All keys are returned as keyboard code's. Values 0 to 255 represent ordinary ASCII + characters, larger values are special keys taken from the KeyCode \c enum + + \ingroup terminfo_group + */ class KeyParser { public: /////////////////////////////////////////////////////////////////////////// // Types + /** \brief Special keyboard key codes */ enum KeyCode { Space = ' ', Tab = '\t', Return = '\r', First = 0xE000, Esc = First, Backspace, Backtab, Begin, CATab, CTab, Cancel, Center, Clear, ClearToEOL, ClearToEOS, Close, Command, Copy, @@ -213,23 +265,46 @@ namespace term { ShiftRedo, ShiftReplace, ShiftResume, ShiftRight, ShiftSave, ShiftSuspend, ShiftTab, ShiftUndo, Suspend, Undo, Up, UpLeft, UpRight, Incomplete = 0xE0FF }; + /** \brief Special key code names + \hideinitializer */ static char const * const KeyNames[]; - typedef wchar_t keycode_t; - typedef std::string::size_type size_type; + typedef wchar_t keycode_t; ///< Key code data type + typedef std::string::size_type size_type; ///< String length type + /** \brief Helper to convert uppercase char to Control key code */ static keycode_t Ctrl(char ch) { return ch-'@'; } /////////////////////////////////////////////////////////////////////////// KeyParser(); - explicit KeyParser(Terminfo const & ti); - void load(Terminfo const & ti); + explicit KeyParser(Terminfo const & ti); ///< Load keymap information from \a ti + void load(Terminfo const & ti); ///< Load keymap information from \a ti std::pair lookup(std::string const & key) const; - static std::string describe(keycode_t key); - - void dump(std::ostream & os) const; + ///< Lookup up string \a key + /**< This call will look up the string in \a key in the + keymap. The return value is a pair. + \li the first value is the key code + \li the second value is the number of characters in \a + key which have been interpreted and returned in the + first value + + If \a key is not a complete key sequence, the return + key code will be the \c Invalid value and the returned + escape sequence size will be 0. + + If \a key does not start with an escape sequence, the + returned key code will be the first character from \a + key and the escape sequence size will be 1. + + Otherwise \a key starts with a complete escape + sequence. The returned key code will be a value from + the KeyCode enum and the escape sequence size will be + returned accordingly. */ + static std::string describe(keycode_t key); ///< Return descriptive, printable key name + + void dump(std::ostream & os) const; ///< Dump keymap for debug purposes private: typedef std::map Keytable;