summaryrefslogtreecommitdiff
path: root/inputsystem/steamcontroller.cpp
blob: 007ccec8ad66313ec5d4f892b5ca4e0b73e7de54 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
//=========== Copyright Valve Corporation, All rights reserved. ===============//
//
// Purpose: Native Steam Controller Interface
//=============================================================================//
#include "inputsystem.h"
#include "key_translation.h"
#include "filesystem.h"
#include "steam/isteamcontroller.h"
#include "math.h"

#ifndef UINT64_MAX
const uint64 UINT64_MAX = 0xffffffffffffffff;
#endif

#if !defined( NO_STEAM )
#include "steam/steam_api.h"
#endif //NO_STEAM

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

ConVar sc_joystick_map( "sc_joystick_map", "1", FCVAR_ARCHIVE, "How to map the analog joystick deadzone and extents 0 = Scaled Cross, 1 = Concentric Mapping to Square." );

#define STEAMPAD_MAX_ANALOGSAMPLE_GYRO		32768
#define STEAMPAD_MAX_ANALOGSAMPLE_TRIGGER	32768
#define STEAMPAD_MAX_ANALOGSAMPLE_LEFT		32768
#define STEAMPAD_MAX_ANALOGSAMPLE_RIGHT		32767
#define STEAMPAD_MAX_ANALOGSAMPLE_DOWN		32768
#define STEAMPAD_MAX_ANALOGSAMPLE_UP		32767
#define STEAMPAD_MAX_ANALOGSAMPLE_MAX		32768
#define STEAMPAD_ANALOG_SCALE_LEFT(x) 		( ( float )STEAMPAD_MAX_ANALOGSAMPLE_LEFT/( float )( STEAMPAD_MAX_ANALOGSAMPLE_LEFT-(x) ) )
#define STEAMPAD_ANALOG_SCALE_RIGHT(x) 		( ( float )STEAMPAD_MAX_ANALOGSAMPLE_RIGHT/( float )( STEAMPAD_MAX_ANALOGSAMPLE_RIGHT-(x) ) )
#define STEAMPAD_ANALOG_SCALE_DOWN(x) 		( ( float )STEAMPAD_MAX_ANALOGSAMPLE_DOWN/( float )( STEAMPAD_MAX_ANALOGSAMPLE_DOWN-(x) ) )
#define STEAMPAD_ANALOG_SCALE_UP(x)	 		( ( float )STEAMPAD_MAX_ANALOGSAMPLE_UP/( float )( STEAMPAD_MAX_ANALOGSAMPLE_UP-(x) ) )


#define STEAMPAD_ANALOG_TRIGGER_THRESHOLD	( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_TRIGGER * 0.5f ) )
#define STEAMPAD_ANALOG_PAD_THRESHOLD		( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.25f ) )
#define STEAMPAD_ANALOG_GYRO_THRESHOLD		( ( int ) ( STEAMPAD_MAX_ANALOGSAMPLE_GYRO * 0.3f ) )
#define STEAMPAD_DIGITAL_PAD_THRESHOLD		( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.52f ) )

#define STEAMPAD_AXIS_REPEAT_INTERVAL_START	0.6f
#define STEAMPAD_AXIS_REPEAT_INTERVAL_END	0.05f
#define STEAMPAD_AXIS_REPEAT_CURVE_TIME		1.75f

#define PAD_ANALOG_BUTTON_THRESHOLD			( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.285f ) )
#define PAD_ANALOG_BUTTON_THRESHOLD_STRONG	( ( int )( STEAMPAD_MAX_ANALOGSAMPLE_MAX * 0.68f ) )

#define SQRT2 1.414213562

// key translation
typedef struct
{
	uint64			steampadinput;
	ButtonCode_t	steampadkey;
} steampadInputTosteampadKey_t;

static const int s_nSteamPadDeadZoneTable[] =
{
	STEAMPAD_ANALOG_PAD_THRESHOLD,		// LEFTPAD_AXIS_X
	STEAMPAD_ANALOG_PAD_THRESHOLD,		// LEFTPAD_AXIS_Y
	STEAMPAD_ANALOG_PAD_THRESHOLD,		// RIGHTPAD_AXIS_X
	STEAMPAD_ANALOG_PAD_THRESHOLD,		// RIGHTPAD_AXIS_Y
	STEAMPAD_ANALOG_TRIGGER_THRESHOLD,  //LEFT_TRIGGER_AXIS
	STEAMPAD_ANALOG_TRIGGER_THRESHOLD,  //RIGHT_TRIGGER_AXIS
	STEAMPAD_ANALOG_GYRO_THRESHOLD,		//GYRO_AXIS_PITCH
	STEAMPAD_ANALOG_GYRO_THRESHOLD,		//GYRO_AXIS_ROLL
	STEAMPAD_ANALOG_GYRO_THRESHOLD,		//GYRO_AXIS_YAW
};

//-----------------------------------------------------------------------------
// Purpose: Counts the number of active gamepads connected
//-----------------------------------------------------------------------------
uint32 CInputSystem::GetNumSteamControllersConnected()
{
	return m_unNumConnected;
}

struct SDigitalMenuAction
{
	const char *strName;
	ButtonCode_t buttonCode;
	ControllerDigitalActionHandle_t handle;
	bool bState[STEAM_CONTROLLER_MAX_COUNT];
	bool bAwaitingDebounce[STEAM_CONTROLLER_MAX_COUNT];
};

static SDigitalMenuAction g_DigitalMenuActions[] = {
	{ "menu_left", STEAMCONTROLLER_DPAD_LEFT, 0, { false }, { false } },
	{ "menu_right", STEAMCONTROLLER_DPAD_RIGHT, 0, { false }, { false } },
	{ "menu_up", STEAMCONTROLLER_DPAD_UP, 0, { false }, { false } },
	{ "menu_down", STEAMCONTROLLER_DPAD_DOWN, 0, { false }, { false } },
	{ "menu_cancel", STEAMCONTROLLER_B, 0, { false }, { false } },
	{ "menu_select", STEAMCONTROLLER_A, 0, { false }, { false } },
	{ "resume_esc", STEAMCONTROLLER_START, 0, { false }, { false } },
	{ "cl_trigger_first_notification", STEAMCONTROLLER_F1, 0, { false }, { false } },		// Command is in the in-game action set (use for hitting accept/ok on dialogs)
	{ "cl_decline_first_notification", STEAMCONTROLLER_F2, 0, { false }, { false } },		// Command is in the in-game action set (use for hitting cancel/decline on dialogs)
	{ "menu_toggle_function", STEAMCONTROLLER_Y, 0, { false }, { false }, },				// Command is in the in-game HUD action set
	{ "menu_alt_function", STEAMCONTROLLER_X, 0, { false }, { false } },					// Command is in the in-game HUD action set
	{ "cancelselect", STEAMCONTROLLER_START, 0, { false }, { false } },						// Command is in the in-game action set
	{ "toggleready", STEAMCONTROLLER_F4, 0, { false }, { false } },							// Command is in the in-game action set
};

struct SAnalogAction
{
	const char *strName;
	JoystickAxis_t joystickAxisX, joystickAxisY;
	ControllerAnalogActionHandle_t handle;
};

struct SGameActionSet
{
	const char *strName;
	GameActionSet_t eGameActionSet;
	ControllerActionSetHandle_t handle;
};
static SGameActionSet g_GameActionSets[] = {
	{ "MenuControls", GAME_ACTION_SET_MENUCONTROLS, 0 },
	{ "FPSControls", GAME_ACTION_SET_FPSCONTROLS, 0 },
	{ "InGameHUDControls", GAME_ACTION_SET_IN_GAME_HUD, 0 },
	{ "SpectatorControls", GAME_ACTION_SET_SPECTATOR, 0 },
};


