--- a/sources/interface.cpp Sat Jan 09 02:35:00 2016 +0200 +++ b/sources/interface.cpp Sat Jan 09 17:41:21 2016 +0200 @@ -28,18 +28,20 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include <curses.h> #include <string.h> #include <time.h> #include "interface.h" #include "network/rconsession.h" #include "network/ipaddress.h" #include "coloredline.h" +BEGIN_ZFC_NAMESPACE static const int g_pageSize = 10; // ------------------------------------------------------------------------------------------------- // -int Interface::color_pair (Color fg, Color bg) +chtype Interface::color_pair (Color fg, Color bg) { return COLOR_PAIR (1 + (int (fg) * NUM_COLORS) + int (bg)); } @@ -127,7 +129,7 @@ { case INPUTSTATE_ADDRESS: if (CurrentAddress.host != 0) - mutable_current_input() = CurrentAddress.to_string (IP_WITH_PORT); + mutable_current_input() = CurrentAddress.to_string (IPAddress::WITH_PORT); break; default: @@ -141,15 +143,20 @@ // ------------------------------------------------------------------------------------------------- // Interface::Interface() : - Session (this) + InputCursor (0), + CursorPosition (0), + InputPanning (0), + NeedRefresh (false), + NeedStatusBarRender (false), + NeedInputRender (false), + NeedOutputRender (false), + NeedNicklistRender (false), + OutputScroll (0), + CurrentInputState (INPUTSTATE_NORMAL), + DisconnectConfirmFunction (nullptr) { -#ifdef XCURSES - ::Xinitscr(argc, argv); -#else - ::initscr(); -#endif - - ::cbreak(); + ::initscr(); + ::raw(); ::keypad (stdscr, true); ::noecho(); ::refresh(); @@ -158,7 +165,8 @@ InputHistory << ""; OutputLines.clear(); OutputLines << ColoredLine(); - Title.sprintf (APPNAME " %s (%d)", full_version_string(), changeset_date_string()); + Session.set_interface (this); + Title.sprintf (APPNAME " %s (%s)", full_version_string(), changeset_date_string()); if (::has_colors()) { @@ -196,7 +204,7 @@ { if (Title.length() <= COLS) { - int pair = color_pair (WHITE, BLUE); + chtype pair = color_pair (WHITE, BLUE); int startx = (COLS - Title.length()) / 2; int endx = startx + Title.length(); attron (pair); @@ -219,7 +227,7 @@ // ------------------------------------------------------------------------------------------------- // -void Interface::safe_disconnect (Function<void()> afterwards) +void Interface::safe_disconnect (std::function<void()> afterwards) { if (Session.is_active()) { @@ -252,8 +260,10 @@ { int x = x0; - for (int byte : line.data()) + for (int i = 0; i < line.data().size(); ++i) { + int byte = line.data()[i]; + if (x == x0 + width) { if (not allowWrap) @@ -263,35 +273,18 @@ ++y; } - if (isprint (byte)) + if (byte < 256 && isprint (byte)) { mvaddch (y, x, char (byte)); ++x; } + else if (byte >= RLINE_ON_COLOR and byte < (RLINE_ON_COLOR + 16)) + { + auto attrfunction = (byte < RLINE_OFF_COLOR ? &attron : &attroff); + (*attrfunction) (color_pair (Color ((byte - RLINE_ON_COLOR) & 7), DEFAULT)); + } else switch (byte) { - case RLINE_ON_BLACK: - case RLINE_ON_RED: - case RLINE_ON_GREEN: - case RLINE_ON_YELLOW: - case RLINE_ON_BLUE: - case RLINE_ON_MAGENTA: - case RLINE_ON_CYAN: - case RLINE_ON_WHITE: - attron (color_pair (Color (byte - RLINE_ON_BLACK), DEFAULT)); - break; - - case RLINE_OFF_BLACK: - case RLINE_OFF_RED: - case RLINE_OFF_GREEN: - case RLINE_OFF_YELLOW: - case RLINE_OFF_BLUE: - case RLINE_OFF_MAGENTA: - case RLINE_OFF_CYAN: - case RLINE_OFF_WHITE: - attroff (color_pair (Color (byte - RLINE_OFF_BLACK), DEFAULT)); - break; - case RLINE_ON_BOLD: attron (A_BOLD); break; @@ -421,7 +414,7 @@ // void Interface::render_input() { - int promptColor = color_pair (WHITE, BLUE); + chtype promptColor = color_pair (WHITE, BLUE); // If we're asking the user if they want to disconnect, we don't render any input strings, // just the confirmation message. @@ -443,8 +436,8 @@ // If we're inputting a password, replace it with asterisks if (CurrentInputState == INPUTSTATE_PASSWORD) { - for (char& ch : displayString) - ch = '*'; + for (int i = 0; i < displayString.length(); ++i) + displayString[i] = '*'; } // Ensure the cursor is within bounds @@ -482,7 +475,7 @@ // void Interface::render_statusbar() { - int color = color_pair (WHITE, BLUE); + chtype color = color_pair (WHITE, BLUE); int y = LINES - 1; attron (color); mvhline (y, 0, ' ', COLS); @@ -506,7 +499,7 @@ case RCON_CONNECTING: case RCON_AUTHENTICATING: - text = "Connecting to " + Session.address().to_string (IP_WITH_PORT) + "..."; + text = "Connecting to " + Session.address().to_string (IPAddress::WITH_PORT) + "..."; break; case RCON_CONNECTED: @@ -523,8 +516,10 @@ Session.num_admins() != 1 ? "s" : ""); } - text.sprintf ("%s | %s | %s", Session.address().to_string (IP_WITH_PORT).chars(), - Session.level().chars(), adminText.chars()); + text.sprintf ("%s | %s | %s", + Session.address().to_string (IPAddress::WITH_PORT).chars(), + Session.level().chars(), + adminText.chars()); } break; } @@ -794,6 +789,7 @@ break; case '\n': + case '\r': case KEY_ENTER: switch (CurrentInputState) { @@ -828,7 +824,13 @@ break; case INPUTSTATE_NORMAL: - if (Session.send_command (current_input())) + if (current_input()[0] == '/') + { + handle_command(current_input()); + InputHistory.insert (0, ""); + NeedInputRender = true; + } + else if (Session.send_command (current_input())) { InputHistory.insert (0, ""); NeedInputRender = true; @@ -842,7 +844,7 @@ safe_disconnect ([&]() {set_input_state (INPUTSTATE_ADDRESS);}); break; - case '\e': // Escape + case '\x1b': // Escape // We may have an alt key coming ch = ::getch(); @@ -947,14 +949,16 @@ // ------------------------------------------------------------------------------------------------- // -void Interface::print_to_console (String a) +void Interface::print_to_console (String message) { // Zandronum sometimes sends color codes as "\\c" and sometimes as "\x1C". // Let's correct that on our end and hope this won't cause conflicts. - a.replace ("\\c", "\x1C"); + message.replace ("\\c", "\x1C"); - for (char ch : a) + for (int i = 0; i < message.length(); ++i) { + char ch = message[i]; + if (ch == '\n') { OutputLines.last().finalize(); @@ -1033,3 +1037,59 @@ Session.disconnect(); set_input_state (INPUTSTATE_NORMAL); } + +// ------------------------------------------------------------------------------------------------- +// +void Interface::handle_command(const String& input) +{ + if (input[0] != '/') + return; + + StringList args = input.right(input.length() - 1).split(" "); + String command = args[0].to_lowercase(); + args.remove_at(0); + + if (command == "connect") + { + if (args.size() != 2) + print_error("Usage: /connect <address> <password>"); + else + { + IPAddress address; + + try + { + address = IPAddress::from_string(args[0]); + } + catch (std::exception& e) + { + print_error("%s\n", e.what()); + return; + } + + if (address.port == 0) + address.port = 10666; + + Session.set_password(args[1]); + Session.disconnect(); + Session.connect(CurrentAddress = address); + } + } + else if (command == "quit") + { + Session.disconnect(); + endwin(); + throw Exitception(); + } + else if (command == "watch") + { + if (not args.is_empty()) + Session.request_watch(args); + else + print_error("No CVars to watch.\n"); + } + else + print_error("Unknown command %s\n", command.chars()); +} + +END_ZFC_NAMESPACE