ioq3/code/sys/con_win32.c
Tony J. White = e46fe24426 * rewrite of the win32 dedicated console:
1) NET_Sleep() no longer watches for input, Sys_Sleep() added for waiting
     on input.
  2) Added "CtrlHandler" for trapping Ctrl-C and other quit methods not
     handled by signals on windows
  3) Added history support
  4) Added tab completion
  5) Removed automatic cursor/scroll adjustment (too problematic)
  6) Enable mousewheel scrolling
  7) Stop using the InputBuffer for editing

  This seems to work pretty well now, but I jumped the gun on a previous
  commit message by saying you can scroll now without locking up your server.
  That was only true up until the point that a server tried to print to
  the console, at that point it will hang until you release the scroll bar :(
  It may be possible to get around this by using a seperate thread for
  console output, but that's a whole new can of worms.
2007-09-15 02:22:58 +00:00

355 lines
7.8 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code 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.
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "../qcommon/q_shared.h"
#include "../qcommon/qcommon.h"
#include "sys_local.h"
#include "windows.h"
#define QCONSOLE_HISTORY 32
static WORD qconsole_attrib;
// saved console status
static DWORD qconsole_orig_mode;
static CONSOLE_CURSOR_INFO qconsole_orig_cursorinfo;
// cmd history
static char qconsole_history[ QCONSOLE_HISTORY ][ MAX_EDIT_LINE ];
static int qconsole_history_pos = -1;
static int qconsole_history_oldest = 0;
// current edit buffer
static char qconsole_line[ MAX_EDIT_LINE ];
static int qconsole_linelen = 0;
static HANDLE qconsole_hout;
static HANDLE qconsole_hin;
/*
==================
CON_CtrlHandler
The Windows Console doesn't use signals for terminating the application
with Ctrl-C, logging off, window closing, etc. Instead it uses a special
handler routine. Fortunately, the values for Ctrl signals don't seem to
overlap with true signal codes that Windows provides, so calling
Sys_SigHandler() with those numbers should be safe for generating unique
shutdown messages.
==================
*/
static BOOL WINAPI CON_CtrlHandler( DWORD sig )
{
Sys_SigHandler( sig );
return TRUE;
}
/*
==================
CON_HistAdd
==================
*/
static void CON_HistAdd( void )
{
Q_strncpyz( qconsole_history[ qconsole_history_oldest ], qconsole_line,
sizeof( qconsole_history[ qconsole_history_oldest ] ) );
if( qconsole_history_oldest >= QCONSOLE_HISTORY - 1 )
qconsole_history_oldest = 0;
else
qconsole_history_oldest++;
qconsole_history_pos = qconsole_history_oldest;
}
/*
==================
CON_HistPrev
==================
*/
static void CON_HistPrev( void )
{
int pos;
pos = ( qconsole_history_pos < 1 ) ?
( QCONSOLE_HISTORY - 1 ) : ( qconsole_history_pos - 1 );
// don' t allow looping through history
if( pos == qconsole_history_oldest )
return;
qconsole_history_pos = pos;
Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ],
sizeof( qconsole_line ) );
qconsole_linelen = strlen( qconsole_line );
}
/*
==================
CON_HistNext
==================
*/
static void CON_HistNext( void )
{
int pos;
pos = ( qconsole_history_pos >= QCONSOLE_HISTORY - 1 ) ?
0 : ( qconsole_history_pos + 1 );
// clear the edit buffer if they try to advance to a future command
if( pos == qconsole_history_oldest )
{
qconsole_line[ 0 ] = '\0';
qconsole_linelen = 0;
return;
}
qconsole_history_pos = pos;
Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ],
sizeof( qconsole_line ) );
qconsole_linelen = strlen( qconsole_line );
}
/*
==================
CON_Hide
==================
*/
void CON_Hide( void )
{
}
/*
==================
CON_Show
==================
*/
void CON_Show( void )
{
CONSOLE_SCREEN_BUFFER_INFO binfo;
COORD writeSize = { MAX_EDIT_LINE, 1 };
COORD writePos = { 0, 0 };
SMALL_RECT writeArea = { 0, 0, 0, 0 };
int i;
CHAR_INFO line[ MAX_EDIT_LINE ];
GetConsoleScreenBufferInfo( qconsole_hout, &binfo );
// if we' re in the middle of printf, don't bother writing the buffer
if( binfo.dwCursorPosition.X != 0 )
return;
writeArea.Left = 0;
writeArea.Top = binfo.dwCursorPosition.Y;
writeArea.Bottom = binfo.dwCursorPosition.Y;
writeArea.Right = MAX_EDIT_LINE;
// build a space-padded CHAR_INFO array
for( i = 0; i < MAX_EDIT_LINE; i++ )
{
if( i < qconsole_linelen )
line[ i ].Char.AsciiChar = qconsole_line[ i ];
else
line[ i ].Char.AsciiChar = ' ';
line[ i ].Attributes = qconsole_attrib;
}
if( qconsole_linelen > binfo.srWindow.Right )
{
WriteConsoleOutput( qconsole_hout,
line + (qconsole_linelen - binfo.srWindow.Right ),
writeSize, writePos, &writeArea );
}
else
{
WriteConsoleOutput( qconsole_hout, line, writeSize,
writePos, &writeArea );
}
}
/*
==================
CON_Shutdown
==================
*/
void CON_Shutdown( void )
{
SetConsoleMode( qconsole_hin, qconsole_orig_mode );
SetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo );
CloseHandle( qconsole_hout );
CloseHandle( qconsole_hin );
}
/*
==================
CON_Init
==================
*/
void CON_Init( void )
{
CONSOLE_CURSOR_INFO curs;
CONSOLE_SCREEN_BUFFER_INFO info;
int i;
// handle Ctrl-C or other console termination
SetConsoleCtrlHandler( CON_CtrlHandler, TRUE );
qconsole_hin = GetStdHandle( STD_INPUT_HANDLE );
if( qconsole_hin == INVALID_HANDLE_VALUE )
return;
qconsole_hout = GetStdHandle( STD_OUTPUT_HANDLE );
if( qconsole_hout == INVALID_HANDLE_VALUE )
return;
GetConsoleMode( qconsole_hin, &qconsole_orig_mode );
// allow mouse wheel scrolling
SetConsoleMode( qconsole_hin,
qconsole_orig_mode & ~ENABLE_MOUSE_INPUT );
FlushConsoleInputBuffer( qconsole_hin );
GetConsoleScreenBufferInfo( qconsole_hout, &info );
qconsole_attrib = info.wAttributes;
SetConsoleTitle("ioquake3 Dedicated Server Console");
// make cursor invisible
GetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo );
curs.dwSize = 1;
curs.bVisible = FALSE;
SetConsoleCursorInfo( qconsole_hout, &curs );
// initialize history
for( i = 0; i < QCONSOLE_HISTORY; i++ )
qconsole_history[ i ][ 0 ] = '\0';
}
/*
==================
CON_ConsoleInput
==================
*/
char *CON_ConsoleInput( void )
{
INPUT_RECORD buff[ MAX_EDIT_LINE ];
DWORD count = 0, events = 0;
WORD key = 0;
int i;
int newlinepos = -1;
if( !GetNumberOfConsoleInputEvents( qconsole_hin, &events ) )
return NULL;
if( events < 1 )
return NULL;
// if we have overflowed, start dropping oldest input events
if( events >= MAX_EDIT_LINE )
{
ReadConsoleInput( qconsole_hin, buff, 1, &events );
return NULL;
}
if( !ReadConsoleInput( qconsole_hin, buff, events, &count ) )
return NULL;
FlushConsoleInputBuffer( qconsole_hin );
for( i = 0; i < count; i++ )
{
if( buff[ i ].EventType != KEY_EVENT )
continue;
if( !buff[ i ].Event.KeyEvent.bKeyDown )
continue;
key = buff[ i ].Event.KeyEvent.wVirtualKeyCode;
if( key == VK_RETURN )
{
newlinepos = i;
break;
}
else if( key == VK_UP )
{
CON_HistPrev();
break;
}
else if( key == VK_DOWN )
{
CON_HistNext();
break;
}
else if( key == VK_TAB )
{
field_t f;
Q_strncpyz( f.buffer, qconsole_line,
sizeof( f.buffer ) );
Field_AutoComplete( &f );
Q_strncpyz( qconsole_line, f.buffer,
sizeof( qconsole_line ) );
qconsole_linelen = strlen( qconsole_line );
break;
}
if( qconsole_linelen < sizeof( qconsole_line ) - 1 )
{
char c = buff[ i ].Event.KeyEvent.uChar.AsciiChar;
if( key == VK_BACK )
{
int pos = ( qconsole_linelen > 0 ) ?
qconsole_linelen - 1 : 0;
qconsole_line[ pos ] = '\0';
qconsole_linelen = pos;
}
else if( c )
{
qconsole_line[ qconsole_linelen++ ] = c;
qconsole_line[ qconsole_linelen ] = '\0';
}
}
}
CON_Show();
if( newlinepos < 0)
return NULL;
if( !qconsole_linelen )
{
Com_Printf( "\n" );
return NULL;
}
CON_HistAdd();
Com_Printf( "%s\n", qconsole_line );
qconsole_linelen = 0;
return qconsole_line;
}