// Table that maps a physical steam controller origin to the corresponding character in our icon font.
// If the EControllerActionOrigin enum or the font changes, this table must be changed to match.
static const wchar_t* g_MapSteamControllerOriginToIconFont[] = {
	L"",					// k_EControllerActionOrigin_None 
	L"A",					// k_EControllerActionOrigin_A
	L"B",					// k_EControllerActionOrigin_B
	L"X",					// k_EControllerActionOrigin_X
	L"Y",					// k_EControllerActionOrigin_Y
	L"2",					// k_EControllerActionOrigin_LeftBumper
	L"3",					// k_EControllerActionOrigin_RightBumper
	L"(",					// k_EControllerActionOrigin_LeftGrip
	L")",					// k_EControllerActionOrigin_RightGrip
	L"5",					// k_EControllerActionOrigin_Start
	L"4",					// k_EControllerActionOrigin_Back
	L"q",					// k_EControllerActionOrigin_LeftPad_Touch
	L"w",					// k_EControllerActionOrigin_LeftPad_Swipe
	L"e",					// k_EControllerActionOrigin_LeftPad_Click
	L"a",					// k_EControllerActionOrigin_LeftPad_DPadNorth
	L"s",					// k_EControllerActionOrigin_LeftPad_DPadSouth
	L"d",					// k_EControllerActionOrigin_LeftPad_DPadWest
	L"f",					// k_EControllerActionOrigin_LeftPad_DPadEast					
	L"y",					// k_EControllerActionOrigin_RightPad_Touch
	L"u",					// k_EControllerActionOrigin_RightPad_Swipe
	L"i",					// k_EControllerActionOrigin_RightPad_Click
	L"h",					// k_EControllerActionOrigin_RightPad_DPadNorth
	L"j",					// k_EControllerActionOrigin_RightPad_DPadSouth
	L"k",					// k_EControllerActionOrigin_RightPad_DPadWest
	L"l",					// k_EControllerActionOrigin_RightPad_DEast
	L"z",					// k_EControllerActionOrigin_LeftTrigger_Pull
	L"x",					// k_EControllerActionOrigin_LeftTrigger_Click
	L"n",					// k_EControllerActionOrigin_RightTrigger_Pull
	L"m",					// k_EControllerActionOrigin_RightTrigger_Click
	L"C",					// k_EControllerActionOrigin_LeftStick_Move
	L"V",					// k_EControllerActionOrigin_LeftStick_Click
	L"7",					// k_EControllerActionOrigin_LeftStick_DPadNorth
	L"8",					// k_EControllerActionOrigin_LeftStick_DPadSouth
	L"9",					// k_EControllerActionOrigin_LeftStick_DPadWest
	L"0",					// k_EControllerActionOrigin_LeftStick_DPadEast
	L"6",					// k_EControllerActionOrigin_Gyro_Move
	L"6",					// k_EControllerActionOrigin_Gyro_Pitch
	L"6",					// k_EControllerActionOrigin_Gyro_Yaw
	L"6",					// k_EControllerActionOrigin_Gyro_Roll
};

// Table that maps a physical steam controller origin to a short description string for user display (only use this
// if it's impractical to use the icon font).
// If the EControllerActionOrigin enum changes, this table must be changed to match.
static const wchar_t* g_MapSteamControllerOriginToDescription[] = {
	L"",					// k_EControllerActionOrigin_None 
	L"A",					// k_EControllerActionOrigin_A
	L"B",					// k_EControllerActionOrigin_B
	L"X",					// k_EControllerActionOrigin_X
	L"Y",					// k_EControllerActionOrigin_Y
	L"LB",					// k_EControllerActionOrigin_LeftBumper
	L"RB",					// k_EControllerActionOrigin_RightBumper
	L"LG",					// k_EControllerActionOrigin_LeftGrip
	L"RG",					// k_EControllerActionOrigin_RightGrip
	L"START",				// k_EControllerActionOrigin_Start
	L"BACK",				// k_EControllerActionOrigin_Back
	L"LPTOUCH",				// k_EControllerActionOrigin_LeftPad_Touch
	L"LPSWIPE",				// k_EControllerActionOrigin_LeftPad_Swipe
	L"LPCLICK",				// k_EControllerActionOrigin_LeftPad_Click
	L"LPUP",				// k_EControllerActionOrigin_LeftPad_DPadNorth
	L"LPDOWN",				// k_EControllerActionOrigin_LeftPad_DPadSouth
	L"LPLEFT",				// k_EControllerActionOrigin_LeftPad_DPadWest
	L"LPRIGHT",				// k_EControllerActionOrigin_LeftPad_DPadEast					
	L"RPTOUCH",				// k_EControllerActionOrigin_RightPad_Touch
	L"RPSWIPE",				// k_EControllerActionOrigin_RightPad_Swipe
	L"RPCLICK",				// k_EControllerActionOrigin_RightPad_Click
	L"RPUP",				// k_EControllerActionOrigin_RightPad_DPadNorth
	L"RPDOWN",				// k_EControllerActionOrigin_RightPad_DPadSouth
	L"RPLEFT",				// k_EControllerActionOrigin_RightPad_DPadWest
	L"RPRIGHT",				// k_EControllerActionOrigin_RightPad_DEast
	L"LT",					// k_EControllerActionOrigin_LeftTrigger_Pull
	L"LT",					// k_EControllerActionOrigin_LeftTrigger_Click
	L"LT",					// k_EControllerActionOrigin_RightTrigger_Pull
	L"LT",					// k_EControllerActionOrigin_RightTrigger_Click
	L"LS",					// k_EControllerActionOrigin_LeftStick_Move
	L"LSCLICK",				// k_EControllerActionOrigin_LeftStick_Click
	L"LSUP",				// k_EControllerActionOrigin_LeftStick_DPadNorth
	L"LSDOWN",				// k_EControllerActionOrigin_LeftStick_DPadSouth
	L"LSLEFT",				// k_EControllerActionOrigin_LeftStick_DPadWest
	L"LSRIGHT",				// k_EControllerActionOrigin_LeftStick_DPadEast
	L"GYRO",				// k_EControllerActionOrigin_Gyro_Move
	L"GYRO",				// k_EControllerActionOrigin_Gyro_Pitch
	L"GYRO",				// k_EControllerActionOrigin_Gyro_Yaw
	L"GYRO",				// k_EControllerActionOrigin_Gyro_Roll
};


