/* ================ Posix_ConsoleInput Checks for a complete line of text typed in at the console. Return NULL if a complete line is not ready. ================ */ char* Posix_ConsoleInput() { if( tty_enabled ) { int ret; char key; bool hidden = false; while( ( ret = read( STDIN_FILENO, &key, 1 ) ) > 0 ) { if( !hidden ) { tty_Hide(); hidden = true; } switch( key ) { case 1: input_field.SetCursor( 0 ); break; case 5: input_field.SetCursor( strlen( input_field.GetBuffer() ) ); break; case 127: case 8: input_field.CharEvent( K_BACKSPACE ); break; case '\n': idStr::Copynz( input_ret, input_field.GetBuffer(), sizeof( input_ret ) ); assert( hidden ); tty_Show(); write( STDOUT_FILENO, &key, 1 ); input_field.Clear(); if( history_count < COMMAND_HISTORY ) { history[ history_count ] = input_ret; history_count++; } else { history[ history_start ] = input_ret; history_start++; history_start %= COMMAND_HISTORY; } history_current = 0; return input_ret; case '\t': input_field.AutoComplete(); break; case 27: { // enter escape sequence mode ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 ) { Sys_Printf( "dropping sequence: '27' " ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } switch( key ) { case 79: ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 ) { Sys_Printf( "dropping sequence: '27' '79' " ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } switch( key ) { case 72: // xterm only input_field.SetCursor( 0 ); break; case 70: // xterm only input_field.SetCursor( strlen( input_field.GetBuffer() ) ); break; default: Sys_Printf( "dropping sequence: '27' '79' '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } break; case 91: { ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 ) { Sys_Printf( "dropping sequence: '27' '91' " ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } switch( key ) { case 49: { ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 || key != 126 ) { Sys_Printf( "dropping sequence: '27' '91' '49' '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } // only screen and linux terms input_field.SetCursor( 0 ); break; } case 50: { ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 || key != 126 ) { Sys_Printf( "dropping sequence: '27' '91' '50' '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } // all terms input_field.KeyDownEvent( K_INS ); break; } case 52: { ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 || key != 126 ) { Sys_Printf( "dropping sequence: '27' '91' '52' '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } // only screen and linux terms input_field.SetCursor( strlen( input_field.GetBuffer() ) ); break; } case 51: { ret = read( STDIN_FILENO, &key, 1 ); if( ret <= 0 ) { Sys_Printf( "dropping sequence: '27' '91' '51' " ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } if( key == 126 ) { input_field.KeyDownEvent( K_DEL ); break; } Sys_Printf( "dropping sequence: '27' '91' '51' '%d'", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } case 65: case 66: { // history if( history_current == 0 ) { history_backup = input_field; } if( key == 65 ) { // up history_current++; } else { // down history_current--; } // history_current cycle: // 0: current edit // 1 .. Min( COMMAND_HISTORY, history_count ): back in history if( history_current < 0 ) { history_current = Min( COMMAND_HISTORY, history_count ); } else { history_current %= Min( COMMAND_HISTORY, history_count ) + 1; } int index = -1; if( history_current == 0 ) { input_field = history_backup; } else { index = history_start + Min( COMMAND_HISTORY, history_count ) - history_current; index %= COMMAND_HISTORY; assert( index >= 0 && index < COMMAND_HISTORY ); input_field.SetBuffer( history[ index ] ); } assert( hidden ); tty_Show(); return NULL; } case 67: input_field.KeyDownEvent( K_RIGHTARROW ); break; case 68: input_field.KeyDownEvent( K_LEFTARROW ); break; default: Sys_Printf( "dropping sequence: '27' '91' '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } break; } default: Sys_Printf( "dropping sequence: '27' '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } break; } default: if( key >= ' ' ) { input_field.CharEvent( key ); break; } Sys_Printf( "dropping sequence: '%d' ", key ); tty_FlushIn(); assert( hidden ); tty_Show(); return NULL; } } if( hidden ) { tty_Show(); } return NULL; } else { // disabled on OSX. works fine from a terminal, but launching from Finder is causing trouble // I'm pretty sure it could be re-enabled if needed, and just handling the Finder failure case right (TTimo) #ifndef __APPLE__ // no terminal support - read only complete lines int len; fd_set fdset; struct timeval timeout; FD_ZERO( &fdset ); FD_SET( STDIN_FILENO, &fdset ); timeout.tv_sec = 0; timeout.tv_usec = 0; if( select( 1, &fdset, NULL, NULL, &timeout ) == -1 || !FD_ISSET( 0, &fdset ) ) { return NULL; } len = read( 0, input_ret, sizeof( input_ret ) ); if( len == 0 ) { // EOF return NULL; } if( len < 1 ) { Sys_Printf( "read failed: %s\n", strerror( errno ) ); // something bad happened, cancel this line and print an error return NULL; } if( len == sizeof( input_ret ) ) { Sys_Printf( "read overflow\n" ); // things are likely to break, as input will be cut into pieces } input_ret[ len - 1 ] = '\0'; // rip off the \n and terminate return input_ret; #endif } return NULL; }
/* ============== ProcessEvent ============== */ bool idConsoleLocal::ProcessEvent( const sysEvent_t* event, bool forceAccept ) { const bool consoleKey = event->evType == SE_KEY && event->evValue == K_GRAVE && com_allowConsole.GetBool(); // we always catch the console key event if( !forceAccept && consoleKey ) { // ignore up events if( event->evValue2 == 0 ) { return true; } consoleField.ClearAutoComplete(); // a down event will toggle the destination lines if( keyCatching ) { Close(); Sys_GrabMouseCursor( true ); } else { consoleField.Clear(); keyCatching = true; if( idKeyInput::IsDown( K_LSHIFT ) || idKeyInput::IsDown( K_RSHIFT ) ) { // if the shift key is down, don't open the console as much SetDisplayFraction( 0.2f ); } else { SetDisplayFraction( 0.5f ); } } return true; } // if we aren't key catching, dump all the other events if( !forceAccept && !keyCatching ) { return false; } // handle key and character events if( event->evType == SE_CHAR ) { // never send the console key as a character if( event->evValue != '`' && event->evValue != '~' ) { consoleField.CharEvent( event->evValue ); } return true; } if( event->evType == SE_KEY ) { // ignore up key events if( event->evValue2 == 0 ) { return true; } KeyDownEvent( event->evValue ); return true; } // we don't handle things like mouse, joystick, and network packets return false; }