diff --git a/README b/README index 72e02192..c80facae 100644 --- a/README +++ b/README @@ -101,6 +101,10 @@ New cvars cl_autoRecordDemo - record a new demo on each map change cl_aviFrameRate - the framerate to use when capturing video cl_aviMotionJpeg - use the mjpeg codec when capturing video + cl_guidServerUniq - makes cl_guid unique for each server + cl_cURLLib - filename of cURL library to load + cl_consoleKeys - space delimited list of key names or + characters that toggle the console s_useOpenAL - use the OpenAL sound backend if available s_alPrecache - cache OpenAL sounds before use @@ -138,8 +142,6 @@ New cvars in_joystickNo - select which joystick to use in_keyboardDebug - print keyboard debug info r_ext_texture_filter_anisotropic - anisotropic texture filtering - cl_guidServerUniq - makes cl_guid unique for each server - cl_cURLLib - filename of cURL library to load sv_dlURL - the base of the HTTP or FTP site that holds custom pk3 files for your server @@ -379,20 +381,39 @@ SDL Keyboard Differences * "Caps Lock" and "Num Lock" can not be used as normal binds since they do not send a KEYUP event until the key is pressed again. - * SDL > 1.2.9 does not support disabling "Dead Key" recognition. - In order to send "Dead Key" characters (e.g. ~, ', `, and ^), you - must key a Space (or sometimes the same character again) after the - character to send it on many international keyboard layouts. + * SDL > 1.2.9 does not support disabling dead key recognition. In order to + send dead key characters (e.g. ~, ', `, and ^), you must key a Space (or + sometimes the same character again) after the character to send it on + many international keyboard layouts. * The SDL client supports many more keys than the original Quake3 client. For example the keys: "Windows", "SysReq", "ScrollLock", and "Break". - For non-US keyboards, all of the so called "World" keys are now - supported as well as F13, F14, F15, and the country-specific - mode/meta keys. + For non-US keyboards, all of the so called "World" keys are now supported + as well as F13, F14, F15, and the country-specific mode/meta keys. - SDL's "Dead Key" behaviour makes the hard-coded toggleConsole binds ~ and ` - annoying to use on many non-US keyboards. In response, an additional - toggleConsole bind has been added on the key combination Shift-Esc. + On many international layouts the default console toggle keys are also dead + keys, meaning that dropping the console potentially results in + unintentionally initiating the keying of a dead key. Futhermore SDL 1.2's + dead key support is broken by design and Q3 doesn't support non-ASCII text + entry, so the chances are you won't get the correct character anyway. + + If you use such a keyboard layout, you can set the cvar cl_consoleKeys. This + is a space delimited list of key names that will toggle the console. The key + names are the usual Q3 names e.g. "BACKSPACE", "PAUSE", "WINDOWS" etc. It's + also possible to use ASCII characters, either by hexadecimal number e.g. + "0x7e" (equivalent to ~) or directly by character. Some example values for + cl_consoleKeys: + + "0x7e 0x60" Toggle on ~ or ` (the default) + "WINDOWS" Toggle on the Windows key + "c" Toggle on the c character + "C" Toggle on the C character (Shift-c) + "PAUSE F1 PGUP" Toggle on the Pause, F1 or Page Up keys + + Note that when you elect a set of console keys or characters, they cannot + then be used for binding, nor will they generate characters when entering + text. Also, in addition to the nominated console keys, Shift-ESC is hard + coded to always toggle the console. Mouse Input On Windows ioq3 uses SDL to abstract away as much as possible from platform specific diff --git a/code/client/cl_keys.c b/code/client/cl_keys.c index b7fb2fa6..fea1b747 100644 --- a/code/client/cl_keys.c +++ b/code/client/cl_keys.c @@ -1172,7 +1172,7 @@ void CL_KeyEvent (int key, qboolean down, unsigned time) { } // console key is hardcoded, so the user can never unbind it - if (key == '`' || key == '~' || + if (key == K_CONSOLE || ( key == K_ESCAPE && keys[K_SHIFT].down ) ) { if (!down) { return; @@ -1315,11 +1315,6 @@ Normal keyboard characters, already shifted / capslocked / etc =================== */ void CL_CharEvent( int key ) { - // the console key should never be used as a char - if ( key == '`' || key == '~' ) { - return; - } - // delete is not a printable character and is // otherwise handled by Field_KeyDownEvent if ( key == 127 ) { diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 98686ad2..45f884fb 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -96,6 +96,8 @@ cvar_t *cl_lanForcePackets; cvar_t *cl_guidServerUniq; +cvar_t *cl_consoleKeys; + clientActive_t cl; clientConnection_t clc; clientStatic_t cls; @@ -3065,6 +3067,9 @@ void CL_Init( void ) { cl_guidServerUniq = Cvar_Get ("cl_guidServerUniq", "1", CVAR_ARCHIVE); + // 0x7e = ~ and 0x60 = ` + cl_consoleKeys = Cvar_Get( "cl_consoleKeys", "0x7e 0x60", CVAR_ARCHIVE); + // userinfo Cvar_Get ("name", "UnnamedPlayer", CVAR_USERINFO | CVAR_ARCHIVE ); Cvar_Get ("rate", "3000", CVAR_USERINFO | CVAR_ARCHIVE ); diff --git a/code/client/client.h b/code/client/client.h index aa4e3890..5ee30c29 100644 --- a/code/client/client.h +++ b/code/client/client.h @@ -401,6 +401,8 @@ extern cvar_t *cl_inGameVideo; extern cvar_t *cl_lanForcePackets; extern cvar_t *cl_autoRecordDemo; +extern cvar_t *cl_consoleKeys; + #ifdef USE_MUMBLE extern cvar_t *cl_useMumble; extern cvar_t *cl_mumbleScale; @@ -489,6 +491,7 @@ void IN_CenterView (void); void CL_VerifyCode( void ); float CL_KeyState (kbutton_t *key); +int Key_StringToKeynum( char *str ); char *Key_KeynumToString (int keynum); // diff --git a/code/client/keycodes.h b/code/client/keycodes.h index c6e6412a..706ca1b0 100644 --- a/code/client/keycodes.h +++ b/code/client/keycodes.h @@ -260,6 +260,9 @@ typedef enum { K_EURO, K_UNDO, + // Pseudo-key that brings the console down + K_CONSOLE, + MAX_KEYS } keyNum_t; diff --git a/code/sdl/sdl_input.c b/code/sdl/sdl_input.c index 669088e1..878943c0 100644 --- a/code/sdl/sdl_input.c +++ b/code/sdl/sdl_input.c @@ -77,7 +77,7 @@ static cvar_t *in_joystickNo = NULL; IN_PrintKey =============== */ -static void IN_PrintKey( const SDL_keysym *keysym, int key, qboolean down ) +static void IN_PrintKey( const SDL_keysym *keysym, keyNum_t key, qboolean down ) { if( down ) Com_Printf( "+ " ); @@ -100,15 +100,66 @@ static void IN_PrintKey( const SDL_keysym *keysym, int key, qboolean down ) if( keysym->mod & KMOD_MODE ) Com_Printf( " KMOD_MODE" ); if( keysym->mod & KMOD_RESERVED ) Com_Printf( " KMOD_RESERVED" ); + Com_Printf( " Q:%d(%s)", key, Key_KeynumToString( key ) ); + if( keysym->unicode ) { - Com_Printf( " %d", keysym->unicode ); + Com_Printf( " U:%d", keysym->unicode ); if( keysym->unicode > ' ' && keysym->unicode < '~' ) Com_Printf( "(%c)", (char)keysym->unicode ); } - Com_Printf( " %d(%s)\n", key, Key_KeynumToString( key ) ); + Com_Printf( "\n" ); +} + +#define MAX_CONSOLE_KEYS 16 + +/* +=============== +IN_IsConsoleKey +=============== +*/ +static qboolean IN_IsConsoleKey( keyNum_t key, const char *buf ) +{ + static int consoleKeys[ MAX_CONSOLE_KEYS ]; + static int numConsoleKeys = 0; + int i; + + // Only parse the variable when it changes + if( cl_consoleKeys->modified ) + { + char *text_p, *token; + + cl_consoleKeys->modified = qfalse; + text_p = cl_consoleKeys->string; + numConsoleKeys = 0; + + while( numConsoleKeys < MAX_CONSOLE_KEYS ) + { + token = COM_Parse( &text_p ); + if( !token[ 0 ] ) + break; + + consoleKeys[ numConsoleKeys++ ] = + Key_StringToKeynum( token ); + } + } + + // If key is ASCII, use the character instead + if( key >= K_SPACE && key < K_BACKSPACE ) + key = 0; + + for( i = 0; i < numConsoleKeys; i++ ) + { + if( !consoleKeys[ i ] ) + continue; + + if( consoleKeys[ i ] == key || consoleKeys[ i ] == *buf ) + return qtrue; + } + + return qfalse; } /* @@ -117,7 +168,7 @@ IN_TranslateSDLToQ3Key =============== */ static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, - int *key, qboolean down ) + keyNum_t *key, qboolean down ) { static char buf[ 2 ] = { '\0', '\0' }; @@ -131,7 +182,7 @@ static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, } else { - switch (keysym->sym) + switch( keysym->sym ) { case SDLK_PAGEUP: *key = K_PGUP; break; case SDLK_KP9: *key = K_KP_PGUP; break; @@ -218,47 +269,35 @@ static const char *IN_TranslateSDLToQ3Key( SDL_keysym *keysym, } } - if( down ) + if( down && keysym->unicode && !( keysym->unicode & 0xFF80 ) ) { - if( keysym->unicode && !( keysym->unicode & 0xFF80 ) ) + char ch = (char)keysym->unicode & 0x7F; + + switch( ch ) { - char ch = (char)keysym->unicode & 0x7F; + case 127: // ASCII delete + if( *key != K_DEL ) + { + // ctrl-h + *buf = CTRL('h'); + break; + } + // fallthrough - switch( ch ) - { - // So the key marked ~ always drops the console - case '~': *key = '~'; break; - - case 127: // ASCII delete - if( *key != K_DEL ) - { - // ctrl-h - *buf = CTRL('h'); - break; - } - // fallthrough - - default: *buf = ch; break; - } - } - else - { - // Unicode character which isn't ASCII, possibly the character - // following a dead key. Fallback on what SDL calls the key - - const char *keyString = SDL_GetKeyName( keysym->sym ); - if( strlen( keyString ) == 1 ) - *buf = *keyString; + default: *buf = ch; break; } } - // Never allow a '~' SE_CHAR event to be generated - if( *key == '~' ) - *buf = '\0'; - if( in_keyboardDebug->integer ) IN_PrintKey( keysym, *key, down ); + if( IN_IsConsoleKey( *key, buf ) ) + { + // Console keys can't be bound or generate characters + *key = K_CONSOLE; + *buf = '\0'; + } + return buf; } @@ -737,7 +776,7 @@ static void IN_ProcessEvents( void ) { SDL_Event e; const char *p = NULL; - int key = 0; + keyNum_t key = 0; if( !SDL_WasInit( SDL_INIT_VIDEO ) ) return;