bool CInputSystem::InitializeSteamControllerActionSets()
{
	auto psteamcontroller = g_pInputSystem->SteamControllerInterface();
	if ( !psteamcontroller )
	{
		return false;
	}

	bool bGotHandles = true;

	for ( int i = 0; i != ARRAYSIZE( g_DigitalMenuActions ); ++i )
	{
		g_DigitalMenuActions[i].handle = psteamcontroller->GetDigitalActionHandle( g_DigitalMenuActions[i].strName );
		bGotHandles = bGotHandles && ( g_DigitalMenuActions[i].handle != 0 );

		// Init the button state array 
		for( int j = 0; j < STEAM_CONTROLLER_MAX_COUNT; j++ )
		{
			g_DigitalMenuActions[i].bState[j] = false;
		}
	}

	for ( int i = 0; i != ARRAYSIZE( g_GameActionSets ); ++i )
	{
		g_GameActionSets[i].handle = psteamcontroller->GetActionSetHandle( g_GameActionSets[i].strName );
		bGotHandles = bGotHandles &&  g_GameActionSets[i].handle;
	}

	return bGotHandles;
}

ConVar sc_debug_sets( "sc_debug_sets", "0", FCVAR_ARCHIVE, "Debugging" );

void CInputSystem::PollSteamControllers( void )
{
	// Make sure we've got the appropriate connections to Steam
	auto steamcontroller = SteamControllerInterface();
	if ( !steamcontroller )
	{
		return;
	}

	steamcontroller->RunFrame();

	uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
	m_unNumConnected = steamcontroller->GetConnectedControllers( nControllerHandles );

	if ( m_unNumConnected > 0 )
	{
		if ( !m_bSteamControllerActionsInitialized )
		{
			// Retry initialization of controller actions if we didn't acquire them all before for some reason.
			m_bSteamControllerActionsInitialized = m_bSteamController && InitializeSteamControllerActionSets();
			g_pInputSystem->ActivateSteamControllerActionSet( GAME_ACTION_SET_MENUCONTROLS );
		}

		if ( m_bSteamControllerActionsInitialized )
		{  
			// If we successfully initialized all the actions, and we have a connected controller, then we're considered "active".
			m_bSteamControllerActive = true;

			// For each digital action
			for ( int i = 0; i != ARRAYSIZE( g_DigitalMenuActions ); ++i )
			{
				SDigitalMenuAction& action = g_DigitalMenuActions[i];

				// and for each controller
				for ( uint64 j = 0; j < m_unNumConnected; ++j )
				{

					// Get the action's current state
					ControllerDigitalActionData_t data = steamcontroller->GetDigitalActionData( nControllerHandles[j], action.handle );

					// We only care if the action is active
					if ( data.bActive )
					{
						action.bAwaitingDebounce[j] = action.bAwaitingDebounce[j] && data.bState;

						// If the action's state has changed
						if ( data.bState != action.bState[j] )
						{
							action.bState[j] = data.bState;

							// Press the key for the correct controller
							ButtonCode_t buttonCode = ButtonCodeToJoystickButtonCode( action.buttonCode, j );

							if ( action.bState[j] )
							{
								if ( !action.bAwaitingDebounce[j] )
								{
									PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
								}
							}
							else
							{
								PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
							}
						}
					}
				}
			}
		}
	}
	else
	{
		// If no controllers are connected, unflag the active state.
		m_bSteamControllerActive = false;
	}
}

