Xash3D FWGS engine.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

631 lines
12 KiB

/*
input.c - win32 input devices
Copyright (C) 2007 Uncle Mike
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"
#include "vgui_draw.h"
#if XASH_SDL
#include <SDL.h>
#endif
#include "platform/platform.h"
void* in_mousecursor;
qboolean in_mouseactive; // false when not focus app
qboolean in_mouseinitialized;
qboolean in_mouse_suspended;
POINT in_lastvalidpos;
qboolean in_mouse_savedpos;
static int in_mstate = 0;
static struct inputstate_s
{
float lastpitch, lastyaw;
} inputstate;
CVAR_DEFINE_AUTO( m_pitch, "0.022", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "mouse pitch value" );
CVAR_DEFINE_AUTO( m_yaw, "0.022", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "mouse yaw value" );
CVAR_DEFINE_AUTO( m_ignore, DEFAULT_M_IGNORE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "ignore mouse events" );
static CVAR_DEFINE_AUTO( look_filter, "0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "filter look events making it smoother" );
static CVAR_DEFINE_AUTO( m_rawinput, "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable mouse raw input" );
static CVAR_DEFINE_AUTO( cl_forwardspeed, "400", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_FILTERABLE, "Default forward move speed" );
static CVAR_DEFINE_AUTO( cl_backspeed, "400", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_FILTERABLE, "Default back move speed" );
static CVAR_DEFINE_AUTO( cl_sidespeed, "400", FCVAR_ARCHIVE | FCVAR_CLIENTDLL | FCVAR_FILTERABLE, "Default side move speed" );
/*
================
IN_CollectInputDevices
Returns a bit mask representing connected devices or, at least, enabled
================
*/
uint IN_CollectInputDevices( void )
{
uint ret = 0;
if( !m_ignore.value ) // no way to check is mouse connected, so use cvar only
ret |= INPUT_DEVICE_MOUSE;
if( touch_enable.value )
ret |= INPUT_DEVICE_TOUCH;
if( Joy_IsActive() ) // connected or enabled
ret |= INPUT_DEVICE_JOYSTICK;
Con_Reportf( "Connected devices: %s%s%s%s\n",
FBitSet( ret, INPUT_DEVICE_MOUSE ) ? "mouse " : "",
FBitSet( ret, INPUT_DEVICE_TOUCH ) ? "touch " : "",
FBitSet( ret, INPUT_DEVICE_JOYSTICK ) ? "joy " : "",
FBitSet( ret, INPUT_DEVICE_VR ) ? "vr " : "");
return ret;
}
/*
=================
IN_LockInputDevices
tries to lock any possibilty to connect another input device after
player is connected to the server
=================
*/
void IN_LockInputDevices( qboolean lock )
{
extern convar_t joy_enable; // private to input system
if( lock )
{
SetBits( m_ignore.flags, FCVAR_READ_ONLY );
SetBits( joy_enable.flags, FCVAR_READ_ONLY );
SetBits( touch_enable.flags, FCVAR_READ_ONLY );
}
else
{
ClearBits( m_ignore.flags, FCVAR_READ_ONLY );
ClearBits( joy_enable.flags, FCVAR_READ_ONLY );
ClearBits( touch_enable.flags, FCVAR_READ_ONLY );
}
}
/*
===========
IN_StartupMouse
===========
*/
static void IN_StartupMouse( void )
{
Cvar_RegisterVariable( &m_ignore );
Cvar_RegisterVariable( &m_pitch );
Cvar_RegisterVariable( &m_yaw );
Cvar_RegisterVariable( &look_filter );
Cvar_RegisterVariable( &m_rawinput );
// You can use -nomouse argument to prevent using mouse from client
// -noenginemouse will disable all mouse input
if( Sys_CheckParm( "-noenginemouse" )) return;
in_mouseinitialized = true;
}
void GAME_EXPORT IN_SetCursor( void *hCursor )
{
// stub
}
/*
===========
IN_MouseSavePos
Save mouse pos before state change e.g. changelevel
===========
*/
void IN_MouseSavePos( void )
{
if( !in_mouseactive )
return;
Platform_GetMousePos( &in_lastvalidpos.x, &in_lastvalidpos.y );
in_mouse_savedpos = true;
}
/*
===========
IN_MouseRestorePos
Restore right position for background
===========
*/
void IN_MouseRestorePos( void )
{
if( !in_mouse_savedpos )
return;
Platform_SetMousePos( in_lastvalidpos.x, in_lastvalidpos.y );
in_mouse_savedpos = false;
}
/*
===========
IN_ToggleClientMouse
Called when key_dest is changed
===========
*/
void IN_ToggleClientMouse( int newstate, int oldstate )
{
if( newstate == oldstate )
return;
// since SetCursorType controls cursor visibility
// execute it first, and then check mouse grab state
if( newstate == key_menu || newstate == key_console )
{
Platform_SetCursorType( dc_arrow );
#if XASH_USE_EVDEV
Evdev_SetGrab( false );
#endif
}
else
{
Platform_SetCursorType( dc_none );
#if XASH_USE_EVDEV
Evdev_SetGrab( true );
#endif
}
// don't leave the user without cursor if they enabled m_ignore
if( m_ignore.value )
return;
if( oldstate == key_game )
{
IN_DeactivateMouse();
}
else if( newstate == key_game )
{
IN_ActivateMouse();
}
}
static void IN_CheckMouseState( qboolean active )
{
static qboolean s_bRawInput, s_bMouseGrab;
#if XASH_WIN32
qboolean useRawInput = ( m_rawinput.value && clgame.client_dll_uses_sdl ) || clgame.dllFuncs.pfnLookEvent != NULL;
#else
qboolean useRawInput = true; // always use SDL code
#endif
if( m_ignore.value )
return;
if( active && useRawInput && !host.mouse_visible && cls.state == ca_active )
{
if( !s_bRawInput )
{
#if XASH_SDL == 2
SDL_GetRelativeMouseState( NULL, NULL );
SDL_SetRelativeMouseMode( SDL_TRUE );
#endif
// Con_Printf( "Enable relative mode\n" );
s_bRawInput = true;
}
}
else
{
if( s_bRawInput )
{
#if XASH_SDL == 2
SDL_GetRelativeMouseState( NULL, NULL );
SDL_SetRelativeMouseMode( SDL_FALSE );
#endif
// Con_Printf( "Disable relative mode\n" );
s_bRawInput = false;
}
}
if( active && !host.mouse_visible && cls.state == ca_active )
{
if( !s_bMouseGrab )
{
#if XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_TRUE );
#endif
// Con_Printf( "Enable grab\n" );
s_bMouseGrab = true;
}
}
else
{
if( s_bMouseGrab )
{
#if XASH_SDL
SDL_SetWindowGrab( host.hWnd, SDL_FALSE );
#endif
// Con_Printf( "Disable grab\n" );
s_bMouseGrab = false;
}
}
}
/*
===========
IN_ActivateMouse
Called when the window gains focus or changes in some way
===========
*/
void IN_ActivateMouse( void )
{
if( !in_mouseinitialized )
return;
IN_CheckMouseState( true );
if( clgame.dllFuncs.IN_ActivateMouse )
clgame.dllFuncs.IN_ActivateMouse();
in_mouseactive = true;
}
/*
===========
IN_DeactivateMouse
Called when the window loses focus
===========
*/
void IN_DeactivateMouse( void )
{
if( !in_mouseinitialized )
return;
IN_CheckMouseState( false );
if( clgame.dllFuncs.IN_DeactivateMouse )
clgame.dllFuncs.IN_DeactivateMouse();
in_mouseactive = false;
}
/*
================
IN_MouseMove
================
*/
static void IN_MouseMove( void )
{
int x, y;
if( !in_mouseinitialized )
return;
if( touch_emulate.value )
{
// touch emulation overrides all input
Touch_KeyEvent( 0, 0 );
return;
}
// find mouse movement
Platform_GetMousePos( &x, &y );
VGui_MouseMove( x, y );
// if the menu is visible, move the menu cursor
UI_MouseMove( x, y );
}
/*
===========
IN_MouseEvent
===========
*/
void IN_MouseEvent( int key, int down )
{
if( !in_mouseinitialized )
return;
if( down )
SetBits( in_mstate, BIT( key ));
else ClearBits( in_mstate, BIT( key ));
// touch emulation overrides all input
if( touch_emulate.value )
{
Touch_KeyEvent( K_MOUSE1 + key, down );
}
else if( cls.key_dest == key_game )
{
// perform button actions
VGui_MouseEvent( K_MOUSE1 + key, down );
// don't do Key_Event here
// client may override IN_MouseEvent
// but by default it calls back to Key_Event anyway
if( in_mouseactive )
clgame.dllFuncs.IN_MouseEvent( in_mstate );
}
else
{
// perform button actions
Key_Event( K_MOUSE1 + key, down );
}
}
/*
==============
IN_MWheelEvent
direction is negative for wheel down, otherwise wheel up
==============
*/
void IN_MWheelEvent( int y )
{
int b = y > 0 ? K_MWHEELUP : K_MWHEELDOWN;
VGui_MWheelEvent( y );
Key_Event( b, true );
Key_Event( b, false );
}
/*
===========
IN_Shutdown
===========
*/
void IN_Shutdown( void )
{
IN_DeactivateMouse( );
#if XASH_USE_EVDEV
Evdev_Shutdown();
#endif
Touch_Shutdown();
}
/*
===========
IN_Init
===========
*/
void IN_Init( void )
{
Cvar_RegisterVariable( &cl_forwardspeed );
Cvar_RegisterVariable( &cl_backspeed );
Cvar_RegisterVariable( &cl_sidespeed );
if( !Host_IsDedicated() )
{
IN_StartupMouse( );
Joy_Init(); // common joystick support init
Touch_Init();
#if XASH_USE_EVDEV
Evdev_Init();
#endif
}
}
/*
================
IN_JoyMove
Common function for engine joystick movement
-1 < forwardmove < 1, -1 < sidemove < 1
================
*/
#define F (1U << 0) // Forward
#define B (1U << 1) // Back
#define L (1U << 2) // Left
#define R (1U << 3) // Right
#define T (1U << 4) // Forward stop
#define S (1U << 5) // Side stop
static void IN_JoyAppendMove( usercmd_t *cmd, float forwardmove, float sidemove )
{
static uint moveflags = T | S;
if( forwardmove ) cmd->forwardmove = forwardmove * cl_forwardspeed.value;
if( sidemove ) cmd->sidemove = sidemove * cl_sidespeed.value;
if( forwardmove )
{
moveflags &= ~T;
}
else if( !( moveflags & T ) )
{
Cmd_ExecuteString( "-back" );
Cmd_ExecuteString( "-forward" );
moveflags |= T;
}
if( sidemove )
{
moveflags &= ~S;
}
else if( !( moveflags & S ) )
{
Cmd_ExecuteString( "-moveleft" );
Cmd_ExecuteString( "-moveright" );
moveflags |= S;
}
if ( forwardmove > 0.7f && !( moveflags & F ))
{
moveflags |= F;
Cmd_ExecuteString( "+forward" );
}
else if ( forwardmove < 0.7f && ( moveflags & F ))
{
moveflags &= ~F;
Cmd_ExecuteString( "-forward" );
}
if ( forwardmove < -0.7f && !( moveflags & B ))
{
moveflags |= B;
Cmd_ExecuteString( "+back" );
}
else if ( forwardmove > -0.7f && ( moveflags & B ))
{
moveflags &= ~B;
Cmd_ExecuteString( "-back" );
}
if ( sidemove > 0.9f && !( moveflags & R ))
{
moveflags |= R;
Cmd_ExecuteString( "+moveright" );
}
else if ( sidemove < 0.9f && ( moveflags & R ))
{
moveflags &= ~R;
Cmd_ExecuteString( "-moveright" );
}
if ( sidemove < -0.9f && !( moveflags & L ))
{
moveflags |= L;
Cmd_ExecuteString( "+moveleft" );
}
else if ( sidemove > -0.9f && ( moveflags & L ))
{
moveflags &= ~L;
Cmd_ExecuteString( "-moveleft" );
}
}
static void IN_CollectInput( float *forward, float *side, float *pitch, float *yaw, qboolean includeMouse )
{
if( includeMouse )
{
float x, y;
Platform_MouseMove( &x, &y );
*pitch += y * m_pitch.value;
*yaw -= x * m_yaw.value;
#if XASH_USE_EVDEV
IN_EvdevMove( yaw, pitch );
#endif
}
Joy_FinalizeMove( forward, side, yaw, pitch );
Touch_GetMove( forward, side, yaw, pitch );
if( look_filter.value )
{
*pitch = ( inputstate.lastpitch + *pitch ) / 2;
*yaw = ( inputstate.lastyaw + *yaw ) / 2;
inputstate.lastpitch = *pitch;
inputstate.lastyaw = *yaw;
}
}
/*
================
IN_EngineAppendMove
Called from cl_main.c after generating command in client
================
*/
void IN_EngineAppendMove( float frametime, void *cmd1, qboolean active )
{
float forward, side, pitch, yaw;
usercmd_t *cmd = cmd1;
if( clgame.dllFuncs.pfnLookEvent )
return;
if( cls.key_dest != key_game || cl.paused || cl.intermission )
return;
forward = side = pitch = yaw = 0;
if( active )
{
float sensitivity = 1;//( (float)cl.local.scr_fov / (float)90.0f );
IN_CollectInput( &forward, &side, &pitch, &yaw, false );
IN_JoyAppendMove( cmd, forward, side );
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 );
}
}
}
static void IN_Commands( void )
{
#if XASH_USE_EVDEV
IN_EvdevFrame();
#endif
if( clgame.dllFuncs.pfnLookEvent )
{
float forward = 0, side = 0, pitch = 0, yaw = 0;
IN_CollectInput( &forward, &side, &pitch, &yaw, in_mouseinitialized && !m_ignore.value );
if( cls.key_dest == key_game )
{
clgame.dllFuncs.pfnLookEvent( yaw, pitch );
clgame.dllFuncs.pfnMoveEvent( forward, side );
}
}
if( !in_mouseinitialized )
return;
IN_CheckMouseState( in_mouseactive );
}
/*
==================
Host_InputFrame
Called every frame, even if not generating commands
==================
*/
void Host_InputFrame( void )
{
Sys_SendKeyEvents ();
IN_Commands();
IN_MouseMove();
}