diff --git a/engine/client/client.h b/engine/client/client.h index d49de2dd..6fb274f0 100644 --- a/engine/client/client.h +++ b/engine/client/client.h @@ -1241,7 +1241,6 @@ void Key_EnumCmds_f( void ); void Key_SetKeyDest( int key_dest ); void Key_EnableTextInput( qboolean enable, qboolean force ); int Key_ToUpper( int key ); -void OSK_Draw( void ); qboolean Cmd_GetKeysList( const char *s, char *completedname, int length, qboolean print_suggestions ); // diff --git a/engine/client/in_keys.c b/engine/client/in_keys.c index 755ad3ca..835a5e91 100644 --- a/engine/client/in_keys.c +++ b/engine/client/in_keys.c @@ -158,9 +158,6 @@ static const keyname_t keynames[] = { "INTERNATIONAL9", K_INTERNATIONAL + 8, "" }, }; -static void OSK_EnableTextInput( qboolean enable, qboolean force ); -static qboolean OSK_KeyEvent( int key, int down ); -static CVAR_DEFINE_AUTO( osk_enable, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "enable built-in on-screen keyboard" ); static CVAR_DEFINE_AUTO( key_rotate, "0", FCVAR_ARCHIVE|FCVAR_FILTERABLE, "rotate arrow keys (0-3)" ); /* @@ -586,7 +583,6 @@ void Key_Init( void ) for( i = 0; i < ARRAYSIZE( keynames ); i++ ) Key_SetBinding( keynames[i].keynum, keynames[i].binding ); - Cvar_RegisterVariable( &osk_enable ); Cvar_RegisterVariable( &key_rotate ); } @@ -992,288 +988,3 @@ int Key_ToUpper( int keynum ) return Q_toupper( keynum ); } - -/* 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 || !osk_enable.value ) - return false; - - if( osk.sending ) - { - osk.sending = false; - return false; - } - - if( osk.curbutton.val == 0 ) - { - if( key == K_ENTER || key == K_A_BUTTON ) - { - osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; - return true; - } - return false; - } - - - switch ( key ) - { - case K_A_BUTTON: - 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 = true; - 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; - } - - ch = (byte)osk.curbutton.val; - - // do not pass UTF-8 sequence into the engine, convert it here - if( !cls.accept_utf8 ) - ch = Con_UtfProcessCharForce( ch ); - - if( !ch ) - break; - - CL_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; - -} - -/* -============= -OSK_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 ) -{ - cl_font_t *font = Con_GetCurFont(); - 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.FillRGBA( kRenderTransTexture, x1, y1, w, h, 255, 160, 0, 100 ); - - if( !symb || symb == ' ' || (symb >= OSK_TAB && symb < OSK_SPECKEY_LAST ) ) - return; - - CL_DrawCharacter( - x1 + width * 0.4 * refState.width, - y1 + height * 0.4 * refState.height, - symb, color, font, 0 ); -} - -/* -============= -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 + width * 0.4 * refState.width, - y * refState.height + height * 0.4 * 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 || !osk_enable.value || !osk.curbutton.val ) - return; - - // draw keyboard - ref.dllFuncs.FillRGBA( kRenderTransTexture, 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/client/in_osk.c b/engine/client/in_osk.c new file mode 100644 index 00000000..5cb95207 --- /dev/null +++ b/engine/client/in_osk.c @@ -0,0 +1,312 @@ +/* +in_osk.c - on-screen keyboard input +Copyright (C) 2016-2026 Alibek Omarov + +This program 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 3 of the License, or +(at your option) any later version. + +This program 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. +*/ + +#include "common.h" +#include "input.h" +#include "client.h" + +CVAR_DEFINE_AUTO( osk_enable, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable built-in on-screen keyboard" ); + +/* 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" + } +}; + +static struct osk_s +{ + qboolean enable; + int curlayout; + qboolean shift; + qboolean sending; + struct + { + signed char x; + signed char y; + char val; + } curbutton; +} osk; + +qboolean OSK_KeyEvent( int key, int down ) +{ + if( !osk.enable || !osk_enable.value ) + return false; + + if( osk.sending ) + { + osk.sending = false; + return false; + } + + if( osk.curbutton.val == 0 ) + { + if( key == K_ENTER || key == K_A_BUTTON ) + { + osk.curbutton.val = osk_keylayout[osk.curlayout][osk.curbutton.y][osk.curbutton.x]; + return true; + } + return false; + } + + + switch( key ) + { + case K_A_BUTTON: + 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 = true; + 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; + } + + ch = (byte)osk.curbutton.val; + + // do not pass UTF-8 sequence into the engine, convert it here + if( !cls.accept_utf8 ) + ch = Con_UtfProcessCharForce( ch ); + + if( !ch ) + break; + + CL_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; + +} + +/* +============= +OSK_EnableTextInput + +Enables built-in IME +============= +*/ +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 + +/* +============ +OSK_DrawSymbolButton + +Draw button with symbol on it +============ +*/ +static void OSK_DrawSymbolButton( int symb, float x, float y, float width, float height ) +{ + cl_font_t *font = Con_GetCurFont(); + 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.FillRGBA( kRenderTransTexture, x1, y1, w, h, 255, 160, 0, 100 ); + + if( !symb || symb == ' ' || ( symb >= OSK_TAB && symb < OSK_SPECKEY_LAST )) + return; + + CL_DrawCharacter( + x1 + width * 0.4 * refState.width, + y1 + height * 0.4 * refState.height, + symb, color, font, 0 ); +} + +/* +============= +OSK_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 + width * 0.4 * refState.width, + y * refState.height + height * 0.4 * refState.height, + name, + color ); +} + +/* +============= +OSK_Draw + +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 || !osk_enable.value || !osk.curbutton.val ) + return; + + // draw keyboard + ref.dllFuncs.FillRGBA( kRenderTransTexture, 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 ); +} + +void OSK_Init( void ) +{ + Cvar_RegisterVariable( &osk_enable ); +} diff --git a/engine/client/input.c b/engine/client/input.c index 96dcad46..f0303e66 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -447,6 +447,8 @@ void IN_Init( void ) IN_GyroInit(); + OSK_Init(); + Joy_Init(); // common joystick support init Touch_Init(); diff --git a/engine/client/input.h b/engine/client/input.h index 2801198a..98e25726 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -99,6 +99,15 @@ qboolean Touch_WantVisibleCursor( void ); void Touch_NotifyResize( void ); #endif +// +// in_osk.c +// +extern convar_t osk_enable; +void OSK_Init( void ); +qboolean OSK_KeyEvent( int key, int down ); +void OSK_EnableTextInput( qboolean enable, qboolean force ); +void OSK_Draw( void ); + // // in_joy.c //