Sun, 14 Dec 2014 23:21:38 +0200
- the interface is now able to connect to arbitrary hosts (uses ^N to start the prompt) instead of the hardcoded localhost:10666
--- a/sources/interface.cpp Sun Dec 14 20:47:44 2014 +0200 +++ b/sources/interface.cpp Sun Dec 14 23:21:38 2014 +0200 @@ -31,9 +31,18 @@ #include <string.h> #include "interface.h" #include "network/rconsession.h" +#include "network/ipaddress.h" enum { PAGE_SIZE = 10 }; +enum InputState +{ + INPUTSTATE_NORMAL, + INPUTSTATE_ADDRESS, + INPUTSTATE_PASSWORD, + INPUTSTATE_CONFIRM_DISCONNECTION, +}; + static String g_input; static int g_cursor = 0; static int g_pan = 0; @@ -46,6 +55,8 @@ static Vector<String> g_output = {""}; static int g_outputScroll = 0; static String g_title; +static InputState g_inputState = INPUTSTATE_NORMAL; +static IPAddress g_address; // ------------------------------------------------------------------------------------------------- // @@ -57,6 +68,24 @@ // ------------------------------------------------------------------------------------------------- // +static FUNCTION +interface_prompt_string() -> String +{ + String prompt; + + switch (g_inputState) + { + case INPUTSTATE_NORMAL: prompt = ">"; break; + case INPUTSTATE_ADDRESS: prompt = "address:"; break; + case INPUTSTATE_PASSWORD: prompt = "password:"; break; + case INPUTSTATE_CONFIRM_DISCONNECTION: break; + } + + return prompt; +} + +// ------------------------------------------------------------------------------------------------- +// FUNCTION Interface::initialize() -> void { @@ -153,8 +182,19 @@ static FUNCTION interface_render_input() -> void { - static char prompt[] = ">"; - int displaylength = COLS - strlen (prompt) - 2; + int pair = interface_color_pair (WHITE, BLUE); + + if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) + { + attron (pair); + mvhline (LINES - 2, 0, ' ', COLS); + mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n"); + attroff (pair); + return; + } + + String prompt = interface_prompt_string(); + int displaylength = COLS - prompt.length() - 2; int y = LINES - 2; // Ensure the cursor is within bounds @@ -170,27 +210,17 @@ int end = min<int> (g_input.length(), start + displaylength); assert (g_cursor >= start and g_cursor <= end); - // Clear, but only as much as is necessary. I want to avoid clearing the entire line to save - // bandwidth over SSH connections. Perhaps needlessly? I'm paranoid. - int renderLength = strlen (prompt) + 1 + g_input.length(); - static int lastRenderLength = 0; - - if (lastRenderLength > renderLength) - mvhline (y, renderLength, ' ', lastRenderLength - renderLength); - - lastRenderLength = renderLength; - // Render the input line, with the part of the input string that's before the cursor written // AFTER the part that comes afterwards. This is to ensure that the cursor is placed at the // position where our cursor is. It looks nice like that. :) - int pair = interface_color_pair (WHITE, BLUE); + mvhline (LINES - 2, 0, ' ', COLS); + mvprintw (y, prompt.length() + 1, "%s", g_input.chars()); attron (pair); - mvprintw (y, 0, "%s", prompt); + mvprintw (y, 0, "%s", prompt.chars()); attroff (pair); - mvprintw (y, strlen (prompt) + 1, "%s", g_input.chars()); g_cursorChar.ch = g_cursor != 0 ? g_input[g_cursor - 1] : '\0'; - g_cursorChar.x = strlen (prompt) + (g_cursor - g_pan); + g_cursorChar.x = prompt.length() + (g_cursor - g_pan); g_needRefresh = true; g_needInputRender = false; } @@ -237,7 +267,7 @@ if (g_cursorChar.ch != '\0') mvprintw (y, g_cursorChar.x, "%c", g_cursorChar.ch); else - mvprintw (y, 1, " "); + mvprintw (y, interface_prompt_string().length(), " "); } // ------------------------------------------------------------------------------------------------- @@ -248,6 +278,15 @@ int ch = ::getch(); set_statusbar_text (String::from_number (ch)); + if (g_inputState == INPUTSTATE_CONFIRM_DISCONNECTION) + { + if (ch == 'y' or ch == 'Y') + g_inputState = INPUTSTATE_ADDRESS; + else if (ch == 'n' or ch == 'N') + g_inputState = INPUTSTATE_NORMAL; + return; + } + if (ch >= 0x20 and ch <= 0x7E) { g_input.insert (g_cursor++, char (ch)); @@ -320,9 +359,65 @@ case '\n': case KEY_ENTER: - if (RCONSession::get_session()->send_command (g_input)) + switch (g_inputState) { + case INPUTSTATE_CONFIRM_DISCONNECTION: + break; // handled above + + case INPUTSTATE_ADDRESS: + try + { + g_address = IPAddress::from_string (g_input); + } + catch (std::exception& e) + { + print (e.what()); + return; + } + + if (g_address.port == 0) + g_address.port = 10666; + g_input.clear(); + g_inputState = INPUTSTATE_PASSWORD; + g_needInputRender = true; + + case INPUTSTATE_PASSWORD: + if (g_inputState == INPUTSTATE_PASSWORD and not g_input.is_empty()) + { + RCONSession* session = RCONSession::new_session(); + session->set_password (g_input); + session->connect (g_address); + g_input.clear(); + g_inputState = INPUTSTATE_NORMAL; + g_needInputRender = true; + } + break; + + case INPUTSTATE_NORMAL: + if (RCONSession::get_session() != nullptr + and RCONSession::get_session()->send_command (g_input)) + { + g_input.clear(); + g_needInputRender = true; + } + break; + } + break; + + case 'N' - 'A' + 1: // ^N + if (g_inputState == INPUTSTATE_NORMAL) + { + if (RCONSession::get_session() != nullptr + and RCONSession::get_session()->state() != RCON_DISCONNECTED) + { + g_inputState = INPUTSTATE_CONFIRM_DISCONNECTION; + } + else + { + g_inputState = INPUTSTATE_ADDRESS; + } + g_needInputRender = true; } break;
--- a/sources/main.cpp Sun Dec 14 20:47:44 2014 +0200 +++ b/sources/main.cpp Sun Dec 14 23:21:38 2014 +0200 @@ -43,10 +43,6 @@ HUFFMAN_Construct(); Interface::initialize(); - new RCONSession; - RCONSession::get_session()->set_password ("testpassword"); - RCONSession::get_session()->connect (IPAddress (localhost, 10666)); - for (;;) { fd_set fdset;
--- a/sources/network/ipaddress.cpp Sun Dec 14 20:47:44 2014 +0200 +++ b/sources/network/ipaddress.cpp Sun Dec 14 23:21:38 2014 +0200 @@ -36,8 +36,6 @@ #include <netdb.h> #include "ipaddress.h" -bool IPAddress::sink; - // ----------------------------------------------------------------------------- // IPAddress::IPAddress() : @@ -140,7 +138,7 @@ // ----------------------------------------------------------------------------- // STATIC METHOD -IPAddress::from_string (String input, bool* ok) -> IPAddress +IPAddress::from_string (String input) -> IPAddress { unsigned int parts[4]; int colonpos = input.find (":"); @@ -156,10 +154,7 @@ else { // Possibly a hostname, try resolve it - value = IPAddress::resolve (addressString, ok); - - if (*ok == false) - return value; + value = IPAddress::resolve (addressString); } if (colonpos != -1) @@ -171,7 +166,7 @@ // ----------------------------------------------------------------------------- // STATIC METHOD -IPAddress::resolve (String node, bool* ok) -> IPAddress +IPAddress::resolve (String node) -> IPAddress { addrinfo hints; addrinfo* lookup; @@ -179,10 +174,7 @@ hints.ai_family = AF_INET; if (getaddrinfo (node, nullptr, &hints, &lookup) != 0) - { - *ok = false; - return IPAddress(); - } + throw StringParseError ("unknown host " + node); IPAddress result; assert (lookup != nullptr);
--- a/sources/network/ipaddress.h Sun Dec 14 20:47:44 2014 +0200 +++ b/sources/network/ipaddress.h Sun Dec 14 23:21:38 2014 +0200 @@ -38,9 +38,23 @@ struct IPAddress { + class StringParseError + { + String m_message; + + public: + StringParseError (String message) : + m_message (message) {} + + inline METHOD + what() const throw() -> const char* + { + return m_message.chars(); + } + }; + unsigned long host; unsigned short port; - static bool sink; IPAddress(); IPAddress (unsigned long host, unsigned short port); @@ -56,8 +70,8 @@ inline METHOD operator!= (const IPAddress& other) const -> bool; inline METHOD operator[] (int n) const -> unsigned char; - static METHOD from_string (String input, bool* ok = &sink) -> IPAddress; - static METHOD resolve (String node, bool* ok = &sink) -> IPAddress; + static METHOD from_string (String input) -> IPAddress; + static METHOD resolve (String node) -> IPAddress; }; static const unsigned long localhost = 0x7F000001;
--- a/sources/network/rconsession.cpp Sun Dec 14 20:47:44 2014 +0200 +++ b/sources/network/rconsession.cpp Sun Dec 14 23:21:38 2014 +0200 @@ -16,13 +16,24 @@ } g_rconSession = this; +} - if (not m_socket.set_blocking (false)) +// ------------------------------------------------------------------------------------------------- +// +STATIC METHOD +RCONSession::new_session() -> RCONSession* +{ + RCONSession* session = new RCONSession; + + if (not session->socket()->set_blocking (false)) { - // TODO: find a better way to deal with errors print ("unable to set socket as non-blocking: %s\n", - m_socket.error_string().chars()); + session->socket()->error_string().chars()); + delete session; + return nullptr; } + + return session; } // ------------------------------------------------------------------------------------------------- @@ -275,3 +286,11 @@ bump_last_ping(); return true; } + +// ------------------------------------------------------------------------------------------------- +// +METHOD +RCONSession::state() const -> RCONSessionState +{ + return m_state; +}
--- a/sources/network/rconsession.h Sun Dec 14 20:47:44 2014 +0200 +++ b/sources/network/rconsession.h Sun Dec 14 23:21:38 2014 +0200 @@ -88,7 +88,6 @@ class RCONSession { public: - RCONSession(); ~RCONSession(); METHOD connect (IPAddress address) -> void; @@ -103,10 +102,14 @@ METHOD tick() -> void; METHOD bump_last_ping() -> void; METHOD send_command (const String& message) -> bool; + METHOD state() const -> RCONSessionState; + static METHOD new_session() -> RCONSession*; static METHOD get_session() -> RCONSession*; private: + RCONSession(); + RCONSessionState m_state; IPAddress m_address; UDPSocket m_socket;