bool CInputSystem::GetRadialMenuStickValues( int nSlot, float &fX, float &fY )
{
	fX = m_pRadialMenuStickVal[nSlot][0];
	fY = m_pRadialMenuStickVal[nSlot][1];

	return true;
}

bool CInputSystem::IsSteamControllerActive( void )
{
	return m_bSteamControllerActive;
}

bool CInputSystem::InitializeSteamControllers()
{
	m_flLastSteamControllerInput = -FLT_MAX;
	auto steamcontroller = SteamControllerInterface();
	if ( steamcontroller )
	{
		if ( !steamcontroller->Init() )
		{
			return false;
		}
	}
	else
	{
		return false;
	}

	for( int i=0; i<STEAM_CONTROLLER_MAX_COUNT; i++ )
	{
		m_pRadialMenuStickVal[i][0] = 0.0f;
		m_pRadialMenuStickVal[i][1] = 0.0f;
	}
	
	// We have to account for other joysticks prior to adding steam controllers
	// So we get the baseline number here when first initializing
	m_nJoystickBaseline = m_nJoystickCount;
	if ( steamcontroller )
	{
		uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
		m_unNumConnected = steamcontroller->GetConnectedControllers( nControllerHandles );
		steamcontroller->RunFrame();

		if ( m_unNumConnected > 0 )
		{
			for ( uint32 i = 0; i < m_unNumConnected; i++ )
			{
				if ( m_Device[i].m_nJoystickIndex == INVALID_USER_ID )
				{
					int nJoystickIndex = i;
					m_Device[i].m_nJoystickIndex = nJoystickIndex;
					m_Device[i].m_nHardwareIndex = i;
				}
				m_nControllerType[m_Device[i].m_nJoystickIndex] = INPUT_TYPE_STEAMCONTROLLER;
			}
		}

		return true;
	}

	return false;	
}

ControllerActionSetHandle_t CInputSystem::GetActionSetHandle( GameActionSet_t eActionSet )
{
	return g_GameActionSets[eActionSet].handle;
}

ControllerActionSetHandle_t CInputSystem::GetActionSetHandle( const char* szActionSet )
{
	for ( int i = 0; i != ARRAYSIZE( g_GameActionSets ); ++i )
	{
		if ( !Q_strcmp( szActionSet, g_GameActionSets[i].strName ) )
		{
			return g_GameActionSets[i].handle;
		}
	}

	return 0;
}


void CInputSystem::ActivateSteamControllerActionSetForSlot( uint64 nSlot, GameActionSet_t eActionSet )
{
	auto steamcontroller = SteamControllerInterface();
	bool bChangedActionSet = false;

	if ( steamcontroller )
	{
		if ( nSlot == STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS )
		{
			for ( int i = 0; i < STEAM_CONTROLLER_MAX_COUNT; i++ )
			{
				if ( m_currentActionSet[i] != eActionSet )
				{
					bChangedActionSet = true;
					m_currentActionSet[i] = eActionSet;
				}
			}

			steamcontroller->ActivateActionSet( STEAM_CONTROLLER_HANDLE_ALL_CONTROLLERS, g_GameActionSets[eActionSet].handle );
		}
		else
		{
			uint64 nControllerHandles[STEAM_CONTROLLER_MAX_COUNT];
			int unNumConnected = steamcontroller->GetConnectedControllers( nControllerHandles );

			if( nSlot < unNumConnected )
			{
				if ( m_currentActionSet[nSlot] != eActionSet )
				{
					bChangedActionSet = true;
					m_currentActionSet[nSlot] = eActionSet;
				}

				steamcontroller->ActivateActionSet( nControllerHandles[nSlot], g_GameActionSets[eActionSet].handle );
			}
		}
	}

	if ( bChangedActionSet )
	{
		// If we changed action set, then flag everything for a debounce (meaning we demand to see an unpressed state before we'll register a pressed one)
		for ( int i = 0; i < STEAM_CONTROLLER_MAX_COUNT; i++ )
		{
			for ( int j = 0; j != ARRAYSIZE( g_DigitalMenuActions ); ++j )
			{
				g_DigitalMenuActions[j].bAwaitingDebounce[i] = true;
			}
		}
	}
}

