Hiya n00bsy b00bsies!  <3 <3  :-P

First let me give you a little bit of background to justify why I spent so
much
time fixing these bugs.  Let's say I'm hanging out with my clan and we find
a
scrim.  Someone writes a text message over Mumble with an IP address, port,
and password to the scrim server.  So currently, I have to try to memorize
this information or minimize my game window so I can type as I read.  It's
really annoying.  Back in the old days I could just copy and paste the info
into my X terminal.  Yes, I'm running Linux.

So, my mission for the past couple of days has been to fix con_tty.c.  I
know
there were many issues with the old [client] version, and as far as I know
I've addressed all the issues.  Now my patch (attached) does
not enable the tty console on clients.  My patch also doesn't modify the
behavior of the console on servers (well, almost doesn't; it does fix two
small bugs, see #5 and #9 below).  So, if you're using my patch on either
the
server or on the client you'll hardly even notice that the patch is applied!

If you would like to start _using_ my patch and trying it out, you'll have
to
edit the Makefile to change "client/con_passive" to "client/con_tty" in the
one
spot where you see it.  If this change to the Makefile is made, I'm not
really
sure how many other systems it affects other than Linux.  I have not gotten
that far into fixing the other consoles yet.  For example, by changing the
Makefile in the way suggested, it may cause Windows client builds to use
con_win32.c.  I really don't know at this point.  However, just fixing
con_tty.c is a step in the right direction.  If needed, I will fix any other
version of the console as well (such as Windows).


Here is a rundown of the changes in my patch:


1. The tty prompt can be changed to any string consisting of one or more
characters (well probably zero or more characters but alas, I didn't
actually
test the zero case; the code should handle it from what I recall).  The tty
prompt is defined via TTY_CONSOLE_PROMPT.  Right now it's set to the
original
"]" for servers and to "ioq3-tty]" for clients.  You can change it to a
better
value before committing my change.

2. Commands typed in the tty console can optionally be sent to appear in the
in-game console.  Right now that feature is turned on.  It's controlled via
TTY_COMMANDS_VISIBLE_IN_GAME (tests presence of this being #defined).

3. Removed some references to a size_t return value from write() where it
wasn't being used.

4. Using the symbolic STDOUT_FILENO more consistently instead of 1 in
write() calls.
5. If a user types Enter at the tty prompt, the empty string will no longer
be added to the history.  Only commands consisting of one or more
characters (even if it's a space) will be added to the console history.
This change affects the server console too.

6. On tty console initialization [especially for clients], make sure that
the
prompt appears immediately and not only after a call to Com_Printf().  When
the game starts and a user doesn't see the prompt, it may lead to poor
user experience.

7. Only commands are allowed in the tty console.  No chats.  (This is the
old
behavior.)  To make that more clear in the client console, all commands will
have a '\' prepended to them.  This '\' will appear before the command when
it's entered (visually) and the '\' will be present in the history.  The '\'
is not prepended if a '/' starts the command.  A '\' is also not prepended
when
the command length is at capacity (length 255).  This only applies to a
client
tty console; the server console is completely unchanged in this respect.

8. CON_Input() returns the stuff after a '/' or a '\' if such a character
precedes the command string.  In other words, autocompletion is no longer
broken.  Like I said in #7, a '\' is prepended to every command, so seeing
that '\' whether you used autocompletion or not makes things very consistent
and causes a delightful user experience.  This change does not apply to the
server console.  (In server console, the command string is treated literally
including any preceding slashes.)

9. CON_Print(), which is called as a result of any Com_Printf(), only
does the CON_Show() of the prompt when the string being printed ends with
a '\n'.  I have a very good explanation of how this works in the comments
to the code.  The problem with outputting a prompt on a line that doesn't
end
with a '\n' is that the output can get garbled, especially if the prompt
consists of more than one character.  This is the only other change that
affects the server console.  It's not a dangerous change; it only improves
things.  It does not cause any problems even if every string passed to
CON_Print() never ends with a '\n' (it will only look a little bit
unpleasant,
but won't be any more unpleasant then before).


Let's test out and proofread my changes!  Hopefully this patch can make it
into the ioquake3 SVN source tree!

If you see any problems please let me know.  I can work on making this as
good as you need it.

I have already tested this change on a server and on a client with
TTY_COMMANDS_VISIBLE_IN_GAME on and off.

Also we need to figure out what other console code needs to be fixed
before enabling tty console in the Makefile.


- "nobugs" Rambetter :-)
Index: code/sys/con_tty.c
===================================================================
--- code/sys/con_tty.c	(revision 1802)
+++ code/sys/con_tty.c	(working copy)
@@ -45,6 +45,7 @@
 // general flag to tell about tty console mode
 static qboolean ttycon_on = qfalse;
 static int ttycon_hide = 0;
+static int ttycon_show_overdue = 0; // used only by CON_Print()
 
 // some key codes that the terminal may be using, initialised on start up
 static int TTY_erase;
@@ -60,6 +61,23 @@
 static field_t ttyEditLines[ CON_HISTORY ];
 static int hist_current = -1, hist_count = 0;
 
+// Note: TTY_CONSOLE_PROMPT can contain any number of characters.
+#ifdef DEDICATED
+#define TTY_CONSOLE_PROMPT "]"
+#else
+// Right now the in-game console has the "]" prompt.
+// We choose a different prompt for the tty console for the following reasons:
+// 1. Both tty and in-game console commands get printed to tty output, so it may be
+//    confusing to tell where a command came from.
+// 2. tty and in-game consoles have an independent history buffers.
+// 3. When connected to a server, a command in the in-game console not preceded
+//    by a slash is a chat (not so for tty).
+#define TTY_CONSOLE_PROMPT "ioq3-tty]"
+// Note: In order to cause tty commands to be visible in the in-game console, you
+// must define TTY_COMMANDS_VISIBLE_IN_GAME.
+#define TTY_COMMANDS_VISIBLE_IN_GAME
+#endif
+
 /*
 ==================
 CON_FlushIn
@@ -88,14 +106,13 @@
 static void CON_Back( void )
 {
 	char key;
-	size_t size;
 
 	key = '\b';
-	size = write(STDOUT_FILENO, &key, 1);
+	write(STDOUT_FILENO, &key, 1);
 	key = ' ';
-	size = write(STDOUT_FILENO, &key, 1);
+	write(STDOUT_FILENO, &key, 1);
 	key = '\b';
-	size = write(STDOUT_FILENO, &key, 1);
+	write(STDOUT_FILENO, &key, 1);
 }
 
 /*
@@ -123,7 +140,9 @@
 				CON_Back();
 			}
 		}
-		CON_Back(); // Delete "]"
+		for (i = strlen(TTY_CONSOLE_PROMPT); i > 0; i--) {
+			CON_Back();
+		}
 		ttycon_hide++;
 	}
 }
@@ -146,13 +165,12 @@
 		ttycon_hide--;
 		if (ttycon_hide == 0)
 		{
-			size_t size;
-			size = write(STDOUT_FILENO, "]", 1);
+			write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT));
 			if (TTY_con.cursor)
 			{
 				for (i=0; i<TTY_con.cursor; i++)
 				{
-					size = write(STDOUT_FILENO, TTY_con.buffer+i, 1);
+					write(STDOUT_FILENO, TTY_con.buffer + i, 1); // Why not write whole string at once?
 				}
 			}
 		}
@@ -168,9 +186,12 @@
 */
 void CON_Shutdown( void )
 {
+	int i;
 	if (ttycon_on)
 	{
-		CON_Back(); // Delete "]"
+		for (i = strlen(TTY_CONSOLE_PROMPT); i > 0; i--) {
+			CON_Back();
+		}
 		tcsetattr (STDIN_FILENO, TCSADRAIN, &TTY_tc);
 	}
 
@@ -186,6 +207,8 @@
 void Hist_Add(field_t *field)
 {
 	int i;
+	if (!field->cursor) { return; }	// Don't save blank lines in history.
+					// We could have checked field->buffer[0] instead.
 	assert(hist_count <= CON_HISTORY);
 	assert(hist_count >= 0);
 	assert(hist_current >= -1);
@@ -315,6 +338,8 @@
 	tc.c_cc[VTIME] = 0;
 	tcsetattr (STDIN_FILENO, TCSADRAIN, &tc);
 	ttycon_on = qtrue;
+	CON_Hide(); // These two lines cause the tty prompt to
+	CON_Show(); // appear right away.  It's a problem on clients.
 }
 
 /*
@@ -329,7 +354,6 @@
 	int avail;
 	char key;
 	field_t *history;
-	size_t size;
 
 	if(ttycon_on)
 	{
@@ -354,14 +378,58 @@
 			{
 				if (key == '\n')
 				{
-					// push it in history
+#ifndef DEDICATED
+					if (TTY_con.cursor &&	// We can either check TTY_con.cursor or
+								// TTY_con.buffer[0].  I'm not sure which one
+								// is cleaner.  You can decide that.  Same
+								// goes for the last boolean below - we could
+								// check strlen(TTY_con.buffer) there.
+							TTY_con.buffer[0] != '/' &&
+							TTY_con.buffer[0] != '\\' &&
+							TTY_con.cursor < MAX_EDIT_LINE - 1) {
+						// Add backslash to beginning of command to make it
+						// clear that only commands are accepted.  Also this is consistent
+						// with the autocomplete feature, which adds a backslash.  We don't
+						// add the backslash if the command buffer is at capacity because
+						// that would truncate the command (poor user experience).  Prepending
+						// the backslash is not necessary; it's only to improve user experience.
+						text[0] = '\\';
+						Q_strncpyz(text + 1, TTY_con.buffer, sizeof(text) - 1);
+						Q_strncpyz(TTY_con.buffer, text, sizeof(TTY_con.buffer));
+						TTY_con.cursor++;
+					}
+					else {
+						Q_strncpyz(text, TTY_con.buffer, sizeof(text));
+					}
 					Hist_Add(&TTY_con);
+#ifdef TTY_COMMANDS_VISIBLE_IN_GAME
+					CON_Hide();
+					Com_Printf("%s%s\n", TTY_CONSOLE_PROMPT, TTY_con.buffer);
+					Field_Clear(&TTY_con);
+					CON_Show();
+#else
+					CON_Hide(); // make sure the added
+					CON_Show(); // backslash shows up
+					Field_Clear(&TTY_con);
+					key = '\n';
+					write(STDOUT_FILENO, &key, 1);
+					write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT));
+#endif
+					if (text[0] == '/' || text[0] == '\\') { // explicit command
+						return text + 1;
+					}
+					else {
+						return text;
+					}
+#else // DEDICATED - Below is the original code before Rambetter's fixes.
+					Hist_Add(&TTY_con);
 					Q_strncpyz(text, TTY_con.buffer, sizeof(text));
 					Field_Clear(&TTY_con);
 					key = '\n';
-					size = write(1, &key, 1);
-					size = write( 1, "]", 1 );
+					write(STDOUT_FILENO, &key, 1);
+					write(STDOUT_FILENO, TTY_CONSOLE_PROMPT, strlen(TTY_CONSOLE_PROMPT));
 					return text;
+#endif
 				}
 				if (key == '\t')
 				{
@@ -422,9 +490,9 @@
 				return NULL;
 			// push regular character
 			TTY_con.buffer[TTY_con.cursor] = key;
-			TTY_con.cursor++;
+			TTY_con.cursor++; // next char will always be '\0'
 			// print the current line (this is differential)
-			size = write(STDOUT_FILENO, &key, 1);
+			write(STDOUT_FILENO, &key, 1);
 		}
 
 		return NULL;
@@ -465,6 +533,8 @@
 */
 void CON_Print( const char *msg )
 {
+	if (!msg[0]) return;
+	
 	CON_Hide( );
 
 	if( com_ansiColor && com_ansiColor->integer )
@@ -472,5 +542,30 @@
 	else
 		fputs( msg, stderr );
 
-	CON_Show( );
+	// The special logic below prevents printing the tty prompt
+	// when this CON_Print() doesn't end with a newline.  The problem
+	// with printing the prompt in this case is that the console
+	// might get garbled when output does not fit on one line,
+	// especially if the prompt is more than one character.
+	// Note that regardless of the hide status of the console,
+	// a user will always be able to input and execute commands [when the
+	// console is turned on].  So even if the newline never arrives,
+	// the user will be able to type commands and see them typed
+	// after the text on the current line (although not pretty).
+	if (!ttycon_on) {
+		// Our CON_Hide() above didn't do anything so we can just do nothing here.
+		return;
+	}
+	// From here on it's guaranteed that CON_Hide() did increment ttycon_hide.
+	if (msg[strlen(msg) - 1] == '\n') { // ends with newline
+		CON_Show();
+		while (ttycon_show_overdue > 0) {
+			CON_Show();
+			ttycon_show_overdue--;
+		}
+	}
+	else {	// This means that ttycon_hide was incremented by CON_Hide() above
+		// so we owe the system one CON_Show() later on.
+		ttycon_show_overdue++;
+	}
 }
_______________________________________________
ioquake3 mailing list
ioquake3@lists.ioquake.org
http://lists.ioquake.org/listinfo.cgi/ioquake3-ioquake.org
By sending this message I agree to love ioquake3 and libsdl.

Reply via email to