Files
sbox-public/engine/Sandbox.Engine/Systems/Input/InputRouter.Input.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

371 lines
9.4 KiB
C#

using NativeEngine;
namespace Sandbox.Engine;
internal static partial class InputRouter
{
static RealTimeSince timeSinceWindowActive;
internal static void OnMouseButton( ButtonCode button, bool down, int ikeymods )
{
SetButtonState( button, down );
var mouse = Contexts.FirstOrDefault( x => x.MouseState != InputContext.InputState.Ignore );
// if this was likely the click that made the window active - and we're not in UI mode
// then ignore it.. because we don't want people shooting guns every time they re-activate
// the window
if ( down && timeSinceWindowActive < 0.1f )
{
if ( mouse is null || mouse.MouseState != InputContext.InputState.UI )
return;
}
if ( mouse is not null )
{
mouse.IN_Button( down, button, false, EngineToModifier( ikeymods ) );
}
//
// When a button is released, we send it to each context. These contexts may have
// the button as pressed. The release should be sent to them so they can clear it.
//
if ( !down )
{
foreach ( var context in Contexts )
{
if ( context == mouse ) continue;
context.IN_ButtonReleased( button, EngineToModifier( ikeymods ) );
}
}
}
internal static void OnWindowActive( bool active )
{
if ( active )
{
timeSinceWindowActive = 0;
}
else
{
//
// Reset state and do button release events
//
PressedButtons.Clear();
foreach ( var context in Contexts )
{
context.ReleaseAllButtons();
}
}
}
/// <summary>
/// Cursor is hidden and restricted to window (game mode) but the mouse has been moved
/// </summary>
internal static void OnMouseMotion( float dx, float dy )
{
var delta = new Vector2( dx, dy );
MouseCursorDelta += delta;
MouseCursorPosition += delta;
// game or mouse capture
var mouse = Contexts.FirstOrDefault( x => x.MouseState != InputContext.InputState.Ignore );
if ( mouse is not null )
{
mouse.In_MousePosition( MouseCursorPosition, delta );
return;
}
}
/// <summary>
/// Cursor is visible and mouse is being moved
/// </summary>
internal static void OnMousePositionChange( float x, float y, float dx, float dy )
{
var delta = new Vector2( 0, 0 );
// if we're not in relative mode - take the delta from this
if ( !NativeEngine.InputSystem.GetRelativeMouseMode() )
{
delta = new Vector2( dx, dy );
MouseCursorDelta += delta;
}
MouseCursorPosition = new Vector2( x, y );
// if this is set, we're in capture mode - so just update the position
// which will update the position of the cursor when we come out of it
if ( mouseCapturePosition is not null )
{
mouseCapturePosition = MouseCursorPosition;
return;
}
var mouse = Contexts.FirstOrDefault( x => x.MouseState != InputContext.InputState.Ignore );
if ( mouse is not null )
{
mouse.In_MousePosition( MouseCursorPosition, delta );
}
}
internal static void OnGameControllerButton( int deviceId, GameControllerCode button, bool down )
{
if ( button == GameControllerCode.Start )
{
OnEscapePressed( down );
return;
}
OnGamepadCode( deviceId, button.ToGamepadCode(), down );
}
/// <summary>
/// The escape, or start button has been pressed
/// </summary>
private static void OnEscapePressed( bool down )
{
EscapeIsDown = down;
if ( down )
{
EscapeWasPressed = true;
TimeSinceEscapePressed = 0;
// Any focused UI? Let that swallow the escape button. This is for things like escaping from text areas
var escapeTarget = Contexts.FirstOrDefault( x => x.KeyboardState != InputContext.InputState.Ignore && x.KeyboardFocusPanel is not null );
if ( escapeTarget is not null && escapeTarget.In_Escape() )
{
return;
}
// Let the game input get first dibs
if ( IGameInstance.Current is not null && IGameInstanceDll.Current.InputContext.In_Escape() )
{
return;
}
}
}
private static void OnGamepadCode( int deviceId, GamepadCode code, bool down )
{
var controller = Controller.All.FirstOrDefault( x => x.DeviceId == deviceId );
Log.Trace( $"OnGameControllerButton {controller} - {code}, {down}" );
Sandbox.Input.UsingController = true;
SetButtonState( code, down );
foreach ( var action in Sandbox.Input.InputActions.Where( x => x.GamepadCode != GamepadCode.None && x.GamepadCode == code ) )
{
var i = Sandbox.Input.GetActionIndex( action );
foreach ( var e in Sandbox.Input.Contexts )
{
if ( down )
{
e.AccumActionsPressed |= 1UL << i;
}
else
{
e.AccumActionsReleased |= 1UL << i;
}
}
}
}
internal static void OnGameControllerAxis( int deviceId, GameControllerAxis axis, int value )
{
var controller = Controller.All.FirstOrDefault( x => x.DeviceId == deviceId );
Log.Trace( $"OnGameControllerButton {controller} - {axis}, {value}" );
if ( controller is null )
return;
controller.SetAxis( axis, value );
//// We're going to check our triggers and convert them into a virtual button, for ease of use
//// (and backwards compatibility with SteamInput)
//// We don't care about non triggers
if ( axis < GameControllerAxis.TriggerLeft )
return;
const float triggerDeadzone = 0.75f;
// I hate this but okay
GamepadCode code = axis switch
{
GameControllerAxis.TriggerLeft => GamepadCode.LeftTrigger,
GameControllerAxis.TriggerRight => GamepadCode.RightTrigger,
_ => GamepadCode.None,
};
OnGamepadCode( deviceId, code, value >= triggerDeadzone );
}
internal static void OnGameControllerConnected( int joystickId, int deviceId )
{
var controller = new Controller( joystickId, deviceId );
Log.Info( $"New {controller} controller detected" );
Controller.All.Add( controller );
}
internal static void OnGameControllerDisconnected( int joystickId )
{
var controller = Controller.All.FirstOrDefault( x => x.SDLHandle == joystickId );
if ( controller is not null )
{
Log.Info( $"{controller} controller removed" );
Controller.All.Remove( controller );
}
else
{
Log.Warning( $"Couldn't find Controller instance with {joystickId}" );
}
}
internal static void OnKey( ButtonCode button, bool down, bool repeat, int ikeymods, int vkcode )
{
if ( !repeat )
{
SetButtonState( button, down );
}
if ( button == ButtonCode.KEY_ESCAPE )
{
if ( repeat )
return;
OnEscapePressed( down );
return;
}
//
// Function keys
//
if ( button >= ButtonCode.KEY_F1 && button <= ButtonCode.KEY_F12 )
{
if ( !down || repeat ) return;
IToolsDll.Current?.OnFunctionKey( button, EngineToModifier( ikeymods ) );
var bind = g_pInputService.GetBinding( button );
if ( string.IsNullOrEmpty( bind ) ) return;
ConVarSystem.Run( bind );
return;
}
//
// Console
//
if ( button == ButtonCode.KEY_BACKQUOTE || button == ButtonCode.KEY_TILDE )
{
if ( !down || repeat ) return;
if ( Engine.IToolsDll.Current?.ConsoleFocus() ?? false )
return;
ConVarSystem.Run( "con_toggle" );
return;
}
var keyboard = Contexts.FirstOrDefault( x => x.KeyboardState != InputContext.InputState.Ignore );
if ( keyboard is not null )
{
keyboard.IN_Button( down, button, repeat, EngineToModifier( ikeymods ) );
}
//
// When a button is released, we send it to each context. These contexts may have
// the button as pressed. The release should be sent to them so they can clear it.
//
if ( !down )
{
foreach ( var context in Contexts )
{
if ( context == keyboard ) continue;
context.IN_ButtonReleased( button, EngineToModifier( ikeymods ) );
}
}
}
internal static void OnText( uint key )
{
var keyboard = Contexts.FirstOrDefault( x => x.KeyboardState == InputContext.InputState.UI );
if ( keyboard is not null )
{
keyboard.IN_Text( (char)key );
}
}
internal static void OnMouseWheel( int x, int y, int ikeymods )
{
var value = new Vector2( x, y );
var mouse = Contexts.FirstOrDefault( x => x.MouseState != InputContext.InputState.Ignore );
if ( mouse is not null )
{
if ( y < 0 )
{
mouse.IN_Button( true, ButtonCode.MouseWheelDown, false, default );
mouse.IN_Button( false, ButtonCode.MouseWheelDown, false, default );
}
else
{
mouse.IN_Button( true, ButtonCode.MouseWheelUp, false, default );
mouse.IN_Button( false, ButtonCode.MouseWheelUp, false, default );
}
mouse.IN_MouseWheel( value, EngineToModifier( ikeymods ) );
}
}
internal static void OnImeStart()
{
var keyboard = Contexts.FirstOrDefault( x => x.KeyboardState != InputContext.InputState.Ignore );
if ( keyboard is not null )
{
keyboard.IN_ImeStart();
}
}
internal static void OnImeComposition( string text, bool final )
{
var keyboard = Contexts.FirstOrDefault( x => x.KeyboardState != InputContext.InputState.Ignore );
if ( keyboard is not null )
{
keyboard.IN_ImeComposition( text, final );
}
}
internal static void OnImeEnd()
{
var keyboard = Contexts.FirstOrDefault( x => x.KeyboardState != InputContext.InputState.Ignore );
if ( keyboard is not null )
{
keyboard.IN_ImeEnd();
}
}
/// <summary>
/// Convert engine (IE_ShiftPressed etc) to our KeyboardModifiers enum
/// </summary>
static KeyboardModifiers EngineToModifier( int engine )
{
KeyboardModifiers m = KeyboardModifiers.None;
if ( (engine & 1) == 1 ) m |= KeyboardModifiers.Shift;
if ( (engine & 2) == 2 ) m |= KeyboardModifiers.Ctrl;
if ( (engine & 4) == 4 ) m |= KeyboardModifiers.Alt;
//if ( (m_nData2 & 8) == 8 ) m |= KeyboardModifiers.Windows;
//if ( (m_nData2 & 16) == 8 ) m |= KeyboardModifiers.Finger;
return m;
}
}