📄 cl_keys.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 Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
#include "client.h"
/*
key up events are sent even if in console mode
*/
field_t historyEditLines[COMMAND_HISTORY];
int nextHistoryLine; // the last line in the history buffer, not masked
int historyLine; // the line being displayed from history buffer
// will be <= nextHistoryLine
field_t g_consoleField;
field_t chatField;
qboolean chat_team;
int chat_playerNum;
qboolean key_overstrikeMode;
qboolean anykeydown;
qkey_t keys[MAX_KEYS];
typedef struct {
char *name;
int keynum;
} keyname_t;
// names not in this list can either be lowercase ascii, or '0xnn' hex sequences
keyname_t keynames[] =
{
{"TAB", K_TAB},
{"ENTER", K_ENTER},
{"ESCAPE", K_ESCAPE},
{"SPACE", K_SPACE},
{"BACKSPACE", K_BACKSPACE},
{"UPARROW", K_UPARROW},
{"DOWNARROW", K_DOWNARROW},
{"LEFTARROW", K_LEFTARROW},
{"RIGHTARROW", K_RIGHTARROW},
{"ALT", K_ALT},
{"CTRL", K_CTRL},
{"SHIFT", K_SHIFT},
{"COMMAND", K_COMMAND},
{"CAPSLOCK", K_CAPSLOCK},
{"F1", K_F1},
{"F2", K_F2},
{"F3", K_F3},
{"F4", K_F4},
{"F5", K_F5},
{"F6", K_F6},
{"F7", K_F7},
{"F8", K_F8},
{"F9", K_F9},
{"F10", K_F10},
{"F11", K_F11},
{"F12", K_F12},
{"INS", K_INS},
{"DEL", K_DEL},
{"PGDN", K_PGDN},
{"PGUP", K_PGUP},
{"HOME", K_HOME},
{"END", K_END},
{"MOUSE1", K_MOUSE1},
{"MOUSE2", K_MOUSE2},
{"MOUSE3", K_MOUSE3},
{"MOUSE4", K_MOUSE4},
{"MOUSE5", K_MOUSE5},
{"MWHEELUP", K_MWHEELUP },
{"MWHEELDOWN", K_MWHEELDOWN },
{"JOY1", K_JOY1},
{"JOY2", K_JOY2},
{"JOY3", K_JOY3},
{"JOY4", K_JOY4},
{"JOY5", K_JOY5},
{"JOY6", K_JOY6},
{"JOY7", K_JOY7},
{"JOY8", K_JOY8},
{"JOY9", K_JOY9},
{"JOY10", K_JOY10},
{"JOY11", K_JOY11},
{"JOY12", K_JOY12},
{"JOY13", K_JOY13},
{"JOY14", K_JOY14},
{"JOY15", K_JOY15},
{"JOY16", K_JOY16},
{"JOY17", K_JOY17},
{"JOY18", K_JOY18},
{"JOY19", K_JOY19},
{"JOY20", K_JOY20},
{"JOY21", K_JOY21},
{"JOY22", K_JOY22},
{"JOY23", K_JOY23},
{"JOY24", K_JOY24},
{"JOY25", K_JOY25},
{"JOY26", K_JOY26},
{"JOY27", K_JOY27},
{"JOY28", K_JOY28},
{"JOY29", K_JOY29},
{"JOY30", K_JOY30},
{"JOY31", K_JOY31},
{"JOY32", K_JOY32},
{"AUX1", K_AUX1},
{"AUX2", K_AUX2},
{"AUX3", K_AUX3},
{"AUX4", K_AUX4},
{"AUX5", K_AUX5},
{"AUX6", K_AUX6},
{"AUX7", K_AUX7},
{"AUX8", K_AUX8},
{"AUX9", K_AUX9},
{"AUX10", K_AUX10},
{"AUX11", K_AUX11},
{"AUX12", K_AUX12},
{"AUX13", K_AUX13},
{"AUX14", K_AUX14},
{"AUX15", K_AUX15},
{"AUX16", K_AUX16},
{"KP_HOME", K_KP_HOME },
{"KP_UPARROW", K_KP_UPARROW },
{"KP_PGUP", K_KP_PGUP },
{"KP_LEFTARROW", K_KP_LEFTARROW },
{"KP_5", K_KP_5 },
{"KP_RIGHTARROW", K_KP_RIGHTARROW },
{"KP_END", K_KP_END },
{"KP_DOWNARROW", K_KP_DOWNARROW },
{"KP_PGDN", K_KP_PGDN },
{"KP_ENTER", K_KP_ENTER },
{"KP_INS", K_KP_INS },
{"KP_DEL", K_KP_DEL },
{"KP_SLASH", K_KP_SLASH },
{"KP_MINUS", K_KP_MINUS },
{"KP_PLUS", K_KP_PLUS },
{"KP_NUMLOCK", K_KP_NUMLOCK },
{"KP_STAR", K_KP_STAR },
{"KP_EQUALS", K_KP_EQUALS },
{"PAUSE", K_PAUSE},
{"SEMICOLON", ';'}, // because a raw semicolon seperates commands
{NULL,0}
};
/*
=============================================================================
EDIT FIELDS
=============================================================================
*/
/*
===================
Field_Draw
Handles horizontal scrolling and cursor blinking
x, y, amd width are in pixels
===================
*/
void Field_VariableSizeDraw( field_t *edit, int x, int y, int width, int size, qboolean showCursor ) {
int len;
int drawLen;
int prestep;
int cursorChar;
char str[MAX_STRING_CHARS];
int i;
drawLen = edit->widthInChars;
len = strlen( edit->buffer ) + 1;
// guarantee that cursor will be visible
if ( len <= drawLen ) {
prestep = 0;
} else {
if ( edit->scroll + drawLen > len ) {
edit->scroll = len - drawLen;
if ( edit->scroll < 0 ) {
edit->scroll = 0;
}
}
prestep = edit->scroll;
/*
if ( edit->cursor < len - drawLen ) {
prestep = edit->cursor; // cursor at start
} else {
prestep = len - drawLen;
}
*/
}
if ( prestep + drawLen > len ) {
drawLen = len - prestep;
}
// extract <drawLen> characters from the field at <prestep>
if ( drawLen >= MAX_STRING_CHARS ) {
Com_Error( ERR_DROP, "drawLen >= MAX_STRING_CHARS" );
}
Com_Memcpy( str, edit->buffer + prestep, drawLen );
str[ drawLen ] = 0;
// draw it
if ( size == SMALLCHAR_WIDTH ) {
float color[4];
color[0] = color[1] = color[2] = color[3] = 1.0;
SCR_DrawSmallStringExt( x, y, str, color, qfalse );
} else {
// draw big string with drop shadow
SCR_DrawBigString( x, y, str, 1.0 );
}
// draw the cursor
if ( !showCursor ) {
return;
}
if ( (int)( cls.realtime >> 8 ) & 1 ) {
return; // off blink
}
if ( key_overstrikeMode ) {
cursorChar = 11;
} else {
cursorChar = 10;
}
i = drawLen - ( Q_PrintStrlen( str ) + 1 );
if ( size == SMALLCHAR_WIDTH ) {
SCR_DrawSmallChar( x + ( edit->cursor - prestep - i ) * size, y, cursorChar );
} else {
str[0] = cursorChar;
str[1] = 0;
SCR_DrawBigString( x + ( edit->cursor - prestep - i ) * size, y, str, 1.0 );
}
}
void Field_Draw( field_t *edit, int x, int y, int width, qboolean showCursor )
{
Field_VariableSizeDraw( edit, x, y, width, SMALLCHAR_WIDTH, showCursor );
}
void Field_BigDraw( field_t *edit, int x, int y, int width, qboolean showCursor )
{
Field_VariableSizeDraw( edit, x, y, width, BIGCHAR_WIDTH, showCursor );
}
/*
================
Field_Paste
================
*/
void Field_Paste( field_t *edit ) {
char *cbd;
int pasteLen, i;
cbd = Sys_GetClipboardData();
if ( !cbd ) {
return;
}
// send as if typed, so insert / overstrike works properly
pasteLen = strlen( cbd );
for ( i = 0 ; i < pasteLen ; i++ ) {
Field_CharEvent( edit, cbd[i] );
}
Z_Free( cbd );
}
/*
=================
Field_KeyDownEvent
Performs the basic line editing functions for the console,
in-game talk, and menu fields
Key events are used for non-printable characters, others are gotten from char events.
=================
*/
void Field_KeyDownEvent( field_t *edit, int key ) {
int len;
// shift-insert is paste
if ( ( ( key == K_INS ) || ( key == K_KP_INS ) ) && keys[K_SHIFT].down ) {
Field_Paste( edit );
return;
}
len = strlen( edit->buffer );
if ( key == K_DEL ) {
if ( edit->cursor < len ) {
memmove( edit->buffer + edit->cursor,
edit->buffer + edit->cursor + 1, len - edit->cursor );
}
return;
}
if ( key == K_RIGHTARROW )
{
if ( edit->cursor < len ) {
edit->cursor++;
}
if ( edit->cursor >= edit->scroll + edit->widthInChars && edit->cursor <= len )
{
edit->scroll++;
}
return;
}
if ( key == K_LEFTARROW )
{
if ( edit->cursor > 0 ) {
edit->cursor--;
}
if ( edit->cursor < edit->scroll )
{
edit->scroll--;
}
return;
}
if ( key == K_HOME || ( tolower(key) == 'a' && keys[K_CTRL].down ) ) {
edit->cursor = 0;
return;
}
if ( key == K_END || ( tolower(key) == 'e' && keys[K_CTRL].down ) ) {
edit->cursor = len;
return;
}
if ( key == K_INS ) {
key_overstrikeMode = !key_overstrikeMode;
return;
}
}
/*
==================
Field_CharEvent
==================
*/
void Field_CharEvent( field_t *edit, int ch ) {
int len;
if ( ch == 'v' - 'a' + 1 ) { // ctrl-v is paste
Field_Paste( edit );
return;
}
if ( ch == 'c' - 'a' + 1 ) { // ctrl-c clears the field
Field_Clear( edit );
return;
}
len = strlen( edit->buffer );
if ( ch == 'h' - 'a' + 1 ) { // ctrl-h is backspace
if ( edit->cursor > 0 ) {
memmove( edit->buffer + edit->cursor - 1,
edit->buffer + edit->cursor, len + 1 - edit->cursor );
edit->cursor--;
if ( edit->cursor < edit->scroll )
{
edit->scroll--;
}
}
return;
}
if ( ch == 'a' - 'a' + 1 ) { // ctrl-a is home
edit->cursor = 0;
edit->scroll = 0;
return;
}
if ( ch == 'e' - 'a' + 1 ) { // ctrl-e is end
edit->cursor = len;
edit->scroll = edit->cursor - edit->widthInChars;
return;
}
//
// ignore any other non printable chars
//
if ( ch < 32 ) {
return;
}
if ( key_overstrikeMode ) {
if ( edit->cursor == MAX_EDIT_LINE - 1 )
return;
edit->buffer[edit->cursor] = ch;
edit->cursor++;
} else { // insert mode
if ( len == MAX_EDIT_LINE - 1 ) {
return; // all full
}
memmove( edit->buffer + edit->cursor + 1,
edit->buffer + edit->cursor, len + 1 - edit->cursor );
edit->buffer[edit->cursor] = ch;
edit->cursor++;
}
if ( edit->cursor >= edit->widthInChars ) {
edit->scroll++;
}
if ( edit->cursor == len + 1) {
edit->buffer[edit->cursor] = 0;
}
}
/*
=============================================================================
CONSOLE LINE EDITING
==============================================================================
*/
/*
====================
Console_Key
Handles history and console scrollback
====================
*/
void Console_Key (int key) {
// ctrl-L clears screen
if ( key == 'l' && keys[K_CTRL].down ) {
Cbuf_AddText ("clear\n");
return;
}
// enter finishes the line
if ( key == K_ENTER || key == K_KP_ENTER ) {
// if not in the game explicitly prepent a slash if needed
if ( cls.state != CA_ACTIVE && g_consoleField.buffer[0] != '\\'
&& g_consoleField.buffer[0] != '/' ) {
char temp[MAX_STRING_CHARS];
Q_strncpyz( temp, g_consoleField.buffer, sizeof( temp ) );
Com_sprintf( g_consoleField.buffer, sizeof( g_consoleField.buffer ), "\\%s", temp );
g_consoleField.cursor++;
}
Com_Printf ( "]%s\n", g_consoleField.buffer );
// leading slash is an explicit command
if ( g_consoleField.buffer[0] == '\\' || g_consoleField.buffer[0] == '/' ) {
Cbuf_AddText( g_consoleField.buffer+1 ); // valid command
Cbuf_AddText ("\n");
} else {
// other text will be chat messages
if ( !g_consoleField.buffer[0] ) {
return; // empty lines just scroll the console without adding to history
} else {
Cbuf_AddText ("cmd say ");
Cbuf_AddText( g_consoleField.buffer );
Cbuf_AddText ("\n");
}
}
// copy line to history buffer
historyEditLines[nextHistoryLine % COMMAND_HISTORY] = g_consoleField;
nextHistoryLine++;
historyLine = nextHistoryLine;
Field_Clear( &g_consoleField );
g_consoleField.widthInChars = g_console_field_width;
if ( cls.state == CA_DISCONNECTED ) {
SCR_UpdateScreen (); // force an update, because the command
} // may take some time
return;
}
// command completion
if (key == K_TAB) {
Field_CompleteCommand(&g_consoleField);
return;
}
// command history (ctrl-p ctrl-n for unix style)
if ( (key == K_MWHEELUP && keys[K_SHIFT].down) || ( key == K_UPARROW ) || ( key == K_KP_UPARROW ) ||
( ( tolower(key) == 'p' ) && keys[K_CTRL].down ) ) {
if ( nextHistoryLine - historyLine < COMMAND_HISTORY
&& historyLine > 0 ) {
historyLine--;
}
g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
return;
}
if ( (key == K_MWHEELDOWN && keys[K_SHIFT].down) || ( key == K_DOWNARROW ) || ( key == K_KP_DOWNARROW ) ||
( ( tolower(key) == 'n' ) && keys[K_CTRL].down ) ) {
if (historyLine == nextHistoryLine)
return;
historyLine++;
g_consoleField = historyEditLines[ historyLine % COMMAND_HISTORY ];
return;
}
// console scrolling
if ( key == K_PGUP ) {
Con_PageUp();
return;
}
if ( key == K_PGDN) {
Con_PageDown();
return;
}
if ( key == K_MWHEELUP) { //----(SA) added some mousewheel functionality to the console
Con_PageUp();
if(keys[K_CTRL].down) { // hold <ctrl> to accelerate scrolling
Con_PageUp();
Con_PageUp();
}
return;
}
if ( key == K_MWHEELDOWN) { //----(SA) added some mousewheel functionality to the console
Con_PageDown();
if(keys[K_CTRL].down) { // hold <ctrl> to accelerate scrolling
Con_PageDown();
Con_PageDown();
}
return;
}
// ctrl-home = top of console
if ( key == K_HOME && keys[K_CTRL].down ) {
Con_Top();
return;
}
// ctrl-end = bottom of console
if ( key == K_END && keys[K_CTRL].down ) {
Con_Bottom();
return;
}
// pass to the normal editline routine
Field_KeyDownEvent( &g_consoleField, key );
}
//============================================================================
/*
================
Message_Key
In game talk message
================
*/
void Message_Key( int key ) {
char buffer[MAX_STRING_CHARS];
if (key == K_ESCAPE) {
cls.keyCatchers &= ~KEYCATCH_MESSAGE;
Field_Clear( &chatField );
return;
}
if ( key == K_ENTER || key == K_KP_ENTER )
{
if ( chatField.buffer[0] && cls.state == CA_ACTIVE ) {
if (chat_playerNum != -1 )
Com_sprintf( buffer, sizeof( buffer ), "tell %i \"%s\"\n", chat_playerNum, chatField.buffer );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -