diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index 45726fd1..48b47f08 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -652,7 +652,9 @@ void CL_CreateCmd( void ) } active = (( cls.signon == SIGNONS ) && !cl.paused && !cls.demoplayback ); + Platform_PreCreateMove(); clgame.dllFuncs.CL_CreateMove( host.frametime, &pcmd->cmd, active ); + IN_EngineAppendMove( host.frametime, &pcmd->cmd, active ); CL_PopPMStates(); @@ -3103,6 +3105,7 @@ void CL_Shutdown( void ) { Host_WriteOpenGLConfig (); Host_WriteVideoConfig (); + Touch_WriteConfig(); } // IN_TouchShutdown (); diff --git a/engine/client/cl_mobile.c b/engine/client/cl_mobile.c index 6e43cb59..00dc1145 100644 --- a/engine/client/cl_mobile.c +++ b/engine/client/cl_mobile.c @@ -95,22 +95,18 @@ static void *pfnGetNativeObject( const char *obj ) return Platform_GetNativeObject( obj ); } -void IN_TouchHideButtons( const char *str, unsigned char hide ) -{ - -} static mobile_engfuncs_t gpMobileEngfuncs = { MOBILITY_API_VERSION, pfnVibrate, pfnEnableTextInput, - NULL, // IN_TouchAddClientButton, - NULL, // IN_TouchAddDefaultButton, - IN_TouchHideButtons, - NULL, // IN_TouchRemoveButton, - NULL, // (void*)IN_TouchSetClientOnly, - NULL, // IN_TouchResetDefaultButtons, + Touch_AddClientButton, + Touch_AddDefaultButton, + Touch_HideButtons, + Touch_RemoveButton, + (void*)Touch_SetClientOnly, + Touch_ResetDefaultButtons, pfnDrawScaledCharacter, Sys_Warn, pfnGetNativeObject, diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index a94ffc96..20d61941 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -19,6 +19,7 @@ GNU General Public License for more details. #include "entity_types.h" #include "vgui_draw.h" #include "sound.h" +#include "input.h" // touch #include "platform/platform.h" // GL_UpdateSwapInterval /* @@ -484,6 +485,7 @@ void V_PostRender( void ) UI_UpdateMenu( host.realtime ); Con_DrawVersion(); Con_DrawDebug(); // must be last + Touch_Draw(); S_ExtraUpdate(); } diff --git a/engine/client/in_touch.c b/engine/client/in_touch.c new file mode 100644 index 00000000..e49c68ef --- /dev/null +++ b/engine/client/in_touch.c @@ -0,0 +1,1911 @@ +/* +touch.c - touchscreen support prototype +Copyright (C) 2015-2018 mittorn + +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. +*/ + +#ifndef XASH_DEDICATED +#include "common.h" +#include "input.h" +#include "client.h" +#include "math.h" +#include "vgui_draw.h" +#include "mobility_int.h" + +typedef enum +{ + touch_command, // just tap a button + touch_move, // like a joystick stick + touch_joy, // like a joystick stick, centered + touch_dpad, // only two directions + touch_look, // like a touchpad +} touchButtonType; + +typedef enum +{ + state_none = 0, + state_edit, + state_edit_move +} touchState; + +typedef enum +{ + round_none = 0, + round_grid, + round_aspect +} touchRound; + + + +typedef struct touch_button_s +{ + // Touch button type: tap, stick or slider + touchButtonType type; + + // Field of button in pixels + float x1, y1, x2, y2; + + // Button texture + int texture; + rgba_t color; + char texturefile[256]; + char command[256]; + char name[32]; + int finger; + int flags; + float fade; + float fadespeed; + float fadeend; + float aspect; + + // Double-linked list + struct touch_button_s *next; + struct touch_button_s *prev; + +} touch_button_t; + +typedef struct touchdefaultbutton_s +{ + char name[32]; + char texturefile[256]; + char command[256]; + float x1, y1, x2, y2; + rgba_t color; + touchRound round; + float aspect; + int flags; +} touchdefaultbutton_t; + +typedef struct touchbuttonlist_s +{ + touch_button_t *first; + touch_button_t *last; +} touchbuttonlist_t; + +struct touch_s +{ + qboolean initialized; + qboolean config_loaded; + touchbuttonlist_t list_user, list_edit; + byte *mempool; + touchState state; + int look_finger; + int move_finger; + touch_button_t *move; + float move_start_x; + float move_start_y; + float forward; + float side; + float yaw; + float pitch; + + // editing + touch_button_t *edit; + touch_button_t *selection; + touch_button_t *hidebutton; + int resize_finger; + qboolean showeditbuttons; + + // other features + qboolean clientonly; + rgba_t scolor; + int swidth; + qboolean precision; + + // textures + int whitetexture; + int joytexture; // touch indicator + qboolean configchanged; +} touch; + +touchdefaultbutton_t g_DefaultButtons[256]; +int g_LastDefaultButton; + +convar_t *touch_pitch; +convar_t *touch_yaw; +convar_t *touch_forwardzone; +convar_t *touch_sidezone; +convar_t *touch_nonlinear_look; +convar_t *touch_pow_mult; +convar_t *touch_pow_factor; +convar_t *touch_exp_mult; +convar_t *touch_grid_enable; +convar_t *touch_grid_count; +convar_t *touch_config_file; +convar_t *touch_enable; +convar_t *touch_in_menu; +convar_t *touch_joy_radius; +convar_t *touch_dpad_radius; +convar_t *touch_move_indicator; +convar_t *touch_highlight_r; +convar_t *touch_highlight_g; +convar_t *touch_highlight_b; +convar_t *touch_highlight_a; +convar_t *touch_precise_amount; +convar_t *touch_joy_texture; +convar_t *touch_emulate; + +// code looks smaller with it +#define B(x) button->x +#define SCR_W ((float)refState.width) +#define SCR_H ((float)refState.height) +#define TO_SCRN_Y(x) (refState.height * (x)) +#define TO_SCRN_X(x) (refState.width * (x)) + +int pfnDrawCharacter( int x, int y, int number, int r, int g, int b ); +static void IN_TouchCheckCoords( float *x1, float *y1, float *x2, float *y2 ); +static void IN_TouchEditClear( void ); +static void Touch_InitConfig( void ); + +void Touch_WriteConfig( void ) +{ + file_t *f; + char newconfigfile[64]; + char oldconfigfile[64]; + + if( !touch.list_user.first ) return; + + if( Sys_CheckParm( "-nowriteconfig" ) || !touch.configchanged || !touch.config_loaded ) + return; + + Con_DPrintf( "Touch_WriteConfig(): %s\n", touch_config_file->string ); + + Q_snprintf( newconfigfile, 64, "%s.new", touch_config_file->string ); + Q_snprintf( oldconfigfile, 64, "%s.bak", touch_config_file->string ); + + f = FS_Open( newconfigfile, "w", true ); + if( f ) + { + touch_button_t *button; + FS_Printf( f, "//=======================================================================\n"); + FS_Printf( f, "//\tCopyright SDLash3D team & XashXT group %s ©\n", Q_timestamp( TIME_YEAR_ONLY )); + FS_Printf( f, "//\t\t\ttouchscreen config\n" ); + FS_Printf( f, "//=======================================================================\n" ); + FS_Printf( f, "\ntouch_config_file \"%s\"\n", touch_config_file->string ); + FS_Printf( f, "\n// touch cvars\n" ); + FS_Printf( f, "\n// sensitivity settings\n" ); + FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value ); + FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value ); + FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value ); + FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value ); + FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look)); + FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value ); + FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value ); + FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value ); FS_Printf( f, "\n// grid settings\n" ); + FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count->value ); + FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable)); + FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" ); + FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] ); + FS_Printf( f, "\n// highlight when pressed\n" ); + FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value ); + FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value ); + FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value ); + FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value ); + FS_Printf( f, "\n// _joy and _dpad options\n" ); + FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value ); + FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value ); + FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" ); + FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value ); + FS_Printf( f, "\n// enable/disable move indicator\n" ); + FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value ); + + FS_Printf( f, "\n// reset menu state when execing config\n" ); + FS_Printf( f, "touch_setclientonly 0\n" ); + FS_Printf( f, "\n// touch buttons\n" ); + FS_Printf( f, "touch_removeall\n" ); + + for( button = touch.list_user.first; button; button = button->next ) + { + string newCommand; + int flags = button->flags; + + if( flags & TOUCH_FL_CLIENT ) + continue; //skip temporary buttons + + if( flags & TOUCH_FL_DEF_SHOW ) + flags &= ~TOUCH_FL_HIDE; + + if( flags & TOUCH_FL_DEF_HIDE ) + flags |= TOUCH_FL_HIDE; + + /// TODO: EscapeCommand + //Com_EscapeCommand( newCommand, B(command), MAX_STRING ); + Q_strncpy( newCommand, B(command), MAX_STRING ); + + + FS_Printf( f, "touch_addbutton \"%s\" \"%s\" \"%s\" %f %f %f %f %d %d %d %d %d\n", + B(name), B(texturefile), newCommand, + B(x1), B(y1), B(x2), B(y2), + B(color[0]), B(color[1]), B(color[2]), B(color[3]), flags ); + } + + FS_Close( f ); + + FS_Delete( oldconfigfile ); + FS_Rename( touch_config_file->string, oldconfigfile ); + FS_Delete( touch_config_file->string ); + FS_Rename( newconfigfile, touch_config_file->string ); + } + else Con_Printf( S_ERROR "Couldn't write %s.\n", touch_config_file->string ); +} + +void Touch_ExportConfig_f( void ) +{ + file_t *f; + const char *name; + + if( Cmd_Argc() != 2 ) + { + Con_Printf( "Usage: touch_exportconfig \n" ); + return; + } + + if( !touch.list_user.first ) return; + + name = Cmd_Argv( 1 ); + + Con_Reportf( "Exporting config to %s\n", name ); + f = FS_Open( name, "w", true ); + if( f ) + { + char profilename[256]; + char profilebase[256]; + touch_button_t *button; + if( Q_strstr( name, "touch_presets/" ) ) + { + COM_FileBase( name, profilebase ); + Q_snprintf( profilename, 256, "touch_profiles/%s (copy).cfg", profilebase ); + } + else Q_strncpy( profilename, name, 256 ); + FS_Printf( f, "//=======================================================================\n"); + FS_Printf( f, "//\tCopyright SDLash3D team & XashXT group %s ©\n", Q_timestamp( TIME_YEAR_ONLY )); + FS_Printf( f, "//\t\t\ttouchscreen preset\n" ); + FS_Printf( f, "//=======================================================================\n" ); + FS_Printf( f, "\ntouch_config_file \"%s\"\n", profilename ); + FS_Printf( f, "\n// touch cvars\n" ); + FS_Printf( f, "\n// sensitivity settings\n" ); + FS_Printf( f, "touch_pitch \"%f\"\n", touch_pitch->value ); + FS_Printf( f, "touch_yaw \"%f\"\n", touch_yaw->value ); + FS_Printf( f, "touch_forwardzone \"%f\"\n", touch_forwardzone->value ); + FS_Printf( f, "touch_sidezone \"%f\"\n", touch_sidezone->value ); + FS_Printf( f, "touch_nonlinear_look \"%d\"\n", CVAR_TO_BOOL(touch_nonlinear_look) ); + FS_Printf( f, "touch_pow_factor \"%f\"\n", touch_pow_factor->value ); + FS_Printf( f, "touch_pow_mult \"%f\"\n", touch_pow_mult->value ); + FS_Printf( f, "touch_exp_mult \"%f\"\n", touch_exp_mult->value ); + FS_Printf( f, "\n// grid settings\n" ); + FS_Printf( f, "touch_grid_count \"%d\"\n", (int)touch_grid_count ); + FS_Printf( f, "touch_grid_enable \"%d\"\n", CVAR_TO_BOOL(touch_grid_enable) ); + FS_Printf( f, "\n// global overstroke (width, r, g, b, a)\n" ); + FS_Printf( f, "touch_set_stroke %d %d %d %d %d\n", touch.swidth, touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] ); + FS_Printf( f, "\n// highlight when pressed\n" ); + FS_Printf( f, "touch_highlight_r \"%f\"\n", touch_highlight_r->value ); + FS_Printf( f, "touch_highlight_g \"%f\"\n", touch_highlight_g->value ); + FS_Printf( f, "touch_highlight_b \"%f\"\n", touch_highlight_b->value ); + FS_Printf( f, "touch_highlight_a \"%f\"\n", touch_highlight_a->value ); + FS_Printf( f, "\n// _joy and _dpad options\n" ); + FS_Printf( f, "touch_dpad_radius \"%f\"\n", touch_dpad_radius->value ); + FS_Printf( f, "touch_joy_radius \"%f\"\n", touch_joy_radius->value ); + FS_Printf( f, "\n// how much slowdown when Precise Look button pressed\n" ); + FS_Printf( f, "touch_precise_amount \"%f\"\n", touch_precise_amount->value ); + FS_Printf( f, "\n// enable/disable move indicator\n" ); + FS_Printf( f, "touch_move_indicator \"%f\"\n", touch_move_indicator->value ); + + FS_Printf( f, "\n// reset menu state when execing config\n" ); + FS_Printf( f, "touch_setclientonly 0\n" ); + FS_Printf( f, "\n// touch buttons\n" ); + FS_Printf( f, "touch_removeall\n" ); + for( button = touch.list_user.first; button; button = button->next ) + { + string newCommand; + float aspect; + int flags = button->flags; + if( flags & TOUCH_FL_CLIENT ) + continue; //skip temporary buttons + if( flags & TOUCH_FL_DEF_SHOW ) + flags &= ~TOUCH_FL_HIDE; + if( flags & TOUCH_FL_DEF_HIDE ) + flags |= TOUCH_FL_HIDE; + + aspect = ( B(y2) - B(y1) ) / ( ( B(x2) - B(x1) ) /(SCR_H/SCR_W) ); + + /// TODO: EscapeCommand + //Com_EscapeCommand( newCommand, B(command), MAX_STRING ); + Q_strncpy( newCommand, B(command), MAX_STRING ); + + FS_Printf( f, "touch_addbutton \"%s\" \"%s\" \"%s\" %f %f %f %f %d %d %d %d %d %f\n", + B(name), B(texturefile), newCommand, + B(x1), B(y1), B(x2), B(y2), + B(color[0]), B(color[1]), B(color[2]), B(color[3]), flags, aspect ); + } + FS_Printf( f, "\n// round button coordinates to grid\n" ); + FS_Printf( f, "touch_roundall\n" ); + FS_Close( f ); + } + else Con_Printf( S_ERROR "Couldn't write %s.\n", name ); +} + +void Touch_GenetateCode_f( void ) +{ + touch_button_t *button; + rgba_t c = {0,0,0,0}; + + if( Cmd_Argc() != 1 ) + { + Con_Printf( "Usage: touch_generate_code\n" ); + return; + } + + if( !touch.list_user.first ) return; + + for( button = touch.list_user.first; button; button = button->next ) + { + float aspect; + int flags = button->flags; + if( flags & TOUCH_FL_CLIENT ) + continue; //skip temporary buttons + if( flags & TOUCH_FL_DEF_SHOW ) + flags &= ~TOUCH_FL_HIDE; + if( flags & TOUCH_FL_DEF_HIDE ) + flags |= TOUCH_FL_HIDE; + + aspect = ( B(y2) - B(y1) ) / ( ( B(x2) - B(x1) ) /(SCR_H/SCR_W) ); + if( memcmp( &c, &B(color), sizeof( rgba_t ) ) ) + { + Con_Printf( "unsigned char color[] = { %d, %d, %d, %d };\n", B(color[0]), B(color[1]), B(color[2]), B(color[3]) ); + memcpy( &c, &B(color), sizeof( rgba_t ) ); + } + Con_Printf( "TOUCH_ADDDEFAULT( \"%s\", \"%s\", \"%s\", %f, %f, %f, %f, color, %d, %f, %d );\n", + B(name), B(texturefile), B(command), + B(x1), B(y1), B(x2), B(y2), (B(type) == touch_command)?(fabs( aspect - 1.0f) < 0.0001)?2:1:0, aspect, flags ); + } +} + +void Touch_RoundAll_f( void ) +{ + touch_button_t *button; + if( !touch_grid_enable->value ) + return; + for( button = touch.list_user.first; button; button = button->next ) + IN_TouchCheckCoords( &B(x1), &B(y1), &B(x2), &B(y2) ); +} + +void Touch_ListButtons_f( void ) +{ + touch_button_t *button; + Touch_InitConfig(); + + for( button = touch.list_user.first; button; button = button->next ) + { + Con_Printf( "%s %s %s %f %f %f %f %d %d %d %d %d\n", + B(name), B(texturefile), B(command), + B(x1), B(y1), B(x2), B(y2), + B(color[0]), B(color[1]), B(color[2]), B(color[3]), B(flags) ); + if( B(flags) & TOUCH_FL_CLIENT) + continue; + UI_AddTouchButtonToList( B(name), B(texturefile), B(command),B(color), B(flags) ); + } + touch.configchanged = true; +} + +void Touch_Stroke_f( void ) +{ + touch.swidth = Q_atoi( Cmd_Argv( 1 ) ); + MakeRGBA( touch.scolor, Q_atoi( Cmd_Argv( 2 ) ), Q_atoi( Cmd_Argv( 3 ) ), Q_atoi( Cmd_Argv( 4 ) ), Q_atoi( Cmd_Argv( 5 ) ) ); +} + +touch_button_t *Touch_FindButton( touchbuttonlist_t *list, const char *name ) +{ + touch_button_t *button; + + for ( button = list->first; button; button = button->next ) + if( !Q_strncmp( button->name, name, 32 ) ) + return button; + return NULL; +} + +touch_button_t *Touch_FindFirst( touchbuttonlist_t *list, const char *name ) +{ + touch_button_t *button; + + for ( button = list->first; button; button = button->next ) + if( ( Q_strstr( name, "*" ) && Q_stricmpext( name, button->name ) ) || !Q_strncmp( name, button->name, 32 ) ) + return button; + return NULL; +} + +void Touch_SetClientOnly( qboolean state ) +{ + touch.clientonly = state; + host.mouse_visible = state; + + touch.move_finger = touch.look_finger = -1; + touch.forward = touch.side = 0; + + /// TODO: touch sdl platform +#if 0 + if( state ) + { + SDL_SetRelativeMouseMode( SDL_FALSE ); + SDL_ShowCursor( true ); + IN_DeactivateMouse(); + } + else + { + SDL_ShowCursor( false ); + SDL_GetRelativeMouseState( 0, 0 ); + } +#endif +} + +void Touch_SetClientOnly_f( void ) +{ + Touch_SetClientOnly( Q_atoi( Cmd_Argv( 1 ) ) ); +} + +void Touch_RemoveButtonFromList( touchbuttonlist_t *list, const char *name ) +{ + touch_button_t *button; + + IN_TouchEditClear(); + + while( ( button = Touch_FindFirst( &touch.list_user, name ) ) ) + { + if( button->prev ) + button->prev->next = button->next; + else + list->first = button->next; + if( button->next ) + button->next->prev = button->prev; + else + list->last = button->prev; + Mem_Free( button ); + } + +} + +void Touch_RemoveButton( const char *name ) +{ + Touch_RemoveButtonFromList( &touch.list_user, name ); +} + +void IN_TouchRemoveButton_f( void ) +{ + Touch_RemoveButton( Cmd_Argv( 1 ) ); +} + +void Touch_ClearList( touchbuttonlist_t *list ) +{ + while( list->first ) + { + touch_button_t *remove = list->first; + list->first = list->first->next; + Mem_Free ( remove ); + } + list->first = list->last = NULL; +} + +void Touch_RemoveAll_f( void ) +{ + IN_TouchEditClear(); + Touch_ClearList( &touch.list_user ); +} + +void Touch_SetColor( touchbuttonlist_t *list, const char *name, byte *color ) +{ + touch_button_t *button; + for( button = list->first; button; button = button->next ) + { + if( ( Q_strstr( name, "*" ) && Q_stricmpext( name, button->name ) ) || !Q_strncmp( name, button->name, 32 ) ) + MakeRGBA( button->color, color[0], color[1], color[2], color[3] ); + } +} + +void Touch_SetTexture( touchbuttonlist_t *list, const char *name, const char *texture ) +{ + touch_button_t *button = Touch_FindButton( list, name ); + if( !button ) + return; + button->texture = -1; // mark for texture load + Q_strncpy( button->texturefile, texture, sizeof( button->texturefile ) ); +} + +void Touch_SetCommand( touchbuttonlist_t *list, const char *name, const char *command ) +{ + touch_button_t *button = Touch_FindButton( list, name ); + + if( !button ) + return; + + if( !Q_strcmp( command, "_look" ) ) + button->type = touch_look; + if( !Q_strcmp( command, "_move" ) ) + button->type = touch_move; + if( !Q_strcmp( command, "_joy" ) ) + button->type = touch_joy; + if( !Q_strcmp( command, "_dpad" ) ) + button->type = touch_dpad; + + Q_strncpy( button->command, command, sizeof( button->command ) ); +} + +void Touch_HideButtons( const char *name, byte hide ) +{ + touch_button_t *button; + + for( button = touch.list_user.first; button; button = button->next) + { + if( ( Q_strstr( name, "*" ) && Q_stricmpext( name, button->name ) ) || !Q_strncmp( name, button->name, 32 ) ) + { + if( hide ) + button->flags |= TOUCH_FL_HIDE; + else + button->flags &= ~TOUCH_FL_HIDE; + } + } + +} + +void Touch_ToggleSelection_f( void ) +{ + if( touch.selection ) + touch.selection->flags ^= TOUCH_FL_HIDE; +} + +void Touch_Hide_f( void ) +{ + Touch_HideButtons( Cmd_Argv( 1 ), true ); +} + +void Touch_Show_f( void ) +{ + Touch_HideButtons( Cmd_Argv( 1 ), false ); +} + +void Touch_FadeButtons( touchbuttonlist_t *list, const char *name, float speed, float end, float start ) +{ + touch_button_t *button; + for( button = list->first; button; button = button->next) + { + if( ( Q_strstr( name, "*" ) && Q_stricmpext( name, button->name ) ) || !Q_strncmp( name, button->name, 32 ) ) + { + if( start >= 0 ) + button->fade = start; + button->fadespeed = speed; + button->fadeend = end; + } + } +} +void Touch_Fade_f( void ) +{ + float start = -1; + if( Cmd_Argc() < 4 ) + return; + if( Cmd_Argc() > 4 ) + start = Q_atof( Cmd_Argv( 4 ) ); + Touch_FadeButtons( &touch.list_user, Cmd_Argv( 1 ), Q_atof( Cmd_Argv( 2 )), Q_atof( Cmd_Argv( 3 )), start ); +} + +void Touch_SetColor_f( void ) +{ + rgba_t color; + if( Cmd_Argc() == 6 ) + { + MakeRGBA( color, Q_atoi( Cmd_Argv(2) ), Q_atoi( Cmd_Argv(3) ), Q_atoi( Cmd_Argv(4) ), Q_atoi( Cmd_Argv(5) ) ); + Touch_SetColor( &touch.list_user, Cmd_Argv(1), color ); + return; + } + Con_Printf( "Usage: touch_setcolor \n" ); +} + +void Touch_SetTexture_f( void ) +{ + if( Cmd_Argc() == 3 ) + { + Touch_SetTexture( &touch.list_user, Cmd_Argv( 1 ), Cmd_Argv( 2 ) ); + return; + } + Con_Printf( "Usage: touch_settexture \n" ); +} + +void Touch_SetFlags_f( void ) +{ + if( Cmd_Argc() == 3 ) + { + touch_button_t *button = Touch_FindButton( &touch.list_user, Cmd_Argv( 1 ) ); + if( button ) + button->flags = Q_atoi( Cmd_Argv( 2 ) ); + return; + } + Con_Printf( "Usage: touch_setflags \n" ); +} + +void Touch_SetCommand_f( void ) +{ + if( Cmd_Argc() == 3 ) + { + Touch_SetCommand( &touch.list_user, Cmd_Argv( 1 ), Cmd_Argv( 2 ) ); + return; + } + Con_Printf( "Usage: touch_command \n" ); +} +void Touch_ReloadConfig_f( void ) +{ + touch.state = state_none; + if( touch.edit ) + touch.edit->finger = -1; + if( touch.selection ) + touch.selection->finger = -1; + touch.edit = touch.selection = NULL; + touch.resize_finger = touch.move_finger = touch.look_finger = -1; + + Cbuf_AddText( va("exec %s\n", touch_config_file->string ) ); +} + +touch_button_t *Touch_AddButton( touchbuttonlist_t *list, const char *name, const char *texture, const char *command, float x1, float y1, float x2, float y2, byte *color ) +{ + touch_button_t *button = Mem_Calloc( touch.mempool, sizeof( touch_button_t ) ); + button->texture = -1; + Q_strncpy( button->texturefile, texture, sizeof( button->texturefile ) ); + Q_strncpy( button->name, name, 32 ); + Touch_RemoveButtonFromList( list, name ); //replace if exist + button->x1 = x1; + button->y1 = y1; + button->x2 = x2; + button->y2 = y2; + MakeRGBA( button->color, color[0], color[1], color[2], color[3] ); + button->command[0] = 0; + button->flags = 0; + button->fade = 1; + + // check keywords + if( !Q_strcmp( command, "_look" ) ) + button->type = touch_look; + if( !Q_strcmp( command, "_move" ) ) + button->type = touch_move; + if( !Q_strcmp( command, "_joy" ) ) + button->type = touch_joy; + if( !Q_strcmp( command, "_dpad" ) ) + button->type = touch_dpad; + + Q_strncpy( button->command, command, sizeof( button->command ) ); + button->finger = -1; + button->next = NULL; + button->prev = list->last; + if( list->last ) + list->last->next = button; + list->last = button; + + if( !list->first ) + list->first = button; + + return button; +} + +void Touch_AddClientButton( const char *name, const char *texture, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ) +{ + touch_button_t *button; + + if( !touch.initialized ) + return; + if( round ) + IN_TouchCheckCoords( &x1, &y1, &x2, &y2 ); + if( round == round_aspect ) + { + y2 = y1 + ( x2 - x1 ) * (SCR_W/SCR_H) * aspect; + } + button = Touch_AddButton( &touch.list_user, name, texture, command, x1, y1, x2, y2, color ); + button->flags |= flags | TOUCH_FL_CLIENT | TOUCH_FL_NOEDIT; + button->aspect = aspect; +} + +void Touch_LoadDefaults_f( void ) +{ + int i; + for( i = 0; i < g_LastDefaultButton; i++ ) + { + touch_button_t *button; + float x1 = g_DefaultButtons[i].x1, + y1 = g_DefaultButtons[i].y1, + x2 = g_DefaultButtons[i].x2, + y2 = g_DefaultButtons[i].y2; + + IN_TouchCheckCoords( &x1, &y1, &x2, &y2 ); + + if( g_DefaultButtons[i].aspect && g_DefaultButtons[i].round == round_aspect ) + { + if( g_DefaultButtons[i].texturefile[0] == '#' ) + y2 = y1 + ( (float)clgame.scrInfo.iCharHeight / (float)clgame.scrInfo.iHeight ) * g_DefaultButtons[i].aspect + touch.swidth*2/SCR_H; + else + y2 = y1 + ( x2 - x1 ) * (SCR_W/SCR_H) * g_DefaultButtons[i].aspect; + } + + IN_TouchCheckCoords( &x1, &y1, &x2, &y2 ); + button = Touch_AddButton( &touch.list_user, g_DefaultButtons[i].name, g_DefaultButtons[i].texturefile, g_DefaultButtons[i].command, x1, y1, x2, y2, g_DefaultButtons[i].color ); + button->flags |= g_DefaultButtons[i].flags; + button->aspect = g_DefaultButtons[i].aspect; + } +} + + +// Add default button from client +void Touch_AddDefaultButton( const char *name, const char *texturefile, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ) +{ + if( g_LastDefaultButton >= 255 ) + return; + Q_strncpy( g_DefaultButtons[g_LastDefaultButton].name, name, 32 ); + Q_strncpy( g_DefaultButtons[g_LastDefaultButton].texturefile, texturefile, 256 ); + Q_strncpy( g_DefaultButtons[g_LastDefaultButton].command, command, 256 ); + g_DefaultButtons[g_LastDefaultButton].x1 = x1; + g_DefaultButtons[g_LastDefaultButton].y1 = y1; + g_DefaultButtons[g_LastDefaultButton].x2 = x2; + g_DefaultButtons[g_LastDefaultButton].y2 = y2; + MakeRGBA( g_DefaultButtons[g_LastDefaultButton].color, color[0], color[1], color[2], color[3] ); + g_DefaultButtons[g_LastDefaultButton].round = round; + g_DefaultButtons[g_LastDefaultButton].aspect = aspect; + g_DefaultButtons[g_LastDefaultButton].flags = flags; + g_LastDefaultButton++; +} + +// Client may remove all default buttons from engine +void Touch_ResetDefaultButtons( void ) +{ + g_LastDefaultButton = 0; +} +void Touch_AddButton_f( void ) +{ + rgba_t color; + int argc = Cmd_Argc( ); + + if( argc >= 12 ) + { + touch_button_t *button; + MakeRGBA( color, Q_atoi( Cmd_Argv(8) ), Q_atoi( Cmd_Argv(9) ), + Q_atoi( Cmd_Argv(10) ), Q_atoi( Cmd_Argv(11) ) ); + button = Touch_AddButton( &touch.list_user, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), + Q_atof( Cmd_Argv(4) ), Q_atof( Cmd_Argv(5) ), + Q_atof( Cmd_Argv(6) ), Q_atof( Cmd_Argv(7) ) , + color ); + if( argc >= 13 ) + button->flags = Q_atoi( Cmd_Argv(12) ); + if( argc >= 14 ) + { + // Recalculate button coordinates aspect ratio + // This is feature for distributed configs + float aspect = Q_atof( Cmd_Argv(13) ); + if( aspect ) + { + if( B(texturefile)[0] != '#' ) + B(y2) = B(y1) + ( B(x2) - B(x1) ) * (SCR_W/SCR_H) * aspect; + B(aspect) = aspect; + } + } + + return; + } + if( argc == 8 ) + { + MakeRGBA( color, 255, 255, 255, 255 ); + Touch_AddButton( &touch.list_user, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), + Q_atof( Cmd_Argv(4) ), Q_atof( Cmd_Argv(5) ), + Q_atof( Cmd_Argv(6) ), Q_atof( Cmd_Argv(7) ), + color ); + return; + } + if( argc == 4 ) + { + MakeRGBA( color, 255, 255, 255, 255 ); + Touch_AddButton( &touch.list_user, Cmd_Argv(1), Cmd_Argv(2), Cmd_Argv(3), 0.4, 0.4, 0.6, 0.6, color ); + return; + } + Con_Printf( "Usage: touch_addbutton [ [ r g b a ] ]\n" ); +} + +void Touch_EnableEdit_f( void ) +{ + if( touch.state == state_none ) + touch.state = state_edit; + touch.resize_finger = touch.move_finger = touch.look_finger = -1; + touch.move = NULL; + touch.configchanged = true; +} + +void Touch_DisableEdit_f( void ) +{ + touch.state = state_none; + if( touch.edit ) + touch.edit->finger = -1; + if( touch.selection ) + touch.selection->finger = -1; + touch.edit = touch.selection = NULL; + touch.resize_finger = touch.move_finger = touch.look_finger = -1; + + if( CVAR_TO_BOOL(touch_in_menu) ) + { + Cvar_Set( "touch_in_menu", "0" ); + } + else if( cls.key_dest == key_game ) + Touch_WriteConfig(); +} + +void Touch_DeleteProfile_f( void ) +{ + if( Cmd_Argc() != 2 ) + { + Con_Printf( "Usage: touch_deleteprofile \n" ); + return; + } + + // delete profile + FS_Delete( va( "touch_profiles/%s.cfg", Cmd_Argv( 1 ))); +} + +void Touch_InitEditor( void ) +{ + float x = 0.1 * (SCR_H/SCR_W); + float y = 0.05; + + Touch_ClearList( &touch.list_edit ); + + Touch_AddButton( &touch.list_edit, "close", "touch_default/edit_close.tga", "touch_disableedit", 0, y, x, y + 0.1, (byte*)"\xff\xff\xff\xff" )->flags |= TOUCH_FL_NOEDIT; + Touch_AddButton( &touch.list_edit, "close", "#Close and save", "", x, y, x + 0.2, y + 0.1, (byte*)"\xff\xff\xff\xff" )->flags |= TOUCH_FL_NOEDIT; + y += 0.2; + Touch_AddButton( &touch.list_edit, "cancel", "touch_default/edit_reset.tga", "touch_reloadconfig", 0, y, x, y + 0.1, (byte*)"\xff\xff\xff\xff" )->flags |= TOUCH_FL_NOEDIT; + Touch_AddButton( &touch.list_edit, "close", "#Cancel and reset", "", x, y, x + 0.2, y + 0.1, (byte*)"\xff\xff\xff\xff" )->flags |= TOUCH_FL_NOEDIT; + y += 0.2; + touch.hidebutton = Touch_AddButton( &touch.list_edit, "showhide", "touch_default/edit_hide.tga", "touch_toggleselection", 0, y, x, y + 0.1, (byte*)"\xff\xff\xff\xff" ); + touch.hidebutton->flags |= TOUCH_FL_HIDE | TOUCH_FL_NOEDIT; +} + +void Touch_Init( void ) +{ + rgba_t color; + if( touch.initialized ) + return; + touch.mempool = Mem_AllocPool( "Touch" ); + //touch.first = touch.last = NULL; + Con_Printf( S_ERROR "IN_TouchInit()\n"); + touch.move_finger = touch.resize_finger = touch.look_finger = -1; + touch.state = state_none; + touch.showeditbuttons = true; + touch.clientonly = false; + touch.precision = false; + MakeRGBA( touch.scolor, 255, 255, 255, 255 ); + touch.swidth = 1; + g_LastDefaultButton = 0; + + touch.list_edit.first = touch.list_edit.last = NULL; + touch.list_user.first = touch.list_user.last = NULL; + + // fill default buttons list + MakeRGBA( color, 255, 255, 255, 255 ); + Touch_AddDefaultButton( "look", "", "_look", 0.500000, 0.000000, 1.000000, 1, color, 0, 0, 0 ); + Touch_AddDefaultButton( "move", "", "_move", 0.000000, 0.000000, 0.500000, 1, color, 0, 0, 0 ); + Touch_AddDefaultButton( "invnext", "touch_default/next_weap.tga", "invnext", 0.000000, 0.530200, 0.120000, 0.757428, color, 2, 1, 0 ); + Touch_AddDefaultButton( "invprev", "touch_default/prev_weap.tga", "invprev", 0.000000, 0.075743, 0.120000, 0.302971, color, 2, 1, 0 ); + Touch_AddDefaultButton( "use", "touch_default/use.tga", "+use", 0.880000, 0.454457, 1.000000, 0.681685, color, 2, 1, 0 ); + Touch_AddDefaultButton( "jump", "touch_default/jump.tga", "+jump", 0.880000, 0.227228, 1.000000, 0.454457, color, 2, 1, 0 ); + Touch_AddDefaultButton( "attack", "touch_default/shoot.tga", "+attack", 0.760000, 0.530200, 0.880000, 0.757428, color, 2, 1, 0 ); + Touch_AddDefaultButton( "attack2", "touch_default/shoot_alt.tga", "+attack2", 0.760000, 0.302971, 0.880000, 0.530200, color, 2, 1, 0 ); + Touch_AddDefaultButton( "loadquick", "touch_default/load.tga", "loadquick", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 16 ); + Touch_AddDefaultButton( "savequick", "touch_default/save.tga", "savequick", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 16 ); + Touch_AddDefaultButton( "messagemode", "touch_default/keyboard.tga", "messagemode", 0.840000, 0.000000, 0.920000, 0.151486, color, 2, 1, 8 ); + Touch_AddDefaultButton( "reload", "touch_default/reload.tga", "+reload", 0.000000, 0.302971, 0.120000, 0.530200, color, 2, 1, 0 ); + Touch_AddDefaultButton( "flashlight", "touch_default/flash_light_filled.tga", "impulse 100", 0.920000, 0.000000, 1.000000, 0.151486, color, 2, 1, 0 ); + Touch_AddDefaultButton( "scores", "touch_default/map.tga", "+showscores", 0.760000, 0.000000, 0.840000, 0.151486, color, 2, 1, 8 ); + Touch_AddDefaultButton( "show_numbers", "touch_default/show_weapons.tga", "exec touch_default/numbers.cfg", 0.440000, 0.833171, 0.520000, 0.984656, color, 2, 1, 0 ); + Touch_AddDefaultButton( "duck", "touch_default/crouch.tga", "+duck", 0.880000, 0.757428, 1.000000, 0.984656, color, 2, 1, 0 ); + Touch_AddDefaultButton( "tduck", "touch_default/tduck.tga", ";+duck", 0.560000, 0.833171, 0.620000, 0.946785, color, 2, 1, 0 ); + Touch_AddDefaultButton( "edit", "touch_default/settings.tga", "touch_enableedit", 0.420000, 0.000000, 0.500000, 0.151486, color, 2, 1, 32 ); + Touch_AddDefaultButton( "menu", "touch_default/menu.tga", "escape", 0.000000, 0.833171, 0.080000, 0.984656, color, 2, 1, 0 ); + + + Cmd_AddCommand( "touch_addbutton", Touch_AddButton_f, "add native touch button" ); + Cmd_AddCommand( "touch_removebutton", IN_TouchRemoveButton_f, "remove native touch button" ); + Cmd_AddCommand( "touch_enableedit", Touch_EnableEdit_f, "enable button editing mode" ); + Cmd_AddCommand( "touch_disableedit", Touch_DisableEdit_f, "disable button editing mode" ); + Cmd_AddCommand( "touch_settexture", Touch_SetTexture_f, "change button texture" ); + Cmd_AddCommand( "touch_setcolor", Touch_SetColor_f, "change button color" ); + Cmd_AddCommand( "touch_setcommand", Touch_SetCommand_f, "change button command" ); + Cmd_AddCommand( "touch_setflags", Touch_SetFlags_f, "change button flags (be careful)" ); + Cmd_AddCommand( "touch_show", Touch_Show_f, "show button" ); + Cmd_AddCommand( "touch_hide", Touch_Hide_f, "hide button" ); + Cmd_AddCommand( "touch_list", Touch_ListButtons_f, "list buttons" ); + Cmd_AddCommand( "touch_removeall", Touch_RemoveAll_f, "remove all buttons" ); + Cmd_AddCommand( "touch_loaddefaults", Touch_LoadDefaults_f, "generate config from defaults" ); + Cmd_AddCommand( "touch_roundall", Touch_RoundAll_f, "round all buttons coordinates to grid" ); + Cmd_AddCommand( "touch_exportconfig", Touch_ExportConfig_f, "export config keeping aspect ratio" ); + Cmd_AddCommand( "touch_set_stroke", Touch_Stroke_f, "set global stroke width and color" ); + Cmd_AddCommand( "touch_setclientonly", Touch_SetClientOnly_f, "when 1, only client buttons are shown" ); + Cmd_AddCommand( "touch_reloadconfig", Touch_ReloadConfig_f, "load config, not saving changes" ); + Cmd_AddCommand( "touch_writeconfig", Touch_WriteConfig, "save current config" ); + Cmd_AddCommand( "touch_deleteprofile", Touch_DeleteProfile_f, "delete profile by name" ); + Cmd_AddCommand( "touch_generate_code", Touch_GenetateCode_f, "create code sample for mobility API" ); + Cmd_AddCommand( "touch_fade", Touch_Fade_f, "start fade animation for selected buttons" ); + Cmd_AddCommand( "touch_toggleselection", Touch_ToggleSelection_f, "toggle vidibility on selected button in editor" ); + + // not saved, just runtime state for scripting + touch_in_menu = Cvar_Get( "touch_in_menu", "0", 0, "draw touch in menu (for internal use only)" ); + + // sensitivity configuration + touch_forwardzone = Cvar_Get( "touch_forwardzone", "0.06", 0, "forward touch zone" ); + touch_sidezone = Cvar_Get( "touch_sidezone", "0.06", 0, "side touch zone" ); + touch_pitch = Cvar_Get( "touch_pitch", "90", 0, "touch pitch sensitivity" ); + touch_yaw = Cvar_Get( "touch_yaw", "120", 0, "touch yaw sensitivity" ); + touch_nonlinear_look = Cvar_Get( "touch_nonlinear_look", "0", 0, "enable nonlinear touch look" ); + touch_pow_factor = Cvar_Get( "touch_pow_factor", "1.3", 0, "set > 1 to enable" ); + touch_pow_mult = Cvar_Get( "touch_pow_mult", "400.0", 0, "power multiplier, usually 200-1000" ); + touch_exp_mult = Cvar_Get( "touch_exp_mult", "0", 0, "exponent multiplier, usually 20-200, 0 to disable" ); + + // touch.cfg + touch_grid_count = Cvar_Get( "touch_grid_count", "50", 0, "touch grid count" ); + touch_grid_enable = Cvar_Get( "touch_grid_enable", "1", 0, "enable touch grid" ); + touch_config_file = Cvar_Get( "touch_config_file", "touch.cfg", FCVAR_ARCHIVE, "current touch profile file" ); + touch_precise_amount = Cvar_Get( "touch_precise_amount", "0.5", 0, "sensitivity multiplier for precise-look" ); + touch_highlight_r = Cvar_Get( "touch_highlight_r", "1.0", 0, "highlight r color" ); + touch_highlight_g = Cvar_Get( "touch_highlight_g", "1.0", 0, "highlight g color" ); + touch_highlight_b = Cvar_Get( "touch_highlight_b", "1.0", 0, "highlight b color" ); + touch_highlight_a = Cvar_Get( "touch_highlight_a", "1.0", 0, "highlight alpha" ); + touch_dpad_radius = Cvar_Get( "touch_dpad_radius", "1.0", 0, "dpad radius multiplier" ); + touch_joy_radius = Cvar_Get( "touch_joy_radius", "1.0", 0, "joy radius multiplier" ); + touch_move_indicator = Cvar_Get( "touch_move_indicator", "0.0", 0, "indicate move events (0 to disable)" ); + touch_joy_texture = Cvar_Get( "touch_joy_texture", "touch_default/joy.tga", 0, "texture for move indicator"); + + // input devices cvar + touch_enable = Cvar_Get( "touch_enable", DEFAULT_TOUCH_ENABLE, FCVAR_ARCHIVE, "enable touch controls" ); + touch_emulate = Cvar_Get( "touch_emulate", "0", FCVAR_ARCHIVE, "emulate touch with mouse" ); + + /// TODO: touch sdl platform + // SDL_SetHint( SDL_HINT_ANDROID_SEPARATE_MOUSE_AND_TOUCH, "1" ); + + touch.initialized = true; +} +//int pfnGetScreenInfo( SCREENINFO *pscrinfo ); +static void Touch_InitConfig( void ) +{ + if( !touch.initialized ) + return; + + if( !host.config_executed ) + return; + + if( touch.config_loaded ) + return; + + /// TODO: hud font + //pfnGetScreenInfo( NULL ); //HACK: update hud screen parameters like iHeight + if( FS_FileExists( touch_config_file->string, true ) ) + Cbuf_AddText( va( "exec \"%s\"\n", touch_config_file->string ) ); + else Touch_LoadDefaults_f( ); + + Touch_InitEditor(); + touch.joytexture = ref.dllFuncs.GL_LoadTexture( touch_joy_texture->string, NULL, 0, TF_NOMIPMAP ); + touch.whitetexture = R_GetBuiltinTexture( REF_WHITE_TEXTURE ); + touch.configchanged = false; + touch.config_loaded = true; +} +qboolean Touch_IsVisible( touch_button_t *button ) +{ + if( !(button->flags & TOUCH_FL_CLIENT) && touch.clientonly ) + return false; // skip nonclient buttons in clientonly mode + + if( touch.state >= state_edit ) + return true; //!!! Draw when editor is open + + if( button->flags & TOUCH_FL_HIDE ) + return false; // skip hidden + + if( button->flags & TOUCH_FL_SP && CL_GetMaxClients() != 1 ) + return false; // skip singleplayer(load, save) buttons in multiplayer + + if( button->flags & TOUCH_FL_MP && CL_GetMaxClients() == 1 ) + return false; // skip multiplayer buttons in singleplayer + + return true; + /* + return ( !touch.clientonly || ( button->flags & TOUCH_FL_CLIENT) ) && + ( + ( touch.state >= state_edit ) + ||( !( button->flags & TOUCH_FL_HIDE ) + && ( !(button->flags & TOUCH_FL_SP) || ( CL_GetMaxClients() == 1 ) ) + && ( !(button->flags & TOUCH_FL_MP) || ( CL_GetMaxClients() != 1 ) ) ) + ); + */ +} + +void Touch_DrawTexture ( float x1, float y1, float x2, float y2, int texture, byte r, byte g, byte b, byte a ) +{ + if( x1 >= x2 ) + return; + + if( y1 >= y2 ) + return; + + ref.dllFuncs.Color4ub( r, g, b, a ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(x1), + TO_SCRN_Y(y1), + TO_SCRN_X(x2 - x1), + TO_SCRN_Y(y2 - y1), + 0, 0, 1, 1, texture ); +} + +#define GRID_COUNT_X ((int)touch_grid_count->value) +#define GRID_COUNT_Y (((int)touch_grid_count->value) * SCR_H / SCR_W) +#define GRID_X (1.0/GRID_COUNT_X) +#define GRID_Y (SCR_W/SCR_H/GRID_COUNT_X) +#define GRID_ROUND_X(x) ((float)round( x * GRID_COUNT_X ) / GRID_COUNT_X) +#define GRID_ROUND_Y(x) ((float)round( x * GRID_COUNT_Y ) / GRID_COUNT_Y) + +static void IN_TouchCheckCoords( float *x1, float *y1, float *x2, float *y2 ) +{ + /// TODO: grid check here + if( *x2 - *x1 < GRID_X * 2 ) + *x2 = *x1 + GRID_X * 2; + if( *y2 - *y1 < GRID_Y * 2) + *y2 = *y1 + GRID_Y * 2; + if( *x1 < 0 ) + *x2 -= *x1, *x1 = 0; + if( *y1 < 0 ) + *y2 -= *y1, *y1 = 0; + if( *y2 > 1 ) + *y1 -= *y2 - 1, *y2 = 1; + if( *x2 > 1 ) + *x1 -= *x2 - 1, *x2 = 1; + if ( CVAR_TO_BOOL(touch_grid_enable) ) + { + *x1 = GRID_ROUND_X( *x1 ); + *x2 = GRID_ROUND_X( *x2 ); + *y1 = GRID_ROUND_Y( *y1 ); + *y2 = GRID_ROUND_Y( *y2 ); + } +} + +float Touch_DrawCharacter( float x, float y, int number, float size ) +{ + float s1, s2, t1, t2, width, height; + int w, h; + wrect_t *prc; + if( !cls.creditsFont.valid ) + return 0; + + number &= 255; + number = Con_UtfProcessChar( number ); + + + R_GetTextureParms( &w, &h, cls.creditsFont.hFontTexture ); + prc = &cls.creditsFont.fontRc[number]; + + s1 = ((float)prc->left) / (float)w; + t1 = ((float)prc->top) / (float)h; + s2 = ((float)prc->right) / (float)w; + t2 = ((float)prc->bottom) / (float)h; + + width = ((float)( prc->right - prc->left )) / 1024.0f * size; + height = ((float)( prc->bottom - prc->top )) / 1024.0f * size; + + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(x), TO_SCRN_Y(y), TO_SCRN_X(width), TO_SCRN_X(height), s1, t1, s2, t2, cls.creditsFont.hFontTexture ); + return width; +} + +float Touch_DrawText( float x1, float y1, float x2, float y2, const char *s, byte *color, float size ) +{ + float x = x1; + float maxy = y2; + float maxx; + if( x2 ) + maxx = x2 - cls.creditsFont.charWidths['M'] / 1024.0f * size; + else + maxx = 1; + + if( !cls.creditsFont.valid ) + return GRID_X * 2; + Con_UtfProcessChar( 0 ); + + ref.dllFuncs.GL_SetRenderMode( kRenderTransAdd ); + + // text is additive and alpha does not work + ref.dllFuncs.Color4ub( color[0] * ( (float)color[3] /255.0f ), color[1] * ( (float)color[3] /255.0f ), + color[2] * ( (float)color[3] /255.0f ), 255 ); + + while( *s ) + { + while( *s && ( *s != '\n' ) && ( *s != ';' ) && ( x1 < maxx ) ) + x1 += Touch_DrawCharacter( x1, y1, *s++, size ); + y1 += cls.creditsFont.charHeight / 1024.f * size / SCR_H * SCR_W; + + if( y1 >= maxy ) + break; + + if( *s == '\n' || *s == ';' ) + s++; + x1 = x; + } + ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); + return x1; +} + +void Touch_DrawButtons( touchbuttonlist_t *list ) +{ + touch_button_t *button; + + for( button = list->first; button; button = button->next ) + { + if( Touch_IsVisible( button ) ) + { + rgba_t color; + MakeRGBA( color, B( color[0] ), B( color[1] ), B( color[2] ), B( color[3] ) ); + + if( B( fadespeed ) ) + { + button->fade += B( fadespeed ) * host.frametime; + button->fade = bound( 0, B(fade), 1 ); + if( ( B( fade ) == 0 ) || ( B(fade) == 1 ) ) + B( fadespeed ) = 0; + if( ( ( B( fade ) >= B( fadeend ) ) && ( B( fadespeed ) > 0 ) ) || + ( ( B( fade ) <= B( fadeend ) ) && ( B( fadespeed ) < 0 ) ) ) + B( fadespeed ) = 0, B( fade ) = B( fadeend ) ; + } + + if( ( B( finger ) != -1 ) && !( B( flags ) & TOUCH_FL_CLIENT ) ) + { + color[0] = bound( 0,(float) color[0] * touch_highlight_r->value, 255 ); + color[1] = bound( 0,(float) color[1] * touch_highlight_g->value, 255 ); + color[2] = bound( 0,(float) color[2] * touch_highlight_b->value, 255 ); + color[3] = bound( 0,(float) color[3] * touch_highlight_a->value, 255 ); + } + + color[3] *= B( fade ); + if( button->texturefile[0] == '#' ) + Touch_DrawText( touch.swidth/SCR_W + B(x1), touch.swidth/SCR_H + B(y1), B(x2), B(y2), button->texturefile + 1, color, B( aspect )?B(aspect):1 ); + else if( button->texturefile[0] ) + { + if( button->texture == -1 ) + { + button->texture = ref.dllFuncs.GL_LoadTexture( button->texturefile, NULL, 0, TF_NOMIPMAP ); + } + + if( B(flags) & TOUCH_FL_DRAW_ADDITIVE ) + ref.dllFuncs.GL_SetRenderMode( kRenderTransAdd ); + + Touch_DrawTexture( B(x1), B(y1), B(x2), B(y2), B(texture), color[0], color[1], color[2], color[3] ); + + ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); + } + if( B(flags) & TOUCH_FL_STROKE ) + { + ref.dllFuncs.Color4ub( touch.scolor[0], touch.scolor[1], touch.scolor[2], touch.scolor[3] * B( fade ) ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(B(x1)), + TO_SCRN_Y(B(y1)), + touch.swidth, + TO_SCRN_Y(B(y2)-B(y1)) - touch.swidth, + 0, 0, 1, 1, touch.whitetexture ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(B(x1)) + touch.swidth, + TO_SCRN_Y(B(y1)), + TO_SCRN_X(B(x2)-B(x1)) - touch.swidth, + touch.swidth, + 0, 0, 1, 1, touch.whitetexture ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(B(x2))-touch.swidth, + TO_SCRN_Y(B(y1)) + touch.swidth, + touch.swidth, + TO_SCRN_Y(B(y2)-B(y1)) - touch.swidth, + 0, 0, 1, 1, touch.whitetexture ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(B(x1)), + TO_SCRN_Y(B(y2))-touch.swidth, + TO_SCRN_X(B(x2)-B(x1)) - touch.swidth, + touch.swidth, + 0, 0, 1, 1, touch.whitetexture ); + ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); + } + } + if( touch.state >= state_edit && !( button->flags & TOUCH_FL_NOEDIT ) ) + { + rgba_t color; + if( !( button->flags & TOUCH_FL_HIDE ) ) + Touch_DrawTexture( B(x1), B(y1), B(x2), B(y2), touch.whitetexture, 255, 255, 0, 32 ); + else + Touch_DrawTexture( B(x1), B(y1), B(x2), B(y2), touch.whitetexture, 128, 128, 128, 128 ); + MakeRGBA( color, 255, 255,127, 255 ); + Con_DrawString( TO_SCRN_X( B(x1) ), TO_SCRN_Y( B(y1) ), B(name), color ); + } + } + +} + +void Touch_Draw( void ) +{ + touch_button_t *button; + + if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) ) + return; + + Touch_InitConfig(); + + if( cls.key_dest != key_game && !CVAR_TO_BOOL(touch_in_menu) ) + return; + + ref.dllFuncs.GL_SetRenderMode( kRenderTransTexture ); + + if( touch.state >= state_edit && CVAR_TO_BOOL(touch_grid_enable) ) + { + float x; + if( CVAR_TO_BOOL(touch_in_menu) ) + Touch_DrawTexture( 0, 0, 1, 1, touch.whitetexture, 32, 32, 32, 255 ); + else + Touch_DrawTexture( 0, 0, 1, 1, touch.whitetexture, 0, 0, 0, 112 ); + ref.dllFuncs.Color4ub( 0, 224, 224, 112 ); + for ( x = 0; x < 1 ; x += GRID_X ) + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X(x), + 0, + 1, + TO_SCRN_Y(1), + 0, 0, 1, 1, touch.whitetexture ); + for ( x = 0; x < 1 ; x += GRID_Y ) + ref.dllFuncs.R_DrawStretchPic( 0, + TO_SCRN_Y(x), + TO_SCRN_X(1), + 1, + 0, 0, 1, 1, touch.whitetexture ); + } + + Touch_DrawButtons( &touch.list_user ); + + if( touch.state >= state_edit ) + { + rgba_t color; + + MakeRGBA( color, 255, 255, 255, 255 ); + + if( touch.edit ) + { + float x1 = touch.edit->x1, + y1 = touch.edit->y1, + x2 = touch.edit->x2, + y2 = touch.edit->y2; + IN_TouchCheckCoords( &x1, &y1, &x2, &y2 ); + Touch_DrawTexture( x1, y1, x2, y2, touch.whitetexture, 0, 255, 0, 32 ); + } + + Touch_DrawTexture( 0, 0, GRID_X, GRID_Y, touch.whitetexture, 255, 255, 255, 64 ); + + + if( touch.showeditbuttons ) + Touch_DrawButtons( &touch.list_edit ); + + /// TODO: move to mainui + if( touch.selection ) + { + button = touch.selection; + Touch_DrawTexture( B(x1), B(y1), B(x2), B(y2), touch.whitetexture, 255, 0, 0, 64 ); + + Con_DrawString( 0, TO_SCRN_Y(GRID_Y * 11), "Selection:", color ); + Con_DrawString( Con_DrawString( 0, TO_SCRN_Y(GRID_Y*12), "Name: ", color ), + TO_SCRN_Y(GRID_Y*12), B(name), color ); + Con_DrawString( Con_DrawString( 0, TO_SCRN_Y(GRID_Y*13), "Texture: ", color ), + TO_SCRN_Y(GRID_Y*13), B(texturefile), color ); + Con_DrawString( Con_DrawString( 0, TO_SCRN_Y(GRID_Y*14), "Command: ", color ), + TO_SCRN_Y(GRID_Y*14), B(command), color ); + } + } + + ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); + + if( ( touch.move_finger != -1 ) && touch.move && touch_move_indicator->value ) + { + float width; + float height; + if( FBitSet( touch_joy_texture->flags, FCVAR_CHANGED ) ) + { + ClearBits( touch_joy_texture->flags, FCVAR_CHANGED ); + touch.joytexture = ref.dllFuncs.GL_LoadTexture( touch_joy_texture->string, NULL, 0, TF_NOMIPMAP ); + } + if( touch.move->type == touch_move ) + { + width = touch_sidezone->value; + height = touch_forwardzone->value; + } + else + { + width = (touch.move->x2 - touch.move->x1)/2; + height = (touch.move->y2 - touch.move->y1)/2; + } + ref.dllFuncs.Color4ub( 255, 255, 255, 128 ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X( touch.move_start_x - GRID_X * touch_move_indicator->value ), + TO_SCRN_Y( touch.move_start_y - GRID_Y * touch_move_indicator->value ), + TO_SCRN_X( GRID_X * 2 * touch_move_indicator->value ), TO_SCRN_Y( GRID_Y * 2 * touch_move_indicator->value ), 0, 0, 1, 1, touch.joytexture ); + ref.dllFuncs.Color4ub( 255, 255, 255, 255 ); + ref.dllFuncs.R_DrawStretchPic( TO_SCRN_X( touch.move_start_x + touch.side * width - GRID_X * touch_move_indicator->value ), + TO_SCRN_Y( touch.move_start_y - touch.forward * height - GRID_Y * touch_move_indicator->value ), + TO_SCRN_X( GRID_X * 2 * touch_move_indicator->value ), TO_SCRN_Y( GRID_Y * 2 * touch_move_indicator->value ), 0, 0, 1, 1, touch.joytexture ); + + } + +} + +// clear move and selection state +static void IN_TouchEditClear( void ) +{ + // allow keep move/look fingers when doing touch_removeall + //touch.move_finger = touch.look_finger = -1; + + if( touch.state < state_edit ) + return; + + touch.state = state_edit; + + if( touch.edit ) + touch.edit->finger = -1; + + touch.resize_finger = -1; + touch.edit = NULL; + touch.selection = NULL; +} + +static void Touch_EditMove( touchEventType type, int fingerID, float x, float y, float dx, float dy ) +{ + if( touch.edit->finger == fingerID ) + { + if( type == event_up ) // shutdown button move + { + touch_button_t *button = touch.edit; + IN_TouchCheckCoords( &B(x1), &B(y1), &B(x2), &B(y2) ); + IN_TouchEditClear(); + if( button->type == touch_command ) + { + touch.selection = button; + + // update "hide" editor button + touch.hidebutton->texture = -1; + touch.hidebutton->flags &= ~TOUCH_FL_HIDE; + + if( button->flags & TOUCH_FL_HIDE ) + Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_show.tga" ); + else + Q_strcpy( touch.hidebutton->texturefile, "touch_default/edit_hide.tga" ); + } + } + if( type == event_motion ) // shutdown button move + { + touch.edit->y1 += dy; + touch.edit->y2 += dy; + touch.edit->x1 += dx; + touch.edit->x2 += dx; + } + } + else + { + if( type == event_down ) // enable resizing + { + if( touch.resize_finger == -1 ) + { + touch.resize_finger = fingerID; + } + } + if( type == event_up ) // disable resizing + { + if( touch.resize_finger == fingerID ) + { + touch.resize_finger = -1; + } + } + if( type == event_motion ) // perform resizing + { + if( touch.resize_finger == fingerID ) + { + touch.edit->y2 += dy; + touch.edit->x2 += dx; + } + } + } +} + +static void Touch_Motion( touchEventType type, int fingerID, float x, float y, float dx, float dy ) +{ + // walk + if( fingerID == touch.move_finger ) + { + // check bounds + if( touch_forwardzone->value <= 0 ) + Cvar_SetValue( "touch_forwardzone", 0.5 ); + if( touch_sidezone->value <= 0 ) + Cvar_SetValue( "touch_sidezone", 0.3 ); + + if( !touch.move || touch.move->type == touch_move ) + { + // move relative to touch start + touch.forward = ( touch.move_start_y - y ) / touch_forwardzone->value; + touch.side = ( x - touch.move_start_x ) / touch_sidezone->value; + } + else if( touch.move->type == touch_joy ) + { + // move relative to joy center + touch.forward = ( ( touch.move->y2 + touch.move->y1 ) - y * 2 ) / ( touch.move->y2 - touch.move->y1 ) * touch_joy_radius->value; + touch.side = ( x * 2 - ( touch.move->x2 + touch.move->x1 ) ) / ( touch.move->x2 - touch.move->x1 ) * touch_joy_radius->value; + } + else if( touch.move->type == touch_dpad ) + { + // like joy, but without acceleration. useful for bhop + touch.forward = round( ( (touch.move->y2 + touch.move->y1) - y * 2 ) / ( touch.move->y2 - touch.move->y1 ) * touch_dpad_radius->value ); + touch.side = round( ( x * 2 - (touch.move->x2 + touch.move->x1) ) / ( touch.move->x2 - touch.move->x1 ) * touch_dpad_radius->value ); + } + + touch.forward = bound( -1, touch.forward, 1 ); + touch.side = bound( -1, touch.side, 1 ); + } + + // process look + if( fingerID == touch.look_finger ) + { + if( touch.precision ) + dx *= touch_precise_amount->value, dy *= touch_precise_amount->value; + + if( CVAR_TO_BOOL(touch_nonlinear_look) ) + { + float dabs, dcos, dsin; + + // save angle, modify only velocity + dabs = sqrt( dx * dx + dy * dy ); + + if( dabs < 0.000001 ) + return; // no motion, avoid division by zero + + dcos = dx / dabs; + dsin = dy / dabs; + + if( touch_exp_mult->value > 1 ) + dabs = ( exp( dabs * touch_exp_mult->value ) - 1 ) / touch_exp_mult->value; + + if( touch_pow_mult->value > 1 && touch_pow_factor->value > 1 ) + dabs = pow( dabs * touch_pow_mult->value, touch_pow_factor->value ) / touch_pow_mult->value; + + dx = dabs * dcos; + dy = dabs * dsin; + } + + // prevent breaking engine/client with bad values + if( IS_NAN( dx ) || IS_NAN( dy ) ) + return; + + // accumulate + touch.yaw -= dx * touch_yaw->value, touch.pitch += dy * touch_pitch->value; + } +} + + +static qboolean Touch_ButtonPress( touchbuttonlist_t *list, touchEventType type, int fingerID, float x, float y, float dx, float dy ) +{ + touch_button_t *button; + qboolean result = false; + + // run from end(front) to start(back) + for( button = list->last; button; button = button->prev ) + { + + // skip invisible buttons + if( !Touch_IsVisible( button ) ) + continue; + + if( type == event_down ) + { + // button bounds check + if( ( x > button->x1 && + x < button->x2 ) && + ( y < button->y2 && + y > button->y1 ) ) + { + button->finger = fingerID; + + if( button->type == touch_command ) + { + char command[256]; + + // command down: just execute command + Q_snprintf( command, sizeof( command ), "%s\n", button->command ); + Cbuf_AddText( command ); + + // increase precision + if( B(flags) & TOUCH_FL_PRECISION ) + touch.precision = true; + + result = true; + } + + // initialize motion when player touched motion zone + if( button->type == touch_move || button->type == touch_joy || button->type == touch_dpad ) + { + if( touch.move_finger !=-1 ) + { + // prevent initializing move while already moving + // revert finger switch, leave first finger + button->finger = touch.move_finger; + continue; + } + + result = true; + + if( touch.look_finger == fingerID ) + { + touch_button_t *newbutton; + + // this is an error, try recover + touch.move_finger = touch.look_finger = -1; + + // player touched touch_move with enabled look mode + // and same finger id. release all move triggers + for( newbutton = list->first; newbutton; newbutton = newbutton->next ) + if( ( newbutton->type == touch_move ) || ( newbutton->type == touch_look ) ) newbutton->finger = -1; + + Con_DPrintf( S_ERROR "Touch: touch_move on look finger %d!\n", fingerID ); + continue; + } + + // initialize move mode + touch.move_finger = fingerID; + touch.move = button; + + if( touch.move->type == touch_move ) + { + // initial position is first touch + touch.move_start_x = x; + touch.move_start_y = y; + } + else if( touch.move->type == touch_joy ) + { + // initial position is button center + touch.move_start_y = (touch.move->y2 + touch.move->y1) / 2; + touch.move_start_x = (touch.move->x2 + touch.move->x1) / 2; + + // start move instanly + touch.forward = ((touch.move->y2 + touch.move->y1) - y * 2) / (touch.move->y2 - touch.move->y1); + touch.side = (x * 2 - (touch.move->x2 + touch.move->x1)) / (touch.move->x2 - touch.move->x1); + } + else if( touch.move->type == touch_dpad ) + { + // dame as joy, but round + touch.move_start_y = (touch.move->y2 + touch.move->y1) / 2; + touch.move_start_x = (touch.move->x2 + touch.move->x1) / 2; + + // start move instanly + touch.forward = round(((touch.move->y2 + touch.move->y1) - y * 2) / (touch.move->y2 - touch.move->y1)); + touch.side = round((x * 2 - (touch.move->x2 + touch.move->x1)) / (touch.move->x2 - touch.move->x1)); + } + } + + // initialize look + if( button->type == touch_look ) + { + if( touch.look_finger !=-1 ) + { + // prevent initializing look while already looking + // revert finger switch, leave first finger + button->finger = touch.look_finger; + continue; + } + + result = true; + + if( touch.move_finger == fingerID ) + { + touch_button_t *newbutton; + + // this is an error, try recover + touch.move_finger = touch.look_finger = -1; + + // player touched touch_move with enabled look mode + // and same finger id. release all move triggers + for( newbutton = list->first; newbutton; newbutton = newbutton->next ) + if( ( newbutton->type == touch_move ) || ( newbutton->type == touch_look ) ) newbutton->finger = -1; + + Con_Printf( S_ERROR "touch: touch_look on move finger %d!\n", fingerID ); + continue; + } + + touch.look_finger = fingerID; + } + } + } + + if( type == event_up ) + { + // no bounds check here. + // button released when finger released + if( fingerID == button->finger ) + { + button->finger = -1; + + // handle +command, replace by -command + if( button->type == touch_command ) + { + if( button->command[0] == '+' ) + { + char command[256]; + + Q_snprintf( command, sizeof( command ), "%s\n", button->command ); + command[0] = '-'; + Cbuf_AddText( command ); + } + + // disable precision mode + if( B(flags) & TOUCH_FL_PRECISION ) + touch.precision = false; + + result = true; + } + + // release motion buttons + if( button->type == touch_move || button->type == touch_joy || button->type == touch_dpad ) + { + touch.move_finger = -1; + touch.forward = touch.side = 0; + touch.move = NULL; + } + + // release look buttons + if( button->type == touch_look ) + { + touch.look_finger = -1; + } + } + } + } + + return result; +} + +static qboolean Touch_ButtonEdit( touchEventType type, int fingerID, float x, float y, float dx, float dy ) +{ + touch_button_t *button; + + // edit buttons are on y1 + if( type == event_down ) + { + if( (x < GRID_X) && (y < GRID_Y) ) + { + touch.showeditbuttons ^= true; + return true; + } + + if( touch.showeditbuttons && Touch_ButtonPress( &touch.list_edit, type, fingerID, x, y, dx, dy ) ) + return true; + } + + // run from end(front) to start(back) + for( button = touch.list_user.last; button; button = button->prev ) + { + if( type == event_down ) + { + if( ( x > button->x1 && + x < button->x2 ) && + ( y < button->y2 && + y > button->y1 ) ) + { + button->finger = fingerID; + + // do not edit NOEDIT buttons + if( button->flags & TOUCH_FL_NOEDIT ) + continue; + + touch.edit = button; + touch.selection = NULL; + + // make button last to bring it up + if( ( button->next ) && ( button->type == touch_command ) ) + { + if( button->prev ) + button->prev->next = button->next; + else + touch.list_user.first = button->next; + + button->next->prev = button->prev; + touch.list_user.last->next = button; + button->prev = touch.list_user.last; + button->next = NULL; + touch.list_user.last = button; + } + touch.state = state_edit_move; + return true; + } + } + if( type == event_up ) + if( fingerID == button->finger ) + button->finger = -1; + } + + if( type == event_down ) + { + touch.selection = NULL; + touch.hidebutton->flags |= TOUCH_FL_HIDE; + } + + return false; +} + +static int Touch_ControlsEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ) +{ + if( touch.state == state_edit_move ) + { + Touch_EditMove( type, fingerID, x, y, dx, dy ); + return 1; + } + + if( touch.state == state_edit && Touch_ButtonEdit( type, fingerID, x, y, dx, dy ) ) + return true; + if( Touch_ButtonPress( &touch.list_user, type, fingerID, x, y, dx, dy ) ) + return true; + if( type == event_motion ) + Touch_Motion( type, fingerID, x, y, dx, dy ); + return true; +} + +int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ) +{ + + // simulate menu mouse click + if( cls.key_dest != key_game && !CVAR_TO_BOOL(touch_in_menu) ) + { + touch.move_finger = touch.resize_finger = touch.look_finger = -1; + // Hack for keyboard, hope it help + if( cls.key_dest == key_console || cls.key_dest == key_message ) + { + Key_EnableTextInput( true, true ); + if( cls.key_dest == key_console ) + { + static float y1 = 0; + y1 += dy; + if( dy > 0.4 ) + Con_Bottom(); + if( y1 > 0.01 ) + { + Con_PageUp( 1 ); + y1 = 0; + } + if( y1 < -0.01 ) + { + Con_PageDown( 1 ); + y1 = 0; + } + } + + // exit of console area + if( type == event_down && x < 0.1f && y > 0.9f ) + Cbuf_AddText( "escape\n" ); + } + UI_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) ); + //MsgDev( D_NOTE, "touch %d %d\n", TO_SCRN_X(x), TO_SCRN_Y(y) ); + if( type == event_down ) + Key_Event( K_MOUSE1, true ); + if( type == event_up ) + Key_Event( K_MOUSE1, false ); + return 0; + } + + + if( VGui_IsActive() ) + { + VGui_MouseMove( TO_SCRN_X(x), TO_SCRN_Y(y) ); + + if( type != event_motion ) + VGui_KeyEvent( K_MOUSE1, type == event_down ? 1 : 0 ); + + // allow scoreboard scroll + if( host.mouse_visible && type == event_motion ) + return 0; + } + + if( !touch.initialized || (!CVAR_TO_BOOL(touch_enable) && !touch.clientonly) ) + { +#if 0 + if( type == event_down ) + Key_Event( K_MOUSE1, true ); + if( type == event_up ) + Key_Event( K_MOUSE1, false ); + Android_AddMove( dx * (float)refState.width, dy * (float)refState.height ); +#endif + return 0; + } + + if( clgame.dllFuncs.pfnTouchEvent && clgame.dllFuncs.pfnTouchEvent( type, fingerID, x, y, dx, dy ) ) + return true; + + return Touch_ControlsEvent( type, fingerID, x, y, dx, dy ); +} + +void Touch_GetMove( float *forward, float *side, float *yaw, float *pitch ) +{ + *forward += touch.forward; + *side += touch.side; + *yaw += touch.yaw; + *pitch += touch.pitch; + touch.yaw = touch.pitch = 0; +} + +void Touch_KeyEvent( int key, int down ) +{ + int xi, yi; + float x, y; + static float lx, ly; + + if( !CVAR_TO_BOOL(touch_emulate) ) + { + if( CVAR_TO_BOOL(touch_enable) ) + return; + + if( !touch.clientonly ) + return; + } + + Platform_GetMousePos( &xi, &yi ); + + x = xi/SCR_W; + y = yi/SCR_H; + + if( cls.key_dest == key_menu && down < 2 && key == K_MOUSE1 ) + { + UI_MouseMove( xi, yi ); + UI_KeyEvent( key, down ); + } + + if( down == 1 ) + Touch_ControlsEvent( event_down, key == K_MOUSE1?0:1, x, y, 0, 0 ); + else + Touch_ControlsEvent( down? event_motion: event_up, key == K_MOUSE1?0:1, x, y, lx-x, ly-y ); + lx = x, ly = y; +} + +void Touch_Shutdown( void ) +{ + if( !touch.initialized ) + return; + Touch_RemoveAll_f(); + Cmd_RemoveCommand( "touch_addbutton" ); + Cmd_RemoveCommand( "touch_removebutton" ); + Cmd_RemoveCommand( "touch_enableedit" ); + Cmd_RemoveCommand( "touch_disableedit" ); + Cmd_RemoveCommand( "touch_settexture" ); + Cmd_RemoveCommand( "touch_setcolor" ); + Cmd_RemoveCommand( "touch_setcommand" ); + Cmd_RemoveCommand( "touch_setflags" ); + Cmd_RemoveCommand( "touch_show" ); + Cmd_RemoveCommand( "touch_hide" ); + Cmd_RemoveCommand( "touch_list" ); + Cmd_RemoveCommand( "touch_removeall" ); + Cmd_RemoveCommand( "touch_loaddefaults" ); + Cmd_RemoveCommand( "touch_roundall" ); + Cmd_RemoveCommand( "touch_exportconfig" ); + Cmd_RemoveCommand( "touch_set_stroke" ); + Cmd_RemoveCommand( "touch_setclientonly" ); + Cmd_RemoveCommand( "touch_reloadconfig" ); + Cmd_RemoveCommand( "touch_writeconfig" ); + Cmd_RemoveCommand( "touch_generate_code" ); + + touch.initialized = false; + Mem_FreePool( &touch.mempool ); +} +#endif diff --git a/engine/client/input.c b/engine/client/input.c index c9bf7746..4b251c1e 100644 --- a/engine/client/input.c +++ b/engine/client/input.c @@ -61,10 +61,8 @@ uint IN_CollectInputDevices( void ) if( !m_ignore->value ) // no way to check is mouse connected, so use cvar only ret |= INPUT_DEVICE_MOUSE; -#if 0 // TOUCHTODO - if( touch_enable->value ) + if( CVAR_TO_BOOL(touch_enable) ) ret |= INPUT_DEVICE_TOUCH; -#endif if( Joy_IsActive() ) // connected or enabled ret |= INPUT_DEVICE_JOYSTICK; @@ -94,13 +92,13 @@ void IN_LockInputDevices( qboolean lock ) { SetBits( m_ignore->flags, FCVAR_READ_ONLY ); SetBits( joy_enable->flags, FCVAR_READ_ONLY ); - // TOUCHTODO + SetBits( touch_enable->flags, FCVAR_READ_ONLY ); } else { ClearBits( m_ignore->flags, FCVAR_READ_ONLY ); ClearBits( joy_enable->flags, FCVAR_READ_ONLY ); - // TOUCHTODO + ClearBits( touch_enable->flags, FCVAR_READ_ONLY ); } } @@ -197,7 +195,7 @@ void IN_ToggleClientMouse( int newstate, int oldstate ) { // reset mouse pos, so cancel effect in game #ifdef XASH_SDL - if( 0 ) // touch_enable->value ) + if( CVAR_TO_BOOL(touch_enable) ) { SDL_SetRelativeMouseMode( SDL_FALSE ); SDL_SetWindowGrab( host.hWnd, SDL_FALSE ); @@ -268,7 +266,9 @@ void IN_ActivateMouse( qboolean force ) if( in_mouse_suspended ) { #ifdef XASH_SDL - SDL_ShowCursor( false ); + /// TODO: Platform_ShowCursor + if( !touch_emulate ) + SDL_ShowCursor( SDL_FALSE ); #endif UI_ShowCursor( false ); } @@ -360,9 +360,14 @@ void IN_MouseEvent( void ) { int i; + // touch emu: handle motion + if( CVAR_TO_BOOL( touch_emulate )) + Touch_KeyEvent( K_MOUSE1, 2 ); + if( !in_mouseinitialized || !in_mouseactive ) return; + if( m_ignore->value ) return; @@ -374,7 +379,7 @@ void IN_MouseEvent( void ) Platform_GetMousePos(&x, &y); if( host.mouse_visible ) SDL_ShowCursor( SDL_TRUE ); - else + else if( !CVAR_TO_BOOL( touch_emulate ) ) SDL_ShowCursor( SDL_FALSE ); if( x < host.window_center_x / 2 || @@ -426,6 +431,8 @@ void IN_Shutdown( void ) #ifdef XASH_USE_EVDEV Evdev_Shutdown(); #endif + + Touch_Shutdown(); } @@ -446,6 +453,8 @@ void IN_Init( void ) Joy_Init(); // common joystick support init + Touch_Init(); + #ifdef XASH_USE_EVDEV Evdev_Init(); #endif @@ -469,7 +478,7 @@ Common function for engine joystick movement #define R (1U << 3) // Right #define T (1U << 4) // Forward stop #define S (1U << 5) // Side stop -void IN_JoyAppendMove( usercmd_t *cmd, float forwardmove, float sidemove ) +static void IN_JoyAppendMove( usercmd_t *cmd, float forwardmove, float sidemove ) { static uint moveflags = T | S; @@ -545,7 +554,7 @@ void IN_JoyAppendMove( usercmd_t *cmd, float forwardmove, float sidemove ) void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qboolean includeMouse, qboolean includeSdlMouse ) { - if( !m_ignore->value || includeMouse ) + if( includeMouse ) { #if XASH_INPUT == INPUT_SDL if( includeSdlMouse ) @@ -572,7 +581,7 @@ void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qbo } Joy_FinalizeMove( forward, side, yaw, pitch ); - // IN_TouchMove( forward, side, yaw, pitch ); + Touch_GetMove( forward, side, yaw, pitch ); if( look_filter->value ) { @@ -591,9 +600,10 @@ IN_EngineAppendMove Called from cl_main.c after generating command in client ================ */ -void IN_EngineAppendMove( float frametime, usercmd_t *cmd, qboolean active ) +void IN_EngineAppendMove( float frametime, void *cmd1, qboolean active ) { float forward, side, pitch, yaw; + usercmd_t *cmd = cmd1; if( clgame.dllFuncs.pfnLookEvent ) return; @@ -605,15 +615,19 @@ void IN_EngineAppendMove( float frametime, usercmd_t *cmd, qboolean active ) if( active ) { - float sensitivity = ( (float)cl.local.scr_fov / (float)90.0f ); + float sensitivity = 1;//( (float)cl.local.scr_fov / (float)90.0f ); - IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized, m_enginemouse->value ); + IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized && !CVAR_TO_BOOL( m_ignore ), m_enginemouse->value ); IN_JoyAppendMove( cmd, forward, side ); - cmd->viewangles[YAW] += yaw * sensitivity; - cmd->viewangles[PITCH] += pitch * sensitivity; - cmd->viewangles[PITCH] = bound( -90, cmd->viewangles[PITCH], 90 ); + if( pitch || yaw ) + { + cmd->viewangles[YAW] += yaw * sensitivity; + cmd->viewangles[PITCH] += pitch * sensitivity; + cmd->viewangles[PITCH] = bound( -90, cmd->viewangles[PITCH], 90 ); + VectorCopy(cmd->viewangles, cl.viewangles); + } } } @@ -637,7 +651,7 @@ void Host_InputFrame( void ) if( clgame.dllFuncs.pfnLookEvent ) { - IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized, true ); + IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized && !CVAR_TO_BOOL( m_ignore ), true ); if( cls.key_dest == key_game ) { diff --git a/engine/client/input.h b/engine/client/input.h index fefda78a..761dd3a0 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -43,6 +43,7 @@ void IN_SetCursor( void *hCursor ); uint IN_CollectInputDevices( void ); void IN_LockInputDevices( qboolean lock ); +void IN_EngineAppendMove( float frametime, void *cmd, qboolean active ); // // in_touch.c @@ -55,25 +56,24 @@ typedef enum } touchEventType; extern convar_t *touch_enable; +extern convar_t *touch_emulate; -void IN_TouchDraw( void ); -void IN_TouchEditClear( void ); -void IN_TouchSetClientOnly( qboolean state ); -void IN_TouchRemoveButton( const char *name ); -void IN_TouchHideButtons( const char *name, unsigned char hide ); +void Touch_Draw( void ); +void Touch_SetClientOnly( qboolean state ); +void Touch_RemoveButton( const char *name ); +void Touch_HideButtons( const char *name, unsigned char hide ); //void IN_TouchSetCommand( const char *name, const char *command ); //void IN_TouchSetTexture( const char *name, const char *texture ); //void IN_TouchSetColor( const char *name, byte *color ); -void IN_TouchAddClientButton( const char *name, const char *texture, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ); -void IN_TouchAddDefaultButton( const char *name, const char *texturefile, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ); -void IN_TouchInitConfig( void ); -void IN_TouchWriteConfig( void ); -void IN_TouchInit( void ); -void IN_TouchShutdown( void ); -void IN_TouchMove( float * forward, float *side, float *yaw, float *pitch ); -void IN_TouchResetDefaultButtons( void ); +void Touch_AddClientButton( const char *name, const char *texture, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ); +void Touch_AddDefaultButton( const char *name, const char *texturefile, const char *command, float x1, float y1, float x2, float y2, byte *color, int round, float aspect, int flags ); +void Touch_WriteConfig( void ); +void Touch_Init( void ); +void Touch_Shutdown( void ); +void Touch_GetMove( float * forward, float *side, float *yaw, float *pitch ); +void Touch_ResetDefaultButtons( void ); int IN_TouchEvent( touchEventType type, int fingerID, float x, float y, float dx, float dy ); -void IN_TouchKeyEvent( int key, int down ); +void Touch_KeyEvent( int key, int down ); // // in_joy.c diff --git a/engine/client/keys.c b/engine/client/keys.c index 0ce42ae8..b70d3b84 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -652,6 +652,7 @@ void Key_Event( int key, int down ) } VGui_KeyEvent( key, down ); + Touch_KeyEvent( key, down ); // console key is hardcoded, so the user can never unbind it if( key == '`' || key == '~' ) diff --git a/engine/client/vgui/vgui_draw.c b/engine/client/vgui/vgui_draw.c index 9617044b..4efd295f 100644 --- a/engine/client/vgui/vgui_draw.c +++ b/engine/client/vgui/vgui_draw.c @@ -23,6 +23,7 @@ GNU General Public License for more details. #include "library.h" #include "keydefs.h" #include "ref_common.h" +#include "input.h" #ifdef XASH_SDL #include static SDL_Cursor* s_pDefaultCursor[20]; @@ -108,6 +109,10 @@ void GAME_EXPORT VGUI_CursorSelect(enum VGUI_DefaultCursor cursor ) } #ifdef XASH_SDL + /// TODO: platform cursors + + if( CVAR_TO_BOOL( touch_emulate ) ) + return; if( host.mouse_visible ) { SDL_SetRelativeMouseMode( SDL_FALSE ); diff --git a/engine/platform/platform.h b/engine/platform/platform.h index 8767fe70..c5c15b12 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -68,6 +68,7 @@ void Platform_RunEvents( void ); // Mouse void Platform_GetMousePos( int *x, int *y ); void Platform_SetMousePos( int x, int y ); +void Platform_PreCreateMove( void ); // Clipboard void Platform_GetClipboardText( char *buffer, size_t size ); void Platform_SetClipboardText( const char *buffer, size_t size ); diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 518ed123..09e0a9d5 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -157,7 +157,12 @@ SDLash_MouseEvent static void SDLash_MouseEvent( SDL_MouseButtonEvent button ) { int down = button.state != SDL_RELEASED; - if( in_mouseinitialized && !m_ignore->value && button.which != SDL_TOUCH_MOUSEID ) + + if( CVAR_TO_BOOL( touch_emulate ) ) + { + Touch_KeyEvent( K_MOUSE1 - 1 + button.button, down ); + } + else if( in_mouseinitialized && !m_ignore->value && button.which != SDL_TOUCH_MOUSEID ) { Key_Event( K_MOUSE1 - 1 + button.button, down ); } @@ -210,27 +215,12 @@ static void SDLash_EventFilter( SDL_Event *event ) case SDL_MOUSEMOTION: if( !host.mouse_visible && event->motion.which != SDL_TOUCH_MOUSEID ) IN_MouseEvent(); -#ifdef TOUCHEMU - if( mdown ) - IN_TouchEvent( event_motion, 0, - event->motion.x/(float)refState.width, - event->motion.y/(float)refState.height, - event->motion.xrel/(float)refState.width, - event->motion.yrel/(float)refState.height ); - SDL_ShowCursor( true ); -#endif break; case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONDOWN: -#ifdef TOUCHEMU - mdown = event->button.state != SDL_RELEASED; - IN_TouchEvent( event_down, 0, - event->button.x/(float)refState.width, - event->button.y/(float)refState.height, 0, 0); -#else + SDLash_MouseEvent( event->button ); -#endif break; case SDL_MOUSEWHEEL: @@ -294,7 +284,7 @@ static void SDLash_EventFilter( SDL_Event *event ) dy /= (float)refState.height; } - // IN_TouchEvent( type, event->tfinger.fingerId, x, y, dx, dy ); + IN_TouchEvent( type, event->tfinger.fingerId, x, y, dx, dy ); break; } @@ -469,4 +459,21 @@ void* Platform_GetNativeObject( const char *name ) return NULL; // SDL don't have it } +/* +======================== +Platform_PreCreateMove + +this should disable mouse look on client when m_ignore enabled +TODO: kill mouse in win32 clients too +======================== +*/ +void Platform_PreCreateMove( void ) +{ + if( CVAR_TO_BOOL( m_ignore ) ) + { + SDL_GetRelativeMouseState( NULL, NULL ); + SDL_ShowCursor( SDL_TRUE ); + } +} + #endif // defined( XASH_SDL ) && !defined( XASH_DEDICATED )