From 57d48b64ebef9244a5ae9639c886c999313e284b Mon Sep 17 00:00:00 2001 From: mittorn Date: Mon, 28 Oct 2019 12:28:52 +0700 Subject: [PATCH] keys: add OSK --- engine/client/cl_view.c | 1 + engine/client/client.h | 20 +++ engine/client/keys.c | 297 ++++++++++++++++++++++++++++++++++++++++ engine/common/common.h | 18 --- 4 files changed, 318 insertions(+), 18 deletions(-) diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index dd52b369..050bdaaf 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -486,6 +486,7 @@ void V_PostRender( void ) Con_DrawVersion(); Con_DrawDebug(); // must be last Touch_Draw(); + OSK_Draw(); S_ExtraUpdate(); } diff --git a/engine/client/client.h b/engine/client/client.h index a6a92296..00b8b61c 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1119,6 +1119,26 @@ void SCR_RunCinematic( void ); void SCR_StopCinematic( void ); void CL_PlayVideo_f( void ); + +// +// keys.c +// +int Key_IsDown( int keynum ); +const char *Key_IsBind( int keynum ); +void Key_Event( int key, int down ); +void Key_Init( void ); +void Key_WriteBindings( file_t *f ); +const char *Key_GetBinding( int keynum ); +void Key_SetBinding( int keynum, const char *binding ); +void Key_ClearStates( void ); +const char *Key_KeynumToString( int keynum ); +int Key_StringToKeynum( const char *str ); +int Key_GetKey( const char *binding ); +void Key_EnumCmds_f( void ); +void Key_SetKeyDest( int key_dest ); +void Key_EnableTextInput( qboolean enable, qboolean force ); +void OSK_Draw( void ); + extern rgba_t g_color_table[8]; #endif//CLIENT_H diff --git a/engine/client/keys.c b/engine/client/keys.c index b70d3b84..8ceec3d2 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -140,6 +140,10 @@ keyname_t keynames[] = {NULL, 0, NULL }, }; +static void OSK_EnableTextInput( qboolean enable, qboolean force ); +static qboolean OSK_KeyEvent( int key, int down ); +static convar_t *osk_enable; + /* =================== Key_IsDown @@ -514,6 +518,8 @@ void Key_Init( void ) // setup default binding. "unbindall" from config.cfg will be reset it for( kn = keynames; kn->name; kn++ ) Key_SetBinding( kn->keynum, kn->binding ); + + osk_enable = Cvar_Get( "osk_enable", "0", FCVAR_ARCHIVE, "enable build-in on-screen keyboard" ); } /* @@ -597,6 +603,9 @@ void Key_Event( int key, int down ) { const char *kb; + if( OSK_KeyEvent( key, down ) ) + return; + // key was pressed before engine was run if( !keys[key].down && !down ) return; @@ -749,6 +758,11 @@ Key_EnableTextInput */ void Key_EnableTextInput( qboolean enable, qboolean force ) { + if( CVAR_TO_BOOL( osk_enable ) ) + { + OSK_EnableTextInput( enable, force ); + return; + } if( enable && ( !host.textmode || force ) ) Platform_EnableTextInput( true ); else if( !enable ) @@ -845,3 +859,286 @@ void CL_CharEvent( int key ) UI_CharEvent( key ); } } + + +/* On-screen keyboard: + * + * 4 lines with 13 buttons each + * Left trigger == backspace + * Right trigger == space + * Any button press is button press on keyboard + * + * Our layout: + * 0 1 2 3 4 5 6 7 8 9 10 11 12 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |` |1 |2 |3 |4 |5 |6 |7 |8 |9 |0 |- |= | 0 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |q |w |e |r |t |y |u |i |o |p |[ |] |\ | 1 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |CL|a |s |d |f |g |h |j |k |l |; |' |BS| 2 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + * |SH|z |x |c |v |b |n |m |, |. |/ |SP|EN| 3 + * +--+--+--+--+--+--+--+--+--+--+--+--+--+ + */ + +#define MAX_OSK_ROWS 13 +#define MAX_OSK_LINES 4 + +enum +{ + OSK_DEFAULT = 0, + OSK_UPPER, // on caps, shift + /* + OSK_RUSSIAN, + OSK_RUSSIAN_UPPER, + */ + OSK_LAST +}; + +enum +{ + OSK_TAB = 16, + OSK_SHIFT, + OSK_BACKSPACE, + OSK_ENTER, + OSK_SPECKEY_LAST +}; +static const char *osk_keylayout[][4] = +{ + { + "`1234567890-=", // 13 + "qwertyuiop[]\\", // 13 + "\x10" "asdfghjkl;'" "\x12", // 11 + caps on a left, enter on a right + "\x11" "zxcvbnm,./ " "\x13" // 10 + esc on left + shift on a left/right + }, + { + "~!@#$%^&*()_+", + "QWERTYUIOP{}|", + "\x10" "ASDFGHJKL:\"" "\x12", + "\x11" "ZXCVBNM<>? " "\x13" + } +}; + +struct osk_s +{ + qboolean enable; + int curlayout; + qboolean shift; + qboolean sending; + struct { + signed char x; + signed char y; + char val; + } curbutton; +} osk; + +static qboolean OSK_KeyEvent( int key, int down ) +{ + if( !osk.enable || !CVAR_TO_BOOL( osk_enable ) ) + return false; + + if( osk.sending ) + { + osk.sending = false; + return false; + } + + if( osk.curbutton.val == 0 ) + { + if( key == K_ENTER ) + { + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + return true; + } + return false; + } + + + switch ( key ) + { + case K_ENTER: + switch( osk.curbutton.val ) + { + case OSK_ENTER: + osk.sending = true; + Key_Event( K_ENTER, down ); + //osk_enable = false; // TODO: handle multiline + break; + case OSK_SHIFT: + if( !down ) + break; + + if( osk.curlayout & 1 ) + osk.curlayout--; + else + osk.curlayout++; + + osk.shift = osk.curbutton.val == OSK_SHIFT; + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + break; + case OSK_BACKSPACE: + Key_Event( K_BACKSPACE, down ); break; + case OSK_TAB: + Key_Event( K_TAB, down ); break; + default: + { + int ch; + + if( !down ) + { + if( osk.shift && osk.curlayout & 1 ) + osk.curlayout--; + + osk.shift = false; + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + break; + } + + if( !Q_stricmp( cl_charset->string, "utf-8" ) ) + ch = (unsigned char)osk.curbutton.val; + else + ch = Con_UtfProcessCharForce( (unsigned char)osk.curbutton.val ); + + if( !ch ) + break; + + Con_CharEvent( ch ); + if( cls.key_dest == key_menu ) + UI_CharEvent ( ch ); + + break; + } + } + break; + case K_UPARROW: + if( down && --osk.curbutton.y < 0 ) + { + osk.curbutton.y = MAX_OSK_LINES - 1; + osk.curbutton.val = 0; + return true; + } + break; + case K_DOWNARROW: + if( down && ++osk.curbutton.y >= MAX_OSK_LINES ) + { + osk.curbutton.y = 0; + osk.curbutton.val = 0; + return true; + } + break; + case K_LEFTARROW: + if( down && --osk.curbutton.x < 0 ) + osk.curbutton.x = MAX_OSK_ROWS - 1; + break; + case K_RIGHTARROW: + if( down && ++osk.curbutton.x >= MAX_OSK_ROWS ) + osk.curbutton.x = 0; + break; + default: + return false; + } + + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + return true; + +} + +/* +============= +Joy_EnableTextInput + +Enables built-in IME +============= +*/ +static void OSK_EnableTextInput( qboolean enable, qboolean force ) +{ + qboolean old = osk.enable; + + osk.enable = enable; + + if( osk.enable && (!old || force) ) + { + osk.curlayout = 0; + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + + } +} + +#define X_START 0.1347475f +#define Y_START 0.567f +#define X_STEP 0.05625 +#define Y_STEP 0.0825 + +/* +============ +Joy_DrawSymbolButton + +Draw button with symbol on it +============ +*/ +static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height ) +{ + char str[] = {symb & 255, 0}; + byte color[] = { 255, 255, 255, 255 }; + int x1 = x * refState.width, + y1 = y * refState.height, + w = width * refState.width, + h = height * refState.height; + + if( symb == osk.curbutton.val ) + { + ref.dllFuncs.FillRGBABlend( x1, y1, w, h, 255, 160, 0, 100 ); + } + + if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) ) + return; + + Con_DrawCharacter( x1 + 1, y1, symb, color ); +} + +/* +============= +Joy_DrawSpecialButton + +Draw special button, like shift, enter or esc +============= +*/ +static void OSK_DrawSpecialButton( const char *name, float x, float y, float width, float height ) +{ + byte color[] = { 0, 255, 0, 255 }; + + Con_DrawString( x * refState.width, y * refState.height, name, color ); +} + + +/* +============= +Joy_DrawOnScreenKeyboard + +Draw on screen keyboard, if enabled +============= +*/ +void OSK_Draw( void ) +{ + const char **curlayout = osk_keylayout[osk.curlayout]; // shortcut :) + float x, y; + int i, j; + + if( !osk.enable || !CVAR_TO_BOOL(osk_enable) || !osk.curbutton.val ) + return; + + // draw keyboard + ref.dllFuncs.FillRGBABlend( X_START * refState.width, Y_START * refState.height, + X_STEP * MAX_OSK_ROWS * refState.width, + Y_STEP * MAX_OSK_LINES * refState.height, 100, 100, 100, 100 ); + + OSK_DrawSpecialButton( "-]", X_START, Y_START + Y_STEP * 2, X_STEP, Y_STEP ); + OSK_DrawSpecialButton( "<-", X_START + X_STEP * 12, Y_START + Y_STEP * 2, X_STEP, Y_STEP ); + + OSK_DrawSpecialButton( "sh", X_START, Y_START + Y_STEP * 3, X_STEP, Y_STEP ); + OSK_DrawSpecialButton( "en", X_START + X_STEP * 12, Y_START + Y_STEP * 3, X_STEP, Y_STEP ); + + for( y = Y_START, j = 0; j < MAX_OSK_LINES; j++, y += Y_STEP ) + for( x = X_START, i = 0; i < MAX_OSK_ROWS; i++, x += X_STEP ) + OSK_DrawSymbolButton( curlayout[j][i], x, y, X_STEP, Y_STEP ); +} diff --git a/engine/common/common.h b/engine/common/common.h index 37eae9b5..fa74966a 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -816,24 +816,6 @@ void HPAK_CheckIntegrity( const char *filename ); void HPAK_CheckSize( const char *filename ); void HPAK_FlushHostQueue( void ); -// -// keys.c -// -int Key_IsDown( int keynum ); -const char *Key_IsBind( int keynum ); -void Key_Event( int key, int down ); -void Key_Init( void ); -void Key_WriteBindings( file_t *f ); -const char *Key_GetBinding( int keynum ); -void Key_SetBinding( int keynum, const char *binding ); -void Key_ClearStates( void ); -const char *Key_KeynumToString( int keynum ); -int Key_StringToKeynum( const char *str ); -int Key_GetKey( const char *binding ); -void Key_EnumCmds_f( void ); -void Key_SetKeyDest( int key_dest ); -void Key_EnableTextInput( qboolean enable, qboolean force ); - #include "avi/avi.h" //