const int CInputSystem::GetSteamPadDeadZone( ESteamPadAxis axis )
{
  int nDeadzone = s_nSteamPadDeadZoneTable[ axis ];

  // Do modifications if required here?

  return nDeadzone;
}

//-----------------------------------------------------------------------------
// Purpose: Processes data for controller
//-----------------------------------------------------------------------------
void CInputSystem::ReadSteamController( int iIndex )
{
}

//-----------------------------------------------------------------------------
//	Purpose: Pulse haptic feedback
//-----------------------------------------------------------------------------
/* void CInputSystem::PulseHapticOnSteamController( uint32 nControllerIndex, ESteamControllerPad ePad, unsigned short durationMicroSec )
{
	auto steamcontroller = SteamControllerInterface();
	if ( steamcontroller )
	{
			steamcontroller->TriggerHapticPulse( nControllerIndex, ePad, durationMicroSec );
	}
}*/

//-----------------------------------------------------------------------------
//	Purpose: Returns the controller State for a particular joystick slot
//-----------------------------------------------------------------------------
bool CInputSystem::GetControllerStateForSlot( int nSlot )
{
	return false;
}

int CInputSystem::GetSteamControllerIndexForSlot( int nSlot )
{
	for ( int i = 0; i < Q_ARRAYSIZE( m_Device ); i++ )
	{
		if ( m_Device[i].active && (int)m_Device[i].m_nJoystickIndex == nSlot )
		{
			return m_Device[i].m_nHardwareIndex;
		}
	}
	return -1;
}

//-----------------------------------------------------------------------------
//	Purpose: Post events, ignoring key repeats
//-----------------------------------------------------------------------------
void CInputSystem::PostKeyEvent( int iIndex, sKey_t sKey, int nSample )
{
	// Rework of xbox code here :

	AnalogCode_t	code	= ANALOG_CODE_LAST;
	float			value	= 0.f;
	//int nMsgSlot = iIndex;
	int nMsgSlot = m_Device[iIndex].m_nJoystickIndex;
	int nSampleThreshold = 1; 

	// Look for changes on the analog axes
	switch( sKey )
	{
	case SK_BUTTON_LPAD_LEFT:
	case SK_BUTTON_LPAD_RIGHT:
		{
			code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_X );
			value = ( sKey == SK_BUTTON_LPAD_LEFT ) ? -nSample : nSample;
			// Kind of a hack to horizontal values for menu selection items in Portal 2
			// The additional 5k helps accidental horizontals in menus.
			nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD ) + 5000;  
		}
		break;

	case SK_BUTTON_LPAD_UP:
	case SK_BUTTON_LPAD_DOWN:
		{
			code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_Y );
			value = ( sKey == SK_BUTTON_LPAD_UP ) ? -nSample : nSample;
			nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD );
		}
		break;

	case SK_BUTTON_RPAD_LEFT:
	case SK_BUTTON_RPAD_RIGHT:
		{
			code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_U );
			value = ( sKey == SK_BUTTON_RPAD_LEFT ) ? -nSample : nSample;
			// Kind of a hack to horizontal values for menu selection items in Portal 2
			// The additional 5k helps accidental horizontals in menus.
			nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD ) + 5000;;
		}
		break;

	case SK_BUTTON_RPAD_UP:
	case SK_BUTTON_RPAD_DOWN:
		{
			code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_R );
			value = ( sKey == SK_BUTTON_RPAD_UP ) ? -nSample : nSample;
			nSampleThreshold = ( int )( STEAMPAD_DIGITAL_PAD_THRESHOLD );
		}
		break;
	}

	// Store the analog event
	if ( ANALOG_CODE_LAST != code )
	{
		InputState_t &state = m_InputState[ m_bIsPolling ];
		state.m_pAnalogDelta[ code ] = ( int )( value - state.m_pAnalogValue[ code ] );
		state.m_pAnalogValue[ code ] = ( int )value;
		if ( state.m_pAnalogDelta[ code ] != 0 )
		{
			PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, ( int )value, state.m_pAnalogDelta[ code ] );
		}
	}

	// store the key
	m_Device[iIndex].m_appSKeys[sKey].sample = nSample;
	if ( nSample > nSampleThreshold )
	{
		m_Device[iIndex].m_appSKeys[sKey].repeats++;
	}
	else
	{
		m_Device[iIndex].m_appSKeys[sKey].repeats = 0;
		nSample = 0;
	}

	if ( m_Device[iIndex].m_appSKeys[sKey].repeats > 1 )
	{
		// application cannot handle streaming keys
		// first keypress is the only edge trigger
		return;
	}

	// package the key
	ButtonCode_t buttonCode = SKeyToButtonCode( nMsgSlot, sKey );
	if ( nSample )
	{
		PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
	}
	else
	{
		PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
	}
}

