sources/interface.cpp

branch
protocol5
changeset 131
4996c8684b93
parent 106
7b156b764d11
parent 128
e5d185b62b7f
child 133
4d8fa5394d67
--- a/sources/interface.cpp	Mon Jan 25 04:15:31 2016 +0200
+++ b/sources/interface.cpp	Wed Jul 20 12:55:39 2016 +0300
@@ -1,5 +1,5 @@
 /*
-	Copyright 2014, 2015 Teemu Piippo
+	Copyright 2014 - 2016 Teemu Piippo
 	All rights reserved.
 
 	Redistribution and use in source and binary forms, with or without
@@ -37,20 +37,23 @@
 #include "coloredline.h"
 BEGIN_ZFC_NAMESPACE
 
-static const int g_pageSize = 10;
+static const int PAGE_SIZE = 10;
 
 // -------------------------------------------------------------------------------------------------
 //
 chtype Interface::color_pair (Color fg, Color bg)
 {
-	return COLOR_PAIR (1 + (int (fg) * NUM_COLORS) + int (bg));
+	if (fg == DEFAULT && bg == DEFAULT)
+		return 0;
+	else
+		return COLOR_PAIR (1 + (int (fg) * NUM_COLORS) + int (bg));
 }
 
 // -------------------------------------------------------------------------------------------------
 //
 const String& Interface::current_input()
 {
-	return InputHistory[InputCursor];
+	return m_inputHistory[m_inputCursor];
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -59,10 +62,10 @@
 //
 void Interface::detach_input()
 {
-	if (InputCursor > 0)
+	if (m_inputCursor > 0)
 	{
-		InputHistory[0] = current_input();
-		InputCursor = 0;
+		m_inputHistory[0] = current_input();
+		m_inputCursor = 0;
 	}
 }
 
@@ -72,7 +75,7 @@
 String& Interface::mutable_current_input()
 {
 	detach_input();
-	return InputHistory[InputCursor];
+	return m_inputHistory[m_inputCursor];
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -80,19 +83,19 @@
 void Interface::move_input_cursor (int delta)
 {
 	// No input history when inputting addresses or passwords
-	if (CurrentInputState != INPUTSTATE_NORMAL)
+	if (m_inputState != INPUTSTATE_NORMAL)
 	{
-		InputCursor = 0;
+		m_inputCursor = 0;
 		return;
 	}
 
-	int oldcursor = InputCursor;
-	InputCursor = clamp (InputCursor + delta, 0, InputHistory.size() - 1);
+	int oldcursor = m_inputCursor;
+	m_inputCursor = clamp (m_inputCursor + delta, 0, m_inputHistory.size() - 1);
 
-	if (InputCursor != oldcursor)
+	if (m_inputCursor != oldcursor)
 	{
-		CursorPosition = current_input().length();
-		NeedInputRender = true;
+		m_cursorPosition = current_input().length();
+		m_needInputRender = true;
 	}
 }
 
@@ -102,7 +105,7 @@
 {
 	String prompt;
 
-	switch (CurrentInputState)
+	switch (m_inputState)
 	{
 	case INPUTSTATE_NORMAL: prompt = ">"; break;
 	case INPUTSTATE_ADDRESS: prompt = "address:"; break;
@@ -119,41 +122,41 @@
 {
 	// Clear the input row (unless going to or from confirm state)
 	if (newstate != INPUTSTATE_CONFIRM_DISCONNECTION
-		and CurrentInputState != INPUTSTATE_CONFIRM_DISCONNECTION)
+		and m_inputState != INPUTSTATE_CONFIRM_DISCONNECTION)
 	{
-		InputCursor = 0;
+		m_inputCursor = 0;
 		mutable_current_input().clear();
 	}
 
 	switch (newstate)
 	{
 	case INPUTSTATE_ADDRESS:
-		if (CurrentAddress.host != 0)
-			mutable_current_input() = CurrentAddress.to_string (IPAddress::WITH_PORT);
+		if (m_remoteAddress.host != 0)
+			mutable_current_input() = m_remoteAddress.to_string (IPAddress::WITH_PORT);
 		break;
 
 	default:
 		break;
 	}
 
-	CurrentInputState = newstate;
-	NeedInputRender = true;
+	m_inputState = newstate;
+	m_needInputRender = true;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
 Interface::Interface() :
-	InputCursor (0),
-	CursorPosition (0),
-	InputPanning (0),
-	NeedRefresh (false),
-	NeedStatusBarRender (false),
-	NeedInputRender (false),
-	NeedOutputRender (false),
-	NeedNicklistRender (false),
-	OutputScroll (0),
-	CurrentInputState (INPUTSTATE_NORMAL),
-	DisconnectConfirmFunction (nullptr)
+	m_inputCursor (0),
+	m_cursorPosition (0),
+	m_inputPanning (0),
+	m_needRefresh (false),
+	m_needStatusBarRender (false),
+	m_needInputRender (false),
+	m_needOutputRender (false),
+	m_needNicklistRender (false),
+	m_outputScroll (0),
+	m_inputState (INPUTSTATE_NORMAL),
+	m_disconnectCallback (nullptr)
 {
 	::initscr();
 	::raw();
@@ -161,20 +164,19 @@
 	::noecho();
 	::refresh();
 	::timeout (0);
-	InputHistory.clear();
-	InputHistory << "";
-	OutputLines.clear();
-	OutputLines << ColoredLine();
-	Session.set_interface (this);
-	Title.sprintf (APPNAME " %s (%s)", full_version_string(), changeset_date_string());
+	m_inputHistory.clear();
+	m_inputHistory << "";
+	m_outputLines.clear();
+	m_outputLines << ColoredLine();
+	m_session.set_interface (this);
+	reset_title();
 
 	if (::has_colors())
 	{
 		::start_color();
-		::use_default_colors();
-
-		int defaultFg = (use_default_colors() == OK) ? -1 : COLOR_WHITE;
-		int defaultBg = (use_default_colors() == OK) ? -1 : COLOR_BLACK;
+		bool hasDefaultColors = (::use_default_colors() == OK);
+		int defaultFg = hasDefaultColors ? -1 : COLOR_WHITE;
+		int defaultBg = hasDefaultColors ? -1 : COLOR_BLACK;
 
 		// Initialize color pairs
 		for (int i = 0; i < NUM_COLORS; ++i)
@@ -184,58 +186,57 @@
 			int fg = (i == DEFAULT) ? defaultFg : i;
 			int bg = (j == DEFAULT) ? defaultBg : j;
 
-			if (::init_pair (pairnum, fg, bg) == ERR)
-				print ("Unable to initialize color pair %d (%d, %d)\n", pairnum, fg, bg);
+			if (fg != -1 || bg != -1)
+			{
+				if (::init_pair (pairnum, fg, bg) == ERR)
+					print_warning ("Unable to initialize color pair %d (%d, %d)\n", pairnum, fg, bg);
+			}
 		}
 	}
-	else
-	{
-		print ("This terminal does not appear to support colors.\n");
-	}
 
 	render_full();
 	refresh();
-	NeedRefresh = false;
+	m_needRefresh = false;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
 void Interface::render_titlebar()
 {
-	if (Title.length() <= COLS)
+	if (m_title.length() <= COLS)
 	{
 		chtype pair = color_pair (WHITE, BLUE);
-		int startx = (COLS - Title.length()) / 2;
-		int endx = startx + Title.length();
+		int startx = (COLS - m_title.length()) / 2;
+		int endx = startx + m_title.length();
 		attron (pair);
-		mvprintw (0, startx, "%s", Title.chars());
+		mvprintw (0, startx, "%s", m_title.chars());
 		mvhline (0, 0, ' ', startx);
 		mvhline (0, endx, ' ', COLS - endx);
 		attroff (pair);
 	}
 
-	NeedRefresh = true;
+	m_needRefresh = true;
 }
 
 // -------------------------------------------------------------------------------------------------
 //
 void Interface::set_title (const String& title)
 {
-	Title = title;
+	m_title = title;
 	render_titlebar();
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void Interface::safe_disconnect (std::function<void()> afterwards)
+void Interface::safe_disconnect (std::function<void(bool)> afterwards)
 {
-	if (Session.is_active())
+	if (m_session.is_active())
 	{
-		DisconnectConfirmFunction = afterwards;
+		m_disconnectCallback = afterwards;
 		set_input_state (INPUTSTATE_CONFIRM_DISCONNECTION);
 	}
 	else
-		afterwards();
+		afterwards(false);
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -302,15 +303,15 @@
 //
 void Interface::render_output()
 {
-	if (OutputLines.size() == 1)
+	if (m_outputLines.size() == 1)
 		return;
 
-	OutputScroll = clamp (OutputScroll, 0, OutputLines.size() - 1);
+	m_outputScroll = clamp (m_outputScroll, 0, m_outputLines.size() - 1);
 
 	int height = LINES - 3;
 	int width = COLS - nicklist_width();
 	int printOffset = 0;
-	int end = OutputLines.size() - 1 - OutputScroll;
+	int end = m_outputLines.size() - 1 - m_outputScroll;
 	int start = end;
 	int usedHeight = 0;
 	int y = 1;
@@ -319,7 +320,7 @@
 	// Where to start?
 	while (start > 0)
 	{
-		int rows = OutputLines[start - 1].rows (width);
+		int rows = m_outputLines[start - 1].rows (width);
 
 		if (usedHeight + rows > height)
 		{
@@ -335,9 +336,9 @@
 	// See if there's any more rows to use (end may be too small)
 	if (not tightFit)
 	{
-		while (end < OutputLines.size())
+		while (end < m_outputLines.size())
 		{
-			int rows = OutputLines[end].rows (width);
+			int rows = m_outputLines[end].rows (width);
 
 			if (usedHeight + rows > height)
 			{
@@ -353,7 +354,7 @@
 	if (start > 0)
 		printOffset = height - usedHeight;
 
-	OutputScroll = OutputLines.size() - 1 - end;
+	m_outputScroll = m_outputLines.size() - 1 - end;
 
 	if (start < 0 or start == end or printOffset >= height)
 		return;
@@ -368,10 +369,10 @@
 	y += printOffset;
 
 	for (int i = start; i < end; ++i)
-		y = render_colorline (y, 0, width, OutputLines[i], true);
+		y = render_colorline (y, 0, width, m_outputLines[i], true);
 
-	NeedOutputRender = false;
-	NeedRefresh = true;
+	m_needOutputRender = false;
+	m_needRefresh = true;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -390,9 +391,9 @@
 	{
 		mvhline (y, x, ' ', width);
 
-		if (i < PlayerNames.size())
+		if (i < m_playerNames.size())
 		{
-			String displaynick = PlayerNames[i];
+			String displaynick = m_playerNames[i];
 
 			if (displaynick.length() > width)
 			{
@@ -406,8 +407,8 @@
 		y++;
 	}
 
-	NeedNicklistRender = false;
-	NeedRefresh = true;
+	m_needNicklistRender = false;
+	m_needRefresh = true;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -418,13 +419,13 @@
 
 	// If we're asking the user if they want to disconnect, we don't render any input strings,
 	// just the confirmation message.
-	if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION)
+	if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
 	{
 		attron (promptColor);
 		mvhline (LINES - 2, 0, ' ', COLS);
 		mvprintw (LINES - 2, 0, "Are you sure you want to disconnect? y/n");
 		attroff (promptColor);
-		NeedRefresh = true;
+		m_needRefresh = true;
 		return;
 	}
 
@@ -434,25 +435,25 @@
 	int y = LINES - 2;
 
 	// If we're inputting a password, replace it with asterisks
-	if (CurrentInputState == INPUTSTATE_PASSWORD)
+	if (m_inputState == INPUTSTATE_PASSWORD)
 	{
 		for (int i = 0; i < displayString.length(); ++i)
 			displayString[i] = '*';
 	}
 
 	// Ensure the cursor is within bounds
-	CursorPosition = clamp (CursorPosition, 0, displayString.length());
+	m_cursorPosition = clamp (m_cursorPosition, 0, displayString.length());
 
 	// Ensure that the cursor is always in view, adjust panning if this is not the case
-	if (CursorPosition > InputPanning + displayLength)
-		InputPanning = CursorPosition - displayLength; // cursor went too far right
-	else if (CursorPosition < InputPanning)
-		InputPanning = CursorPosition; // cursor went past the pan value to the left
+	if (m_cursorPosition > m_inputPanning + displayLength)
+		m_inputPanning = m_cursorPosition - displayLength; // cursor went too far right
+	else if (m_cursorPosition < m_inputPanning)
+		m_inputPanning = m_cursorPosition; // cursor went past the pan value to the left
 
 	// What part of the string to draw?
-	int start = InputPanning;
+	int start = m_inputPanning;
 	int end = min<int> (displayString.length(), start + displayLength);
-	assert (CursorPosition >= start and CursorPosition <= end);
+	assert (m_cursorPosition >= start and m_cursorPosition <= end);
 
 	// Render the input string
 	mvhline (LINES - 2, 0, ' ', COLS);
@@ -465,10 +466,10 @@
 
 	// Store in memory where the cursor is now (so that we can re-draw it to position the terminal
 	// cursor).
-	CursorCharacter.ch = CursorPosition != 0 ? displayString[CursorPosition - 1] : '\0';
-	CursorCharacter.x = prompt.length() + (CursorPosition - InputPanning);
-	NeedRefresh = true;
-	NeedInputRender = false;
+	m_cursorCharacter.ch = m_cursorPosition != 0 ? displayString[m_cursorPosition - 1] : '\0';
+	m_cursorCharacter.x = prompt.length() + (m_cursorPosition - m_inputPanning);
+	m_needRefresh = true;
+	m_needInputRender = false;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -479,10 +480,10 @@
 	int y = LINES - 1;
 	attron (color);
 	mvhline (y, 0, ' ', COLS);
-	mvprintw (y, 0, "%s", StatusBarText.chars());
+	mvprintw (y, 0, "%s", m_statusBarText.chars());
 	attroff (color);
-	NeedRefresh = true;
-	NeedStatusBarRender = false;
+	m_needRefresh = true;
+	m_needStatusBarRender = false;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -491,7 +492,7 @@
 {
 	String text;
 
-	switch (Session.state())
+	switch (m_session.state())
 	{
 	case RCON_DISCONNECTED:
 		text = "Disconnected.";
@@ -499,26 +500,26 @@
 
 	case RCON_CONNECTING:
 	case RCON_AUTHENTICATING:
-		text = "Connecting to " + Session.address().to_string (IPAddress::WITH_PORT) + "...";
+		text = "Connecting to " + m_session.address().to_string (IPAddress::WITH_PORT) + "...";
 		break;
 
 	case RCON_CONNECTED:
 		{
 			String adminText;
 
-			if (Session.num_admins() == 0)
+			if (m_session.num_admins() == 0)
 			{
 				adminText = "No other admins";
 			}
 			else
 			{
-				adminText.sprintf ("%d other admin%s", Session.num_admins(),
-					Session.num_admins() != 1 ? "s" : "");
+				adminText.sprintf ("%d other admin%s", m_session.num_admins(),
+					m_session.num_admins() != 1 ? "s" : "");
 			}
 
 			text.sprintf ("%s | %s | %s",
-				Session.address().to_string (IPAddress::WITH_PORT).chars(),
-				Session.level().chars(),
+				m_session.address().to_string (IPAddress::WITH_PORT).chars(),
+				m_session.level().chars(),
 				adminText.chars());
 		}
 		break;
@@ -527,12 +528,13 @@
 	if (not text.is_empty())
 		text += " | ";
 
-	text += "Ctrl+N to connect, Ctrl+Q to quit";
+	text += "Ctrl+N to connect, Ctrl+Q to ";
+	text += (m_session.state() == RCON_DISCONNECTED) ? "quit" : "disconnect";
 
-	if (text != StatusBarText)
+	if (text != m_statusBarText)
 	{
-		StatusBarText = text;
-		NeedStatusBarRender = true;
+		m_statusBarText = text;
+		m_needStatusBarRender = true;
 	}
 }
 
@@ -553,13 +555,13 @@
 void Interface::position_cursor()
 {
 	// This is only relevant if the input string is being drawn
-	if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION)
+	if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
 		return;
 
 	int y = LINES - 2;
 
-	if (CursorCharacter.ch != '\0')
-		mvprintw (y, CursorCharacter.x, "%c", CursorCharacter.ch);
+	if (m_cursorCharacter.ch != '\0')
+		mvprintw (y, m_cursorCharacter.x, "%c", m_cursorCharacter.ch);
 	else
 		mvprintw (y, prompt_string().length(), " ");
 }
@@ -569,7 +571,7 @@
 int Interface::find_previous_word()
 {
 	const String& input = current_input();
-	int pos = CursorPosition;
+	int pos = m_cursorPosition;
 
 	// Move past whitespace
 	while (pos > 0 and isspace (input[pos - 1]))
@@ -587,7 +589,7 @@
 int Interface::find_next_word()
 {
 	const String& input = current_input();
-	int pos = CursorPosition;
+	int pos = m_cursorPosition;
 
 	// Move past current whitespace
 	while (pos < input.length() and isspace (input[pos]))
@@ -607,13 +609,13 @@
 	if (a >= b)
 		return;
 
-	if (CursorPosition > a and CursorPosition <= b)
-		CursorPosition = a;
+	if (m_cursorPosition > a and m_cursorPosition <= b)
+		m_cursorPosition = a;
 
 	String& input = mutable_current_input();
-	PasteBuffer = input.mid (a, b);
+	m_pasteBuffer = input.mid (a, b);
 	input.remove (a, b - a);
-	NeedInputRender = true;
+	m_needInputRender = true;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -632,7 +634,7 @@
 		return;
 	}
 
-	if (CurrentInputState == INPUTSTATE_CONFIRM_DISCONNECTION)
+	if (m_inputState == INPUTSTATE_CONFIRM_DISCONNECTION)
 	{
 		if (ch == 'y' or ch == 'Y')
 			disconnected();
@@ -644,23 +646,22 @@
 
 	if (ch >= 0x20 and ch <= 0x7E)
 	{
-		mutable_current_input().insert (CursorPosition++, char (ch));
-		NeedInputRender = true;
+		mutable_current_input().insert (m_cursorPosition++, char (ch));
+		m_needInputRender = true;
 	}
 	else switch (ch)
 	{
 	case 'Q' - 'A' + 1: // ^Q
-		switch (CurrentInputState)
+		switch (m_inputState)
 		{
 		case INPUTSTATE_CONFIRM_DISCONNECTION:
 			break;
 
 		case INPUTSTATE_NORMAL:
-			safe_disconnect ([&]()
+			safe_disconnect ([&](bool hadsession)
 			{
-				if (Session.is_active())
+				if (hadsession)
 				{
-					Session.disconnect();
 					set_input_state (INPUTSTATE_NORMAL);
 				}
 				else
@@ -682,19 +683,19 @@
 
 	case KEY_LEFT:
 	case 'B' - 'A' + 1: // readline ^B
-		if (CursorPosition > 0)
+		if (m_cursorPosition > 0)
 		{
-			CursorPosition--;
-			NeedInputRender = true;
+			m_cursorPosition--;
+			m_needInputRender = true;
 		}
 		break;
 
 	case KEY_RIGHT:
 	case 'F' - 'A' + 1: // readline ^F
-		if (CursorPosition < current_input().length())
+		if (m_cursorPosition < current_input().length())
 		{
-			CursorPosition++;
-			NeedInputRender = true;
+			m_cursorPosition++;
+			m_needInputRender = true;
 		}
 		break;
 
@@ -705,72 +706,72 @@
 
 	case KEY_HOME:
 	case 'A' - 'A' + 1: // readline ^A
-		if (CursorPosition != 0)
+		if (m_cursorPosition != 0)
 		{
-			CursorPosition = 0;
-			NeedInputRender = true;
+			m_cursorPosition = 0;
+			m_needInputRender = true;
 		}
 		break;
 
 	case KEY_END:
 	case 'E' - 'A' + 1: // readline ^E
-		if (CursorPosition != current_input().length())
+		if (m_cursorPosition != current_input().length())
 		{
-			CursorPosition = current_input().length();
-			NeedInputRender = true;
+			m_cursorPosition = current_input().length();
+			m_needInputRender = true;
 		}
 		break;
 
 	case KEY_BACKSPACE:
 	case '\b':
-		if (CursorPosition > 0)
+		if (m_cursorPosition > 0)
 		{
-			mutable_current_input().remove_at (--CursorPosition);
-			NeedInputRender = true;
+			mutable_current_input().remove_at (--m_cursorPosition);
+			m_needInputRender = true;
 		}
 		break;
 
 	case KEY_DC:
 	case 'D' - 'A' + 1: // readline ^D
-		if (CursorPosition < current_input().length())
+		if (m_cursorPosition < current_input().length())
 		{
-			mutable_current_input().remove_at (CursorPosition);
-			NeedInputRender = true;
+			mutable_current_input().remove_at (m_cursorPosition);
+			m_needInputRender = true;
 		}
 		break;
 
 	case KEY_PPAGE:
-		OutputScroll += min (g_pageSize, LINES / 2);
-		NeedOutputRender = true;
+		m_outputScroll += min (PAGE_SIZE, LINES / 2);
+		m_needOutputRender = true;
 		break;
 
 	case KEY_NPAGE:
-		OutputScroll -= min (g_pageSize, LINES / 2);
-		NeedOutputRender = true;
+		m_outputScroll -= min (PAGE_SIZE, LINES / 2);
+		m_needOutputRender = true;
 		break;
 
 	case 'U' - 'A' + 1: // readline ^U - delete from start to cursor
-		if (CursorPosition > 0)
+		if (m_cursorPosition > 0)
 		{
-			yank (0, CursorPosition);
-			CursorPosition = 0;
+			yank (0, m_cursorPosition);
+			m_cursorPosition = 0;
 		}
 		break;
 
 	case 'K' - 'A' + 1: // readline ^K - delete from cursor to end
-		yank (CursorPosition, mutable_current_input().length());
+		yank (m_cursorPosition, mutable_current_input().length());
 		break;
 
 	case 'W' - 'A' + 1: // readline ^W - delete from previous word bounary to current
-		yank (find_previous_word(), CursorPosition);
+		yank (find_previous_word(), m_cursorPosition);
 		break;
 
 	case 'Y' - 'A' + 1: // readline ^Y - paste previously deleted text
-		if (not PasteBuffer.is_empty())
+		if (not m_pasteBuffer.is_empty())
 		{
-			mutable_current_input().insert (CursorPosition, PasteBuffer);
-			CursorPosition += PasteBuffer.length();
-			NeedInputRender = true;
+			mutable_current_input().insert (m_cursorPosition, m_pasteBuffer);
+			m_cursorPosition += m_pasteBuffer.length();
+			m_needInputRender = true;
 		}
 		break;
 
@@ -778,12 +779,12 @@
 		{
 			int space = current_input().find (" ");
 
-			if (CurrentInputState == INPUTSTATE_NORMAL
-				and CursorPosition > 0
-				and (space == -1 or space >= CursorPosition))
+			if (m_inputState == INPUTSTATE_NORMAL
+				and m_cursorPosition > 0
+				and (space == -1 or space >= m_cursorPosition))
 			{
-				String start = current_input().mid (0, CursorPosition);
-				Session.request_tab_complete (start);
+				String start = current_input().mid (0, m_cursorPosition);
+				m_session.request_tab_complete (start);
 			}
 		}
 		break;
@@ -791,7 +792,7 @@
 	case '\n':
 	case '\r':
 	case KEY_ENTER:
-		switch (CurrentInputState)
+		switch (m_inputState)
 		{
 		case INPUTSTATE_CONFIRM_DISCONNECTION:
 			break; // handled above
@@ -799,7 +800,7 @@
 		case INPUTSTATE_ADDRESS:
 			try
 			{
-				CurrentAddress = IPAddress::from_string (current_input());
+				m_remoteAddress = IPAddress::from_string (current_input());
 			}
 			catch (std::exception& e)
 			{
@@ -807,18 +808,18 @@
 				return;
 			}
 
-			if (CurrentAddress.port == 0)
-				CurrentAddress.port = 10666;
+			if (m_remoteAddress.port == 0)
+				m_remoteAddress.port = 10666;
 
 			set_input_state (INPUTSTATE_PASSWORD);
 			break;
 
 		case INPUTSTATE_PASSWORD:
-			if (CurrentInputState == INPUTSTATE_PASSWORD and not current_input().is_empty())
+			if (m_inputState == INPUTSTATE_PASSWORD and not current_input().is_empty())
 			{
-				Session.disconnect();
-				Session.set_password (current_input());
-				Session.connect (CurrentAddress);
+				m_session.disconnect();
+				m_session.set_password (current_input());
+				m_session.connect (m_remoteAddress);
 				set_input_state (INPUTSTATE_NORMAL);
 			}
 			break;
@@ -827,21 +828,19 @@
 			if (current_input()[0] == '/')
 			{
 				handle_command(current_input());
-				InputHistory.insert (0, "");
-				NeedInputRender = true;
+				flush_input();
 			}
-			else if (Session.send_command (current_input()))
+			else if (m_session.send_command (current_input()))
 			{
-				InputHistory.insert (0, "");
-				NeedInputRender = true;
+				flush_input();
 			}
 			break;
 		}
 		break;
 
 	case 'N' - 'A' + 1: // ^N
-		if (CurrentInputState == INPUTSTATE_NORMAL)
-			safe_disconnect ([&]() {set_input_state (INPUTSTATE_ADDRESS);});
+		if (m_inputState == INPUTSTATE_NORMAL)
+			safe_disconnect ([&](bool){set_input_state (INPUTSTATE_ADDRESS);});
 		break;
 
 	case '\x1b': // Escape
@@ -855,30 +854,35 @@
 			case 'b':
 			case 'B':
 				// readline alt-b - move one word to the left
-				CursorPosition = find_previous_word();
-				NeedInputRender = true;
+				m_cursorPosition = find_previous_word();
+				m_needInputRender = true;
 				break;
 
 			case 'f':
 			case 'F':
 				// readline alt-f - move one word to the right
-				CursorPosition = find_next_word();
-				NeedInputRender = true;
+				m_cursorPosition = find_next_word();
+				m_needInputRender = true;
 				break;
 
 			case 'd':
 			case 'D':
 				// readline alt-d - delete from here till next word boundary
-				yank (CursorPosition, find_next_word());
+				yank (m_cursorPosition, find_next_word());
+				break;
+
+			case KEY_BACKSPACE: // alt+backspace, remove previous word
+			case '\b':
+				yank (find_previous_word(), m_cursorPosition);
 				break;
 			}
 		}
 		else
 		{
 			// No alt-key, handle pure escape
-			if (CurrentInputState == INPUTSTATE_PASSWORD)
+			if (m_inputState == INPUTSTATE_PASSWORD)
 				set_input_state (INPUTSTATE_ADDRESS);
-			else if (CurrentInputState == INPUTSTATE_ADDRESS)
+			else if (m_inputState == INPUTSTATE_ADDRESS)
 				set_input_state (INPUTSTATE_NORMAL);
 		}
 		break;
@@ -891,16 +895,16 @@
 //
 void Interface::render()
 {
-	if (NeedStatusBarRender) render_statusbar();
-	if (NeedInputRender) render_input();
-	if (NeedOutputRender) render_output();
-	if (NeedNicklistRender) render_nicklist();
+	if (m_needStatusBarRender) render_statusbar();
+	if (m_needInputRender) render_input();
+	if (m_needOutputRender) render_output();
+	if (m_needNicklistRender) render_nicklist();
 
-	if (NeedRefresh)
+	if (m_needRefresh)
 	{
 		position_cursor();
 		refresh();
-		NeedRefresh = false;
+		m_needRefresh = false;
 	}
 }
 
@@ -915,10 +919,21 @@
 
 // -------------------------------------------------------------------------------------------------
 //
+void __cdecl Interface::print_text (const char* fmtstr, ...)
+{
+	va_list args;
+	va_start (args, fmtstr);
+	vprint (fmtstr, args);
+	va_end (args);
+}
+
+// -------------------------------------------------------------------------------------------------
+//
 void __cdecl Interface::print (const char* fmtstr, ...)
 {
 	va_list args;
 	va_start (args, fmtstr);
+	print_to_console (TEXTCOLOR_BrightBlue);
 	vprint (fmtstr, args);
 	va_end (args);
 }
@@ -931,7 +946,6 @@
 	va_start (args, fmtstr);
 	print_to_console (TEXTCOLOR_BrightYellow "-!- ");
 	vprint (fmtstr, args);
-	print_to_console (TEXTCOLOR_Reset);
 	va_end (args);
 }
 
@@ -943,7 +957,6 @@
 	va_start (args, fmtstr);
 	print_to_console (TEXTCOLOR_BrightRed "!!! ");
 	vprint (fmtstr, args);
-	print_to_console (TEXTCOLOR_Reset);
 	va_end (args);
 }
 
@@ -961,12 +974,12 @@
 
 		if (ch == '\n')
 		{
-			OutputLines.last().finalize();
-			OutputLines << ColoredLine();
+			m_outputLines.last().finalize();
+			m_outputLines << ColoredLine();
 			continue;
 		}
 
-		if (OutputLines.last().length() == 0)
+		if (m_outputLines.last().length() == 0)
 		{
 			time_t now;
 			time (&now);
@@ -974,13 +987,17 @@
 			strftime (timestamp, sizeof timestamp, "[%H:%M:%S] ", localtime (&now));
 
 			for (char* cp = timestamp; *cp != '\0'; ++cp)
-				OutputLines.last().add_char (*cp);
+				m_outputLines.last().add_char (*cp);
 		}
 
-		OutputLines.last().add_char (ch);
+		// Remove some lines if there's too many of them. 20,000 should be enough, I hope.
+		while (m_outputLines.size() > 20000)
+			m_outputLines.remove_at(0);
+
+		m_outputLines.last().add_char (ch);
 	}
 
-	NeedOutputRender = true;
+	m_needOutputRender = true;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -989,7 +1006,7 @@
 {
 	try
 	{
-		CurrentAddress = IPAddress::from_string (address);
+		m_remoteAddress = IPAddress::from_string (address);
 	}
 	catch (std::exception& e)
 	{
@@ -997,20 +1014,20 @@
 		return;
 	}
 
-	if (CurrentAddress.port == 0)
-		CurrentAddress.port = 10666;
+	if (m_remoteAddress.port == 0)
+		m_remoteAddress.port = 10666;
 
-	Session.disconnect();
-	Session.set_password (password);
-	Session.connect (CurrentAddress);
+	m_session.disconnect();
+	m_session.set_password (password);
+	m_session.connect (m_remoteAddress);
 }
 
 // -------------------------------------------------------------------------------------------------
 //
 void Interface::set_player_names (const StringList& names)
 {
-	PlayerNames = names;
-	NeedNicklistRender = true;
+	m_playerNames = names;
+	m_needNicklistRender = true;
 }
 
 // -------------------------------------------------------------------------------------------------
@@ -1025,21 +1042,13 @@
 			complete += ' ';
 
 		input.replace (0, part.length(), complete);
-		CursorPosition = complete.length();
-		NeedInputRender = true;
+		m_cursorPosition = complete.length();
+		m_needInputRender = true;
 	}
 }
 
 // -------------------------------------------------------------------------------------------------
 //
-void Interface::disconnected()
-{
-	Session.disconnect();
-	set_input_state (INPUTSTATE_NORMAL);
-}
-
-// -------------------------------------------------------------------------------------------------
-//
 void Interface::handle_command(const String& input)
 {
 	if (input[0] != '/')
@@ -1052,7 +1061,9 @@
 	if (command == "connect")
 	{
 		if (args.size() != 2)
-			print_error("Usage: /connect <address> <password>");
+		{
+			print_error("Usage: /connect <address> <password>\n");
+		}
 		else
 		{
 			IPAddress address;
@@ -1070,21 +1081,25 @@
 			if (address.port == 0)
 				address.port = 10666;
 
-			Session.set_password(args[1]);
-			Session.disconnect();
-			Session.connect(CurrentAddress = address);
+			m_session.set_password(args[1]);
+			m_session.disconnect();
+			m_session.connect(m_remoteAddress = address);
 		}
 	}
+	else if (command == "disconnect")
+	{
+		m_session.disconnect();
+	}
 	else if (command == "quit")
 	{
-		Session.disconnect();
+		m_session.disconnect();
 		endwin();
 		throw Exitception();
 	}
 	else if (command == "watch")
 	{
 		if (not args.is_empty())
-			Session.request_watch(args);
+			m_session.request_watch(args);
 		else
 			print_error("No CVars to watch.\n");
 	}
@@ -1092,4 +1107,29 @@
 		print_error("Unknown command %s\n", command.chars());
 }
 
+// -------------------------------------------------------------------------------------------------
+//
+void Interface::disconnected()
+{
+	print("Disconnected from %s\n", m_session.address().to_string(IPAddress::WITH_PORT).chars());
+	reset_title();
+	render_full();
+}
+
+// -------------------------------------------------------------------------------------------------
+//
+void Interface::reset_title()
+{
+	m_title.sprintf ("%s %s (%s)", application_name(), full_version_string(), changeset_date_string());
+}
+
+// -------------------------------------------------------------------------------------------------
+//
+void Interface::flush_input()
+{
+	m_inputHistory.insert (0, "");
+	m_inputCursor = 0;
+	m_needInputRender = true;
+}
+
 END_ZFC_NAMESPACE

mercurial