// Gets the action origin (i.e. which physical input) maps to the given virtual button for the given action set
EControllerActionOrigin CInputSystem::GetSteamControllerActionOrigin( const char* action, GameActionSet_t action_set )
{
	auto pSC = SteamControllerInterface();
	if ( pSC )
	{
		ControllerHandle_t hConnected[STEAM_CONTROLLER_MAX_COUNT];
		auto nConnected = pSC->GetConnectedControllers( hConnected );
		if ( nConnected == 0 )
		{
			return k_EControllerActionOrigin_None;
		}

		SGameActionSet* pActionSet = nullptr;
		for ( int i = 0; i < ARRAYSIZE( g_GameActionSets ); i++ )
		{
			if ( g_GameActionSets[i].eGameActionSet == action_set )
			{
				pActionSet = &g_GameActionSets[i];
				break;
			}
		}

		if ( pActionSet )
		{
			auto actionHandle = pSC->GetDigitalActionHandle( action );
			EControllerActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
			int nOrigins = pSC->GetDigitalActionOrigins( hConnected[0], pActionSet->handle, actionHandle, origins );
			if ( nOrigins > 0 )
			{
				return origins[0];
			}
		}
	}

	return k_EControllerActionOrigin_None;
}

// Gets the action origin (i.e. which physical input) maps to the given virtual button for the given action set
EControllerActionOrigin CInputSystem::GetSteamControllerActionOrigin( const char* action, ControllerActionSetHandle_t action_set_handle )
{
	auto pSC = SteamControllerInterface();
	if ( pSC && action_set_handle )
	{
		ControllerHandle_t hConnected[STEAM_CONTROLLER_MAX_COUNT];
		auto nConnected = pSC->GetConnectedControllers( hConnected );
		if ( nConnected == 0 )
		{
			return k_EControllerActionOrigin_None;
		}

		auto actionHandle = pSC->GetDigitalActionHandle( action );
		EControllerActionOrigin origins[STEAM_CONTROLLER_MAX_ORIGINS];
		int nOrigins = pSC->GetDigitalActionOrigins( hConnected[0], action_set_handle, actionHandle, origins );
		if ( nOrigins > 0 )
		{
			return origins[0];
		}
	}

	return k_EControllerActionOrigin_None;
}

// Maps a Steam Controller action origin to a string (consisting of a single character) in our SC icon font
const wchar_t*	CInputSystem::GetSteamControllerFontCharacterForActionOrigin( EControllerActionOrigin origin )
{
	if ( origin >= 0 && origin < ARRAYSIZE( g_MapSteamControllerOriginToIconFont ) )
	{
		return g_MapSteamControllerOriginToIconFont[origin];
	}
	else
	{
		return L"";
	}
}

// Maps a Steam Controller action origin to a short text string (e.g. "X", "LB", "LDOWN") describing the control.
// Prefer to actually use the icon font wherever possible.
const wchar_t* CInputSystem::GetSteamControllerDescriptionForActionOrigin( EControllerActionOrigin origin )
{
	if ( origin >= 0 && origin < ARRAYSIZE( g_MapSteamControllerOriginToDescription ) )
	{
		return g_MapSteamControllerOriginToDescription[origin];
	}
	else
	{
		return L"";
	}
}