Counter Strike : Global Offensive Source Code
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.

1025 lines
31 KiB

  1. //===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: OSX Joystick implementation for inputsystem.dll
  4. //
  5. //===========================================================================//
  6. #include <mach/mach.h>
  7. #include <mach/mach_error.h>
  8. /* For force feedback testing. */
  9. #include "inputsystem.h"
  10. #include "tier1/convar.h"
  11. // NOTE: This has to be the last file included!
  12. #include "tier0/memdbgon.h"
  13. ConVar joy_axisbutton_threshold( "joy_axisbutton_threshold", "0.3", FCVAR_ARCHIVE, "Analog axis range before a button press is registered." );
  14. ConVar joy_axis_deadzone( "joy_axis_deadzone", "0.2", FCVAR_ARCHIVE, "Dead zone near the zero point to not report movement." );
  15. //-----------------------------------------------------------------------------
  16. // Initialize all joysticks
  17. //-----------------------------------------------------------------------------
  18. void CInputSystem::InitializeJoysticks( void )
  19. {
  20. // assume no joystick
  21. m_nJoystickCount = 0;
  22. // abort startup if user requests no joystick
  23. if ( CommandLine()->FindParm("-nojoy" ) )
  24. return;
  25. IOReturn result = kIOReturnSuccess;
  26. mach_port_t masterPort = 0;
  27. io_iterator_t hidObjectIterator = 0;
  28. CFMutableDictionaryRef hidMatchDictionary = NULL;
  29. result = IOMasterPort( MACH_PORT_NULL, &masterPort);
  30. if ( kIOReturnSuccess != result )
  31. {
  32. DevMsg( 1, "joystick not found -- IOMasterPort error with bootstrap_port.\n");
  33. return;
  34. }
  35. hidMatchDictionary = IOServiceMatching(kIOHIDDeviceKey);
  36. if ( !hidMatchDictionary )
  37. {
  38. DevMsg( 1, "joystick not found -- Failed to get HID CFMutableDictionaryRef via IOServiceMatching.");
  39. return;
  40. }
  41. result = IOServiceGetMatchingServices( masterPort, hidMatchDictionary, &hidObjectIterator );
  42. if ( kIOReturnSuccess != result )
  43. {
  44. DevMsg( 1, "joystick not found -- Couldn't create a HID object iterator.");
  45. return ;
  46. }
  47. if ( !hidObjectIterator )
  48. {
  49. m_nJoystickCount = 0;
  50. return;
  51. }
  52. io_object_t ioHIDDeviceObject = 0;
  53. while ( ( ioHIDDeviceObject = IOIteratorNext(hidObjectIterator ) ) )
  54. {
  55. JoystickInfo_t &info = m_pJoystickInfo[m_nJoystickCount];
  56. info.m_pParent = this;
  57. info.m_bRemoved = false;
  58. info.m_Interface = NULL;
  59. info.m_FFInterface = 0;
  60. info.m_nButtonCount = 0;
  61. info.m_nFlags = 0;
  62. info.m_nAxisFlags = 0;
  63. info.m_bXBoxRumbleEnabled = false;
  64. // grab the device record
  65. if ( !HIDBuildDevice( ioHIDDeviceObject, info ) )
  66. continue;
  67. if ( FFIsForceFeedback(ioHIDDeviceObject) == FF_OK )
  68. {
  69. FFCreateDevice(ioHIDDeviceObject,&info.m_FFInterface);;
  70. }
  71. else
  72. {
  73. info.m_FFInterface = 0;
  74. }
  75. info.m_nDeviceId = m_nJoystickCount;
  76. info.m_nLastPolledButtons = 0;
  77. info.m_nLastPolledAxisButtons = 0;
  78. info.m_nLastPolledPOVState = 0;
  79. memset( info.m_pLastPolledAxes, 0, sizeof(info.m_pLastPolledAxes) );
  80. EnableJoystickInput( m_nJoystickCount, true );
  81. ++m_nJoystickCount;
  82. }
  83. result = IOObjectRelease(hidObjectIterator);
  84. }
  85. void HIDReportErrorNum(char *strError, long numError)
  86. {
  87. DevMsg( 1, "%s", strError );
  88. }
  89. int ButtonSort(const void *a, const void *b)
  90. {
  91. CInputSystem::OSXInputValue_t *left = (CInputSystem::OSXInputValue_t *)a;
  92. CInputSystem::OSXInputValue_t *right = (CInputSystem::OSXInputValue_t *)b;
  93. return left->m_Usage - right->m_Usage;
  94. }
  95. void CInputSystem::HIDSortJoystickButtons( JoystickInfo_t &info )
  96. {
  97. qsort( info.m_Buttons, info.m_nButtonCount, sizeof(info.m_Buttons[0]), ButtonSort );
  98. }
  99. bool CInputSystem::HIDBuildDevice( io_object_t ioHIDDeviceObject, JoystickInfo_t &info )
  100. {
  101. CFMutableDictionaryRef hidProperties = 0;
  102. kern_return_t result = IORegistryEntryCreateCFProperties( ioHIDDeviceObject, &hidProperties, kCFAllocatorDefault, kNilOptions);
  103. if ((result == KERN_SUCCESS) && hidProperties)
  104. {
  105. if ( HIDCreateOpenDeviceInterface( ioHIDDeviceObject, info ) )
  106. {
  107. HIDGetDeviceInfo( ioHIDDeviceObject, hidProperties, info );
  108. HIDGetCollectionElements( hidProperties, info );
  109. HIDSortJoystickButtons( info );
  110. }
  111. else
  112. return false;
  113. CFRelease(hidProperties);
  114. }
  115. else
  116. return false;
  117. // Filter device list to non-keyboard/mouse stuff
  118. if ( info.usagePage != kHIDPage_GenericDesktop ||
  119. ( ( info.usage != kHIDUsage_GD_Joystick &&
  120. info.usage != kHIDUsage_GD_GamePad &&
  121. info.usage != kHIDUsage_GD_MultiAxisController ) ) )
  122. {
  123. HIDDisposeDevice( info );
  124. return false;
  125. }
  126. return true;
  127. }
  128. static void HIDRemovalCallback(void *target, IOReturn result, void *refcon, void *sender)
  129. {
  130. CInputSystem::JoystickInfo_t *joy = (CInputSystem::JoystickInfo_t *) refcon;
  131. joy->m_bRemoved = true;
  132. }
  133. /* Create and open an interface to device, required prior to extracting values or building queues.
  134. * Note: appliction now owns the device and must close and release it prior to exiting
  135. */
  136. bool CInputSystem::HIDCreateOpenDeviceInterface( io_object_t hidDevice, JoystickInfo_t &info )
  137. {
  138. IOReturn result = kIOReturnSuccess;
  139. HRESULT plugInResult = S_OK;
  140. SInt32 score = 0;
  141. IOCFPlugInInterface **ppPlugInInterface = NULL;
  142. if ( NULL == info.m_Interface )
  143. {
  144. result = IOCreatePlugInInterfaceForService(hidDevice, kIOHIDDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &ppPlugInInterface, &score);
  145. if (kIOReturnSuccess == result)
  146. {
  147. plugInResult = (*ppPlugInInterface)->QueryInterface(ppPlugInInterface, CFUUIDGetUUIDBytes(kIOHIDDeviceInterfaceID),(void **)&(info.m_Interface) );
  148. if ( plugInResult != S_OK )
  149. HIDReportErrorNum("Couldn’t query HID class device interface from plugInInterface", plugInResult);
  150. (*ppPlugInInterface)->Release(ppPlugInInterface);
  151. }
  152. else
  153. HIDReportErrorNum("Failed to create **plugInInterface via IOCreatePlugInInterfaceForService.", result);
  154. }
  155. if ( info.m_Interface != NULL )
  156. {
  157. result = (*info.m_Interface)->open( info.m_Interface, 0);
  158. if ( result != kIOReturnSuccess )
  159. {
  160. HIDReportErrorNum("Failed to open pDevice->interface via open.", result);
  161. }
  162. else
  163. {
  164. (*info.m_Interface)->setRemovalCallback( info.m_Interface, HIDRemovalCallback, &info, this );
  165. }
  166. }
  167. return result == kIOReturnSuccess;
  168. }
  169. static void HIDTopLevelElementHandler(const void *value, void *parameter)
  170. {
  171. CFTypeRef refCF = 0;
  172. if (CFGetTypeID(value) != CFDictionaryGetTypeID())
  173. return;
  174. refCF = CFDictionaryGetValue((CFDictionaryRef)value, CFSTR(kIOHIDElementUsagePageKey));
  175. if ( !CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &((CInputSystem::JoystickInfo_t *) parameter)->usagePage) )
  176. DevMsg( 1, "HIDTopLevelElementHandler CFNumberGetValue error retrieving pDevice->usagePage." );
  177. refCF = CFDictionaryGetValue((CFDictionaryRef)value, CFSTR(kIOHIDElementUsageKey));
  178. if ( !CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &((CInputSystem::JoystickInfo_t *) parameter)->usage) )
  179. DevMsg( 1, "HIDTopLevelElementHandler CFNumberGetValue error retrieving pDevice->usage." );
  180. }
  181. void CInputSystem::HIDGetDeviceInfo( io_object_t hidDevice, CFMutableDictionaryRef hidProperties, JoystickInfo_t &info )
  182. {
  183. CFMutableDictionaryRef usbProperties = 0;
  184. io_registry_entry_t parent1, parent2;
  185. if ((KERN_SUCCESS == IORegistryEntryGetParentEntry(hidDevice, kIOServicePlane, &parent1))
  186. && (KERN_SUCCESS == IORegistryEntryGetParentEntry(parent1, kIOServicePlane, &parent2))
  187. && (KERN_SUCCESS == IORegistryEntryCreateCFProperties(parent2, &usbProperties, kCFAllocatorDefault, kNilOptions)))
  188. {
  189. if ( usbProperties )
  190. {
  191. CFTypeRef refCF = 0;
  192. long vendorid = 0;
  193. long productid = 0;
  194. refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDVendorIDKey) );
  195. if (refCF)
  196. CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &vendorid );
  197. refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDProductIDKey) );
  198. if (refCF)
  199. CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &productid );
  200. if ( vendorid == 0x045e )
  201. {
  202. m_bXController = true; // microsoft gamepad, lets call it a 360 controller
  203. }
  204. /* get usage page and usage */
  205. refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDPrimaryUsagePageKey) );
  206. if (refCF)
  207. {
  208. if ( !CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &info.usagePage ) )
  209. DevMsg( 1, "CInputSystem::HIDGetDeviceInfo error retrieving pDevice->usagePage." );
  210. refCF = CFDictionaryGetValue((CFDictionaryRef)hidProperties, CFSTR(kIOHIDPrimaryUsageKey));
  211. if (refCF)
  212. if (!CFNumberGetValue((CFNumberRef)refCF, kCFNumberLongType, &info.usage))
  213. DevMsg( 1, "CInputSystem::HIDGetDeviceInfo error retrieving pDevice->usage." );
  214. }
  215. if (NULL == refCF)
  216. {
  217. CFTypeRef refCFTopElement = 0;
  218. refCFTopElement = CFDictionaryGetValue( (CFDictionaryRef)hidProperties, CFSTR(kIOHIDElementKey) );
  219. {
  220. CFRange range = { 0, CFArrayGetCount((CFArrayRef)refCFTopElement) };
  221. CFArrayApplyFunction( (CFArrayRef)refCFTopElement, range, HIDTopLevelElementHandler, &info );
  222. }
  223. }
  224. CFRelease( usbProperties );
  225. }
  226. else
  227. DevMsg( 1, "CInputSystem::HIDGetDeviceInfo IORegistryEntryCreateCFProperties failed to create usbProperties." );
  228. if (kIOReturnSuccess != IOObjectRelease(parent2) )
  229. DevMsg( 1, "CInputSystem::HIDGetDeviceInfo IOObjectRelease error with parent2." );
  230. if (kIOReturnSuccess != IOObjectRelease(parent1))
  231. DevMsg( 1, "CInputSystem::HIDGetDeviceInfo IOObjectRelease error with parent1." );
  232. }
  233. }
  234. void CInputSystem::HIDGetElementInfo( CFTypeRef refElement, OSXInputValue_t &input )
  235. {
  236. long number;
  237. CFTypeRef refType;
  238. refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementCookieKey) );
  239. if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
  240. input.m_Cookie = (int) number;
  241. refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementMinKey) );
  242. if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
  243. {
  244. input.m_MinVal = input.m_MinReport = number;
  245. input.m_MaxVal = input.m_MaxReport = input.m_MinVal;
  246. }
  247. refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementMaxKey) );
  248. if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
  249. {
  250. input.m_MaxVal = input.m_MaxReport = number;
  251. }
  252. input.m_bSet = true;
  253. input.m_RefElement = refElement;
  254. refType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsageKey) );
  255. if ( refType && CFNumberGetValue( (CFNumberRef)refType, kCFNumberLongType, &number ) )
  256. {
  257. input.m_Usage = number;
  258. }
  259. }
  260. void CInputSystem::HIDAddElement(CFTypeRef refElement, JoystickInfo_t &info )
  261. {
  262. long elementType, usagePage, usage;
  263. CFTypeRef refElementType = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementTypeKey) );
  264. CFTypeRef refUsagePage = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsagePageKey) );
  265. CFTypeRef refUsage = CFDictionaryGetValue( (CFDictionaryRef)refElement, CFSTR(kIOHIDElementUsageKey) );
  266. if ( refElementType && CFNumberGetValue( (CFNumberRef)refElementType, kCFNumberLongType, &elementType) )
  267. {
  268. if ( (elementType == kIOHIDElementTypeInput_Misc) || (elementType == kIOHIDElementTypeInput_Button) || (elementType == kIOHIDElementTypeInput_Axis) )
  269. {
  270. if ( refUsagePage && CFNumberGetValue( (CFNumberRef)refUsagePage, kCFNumberLongType, &usagePage )
  271. && refUsage && CFNumberGetValue( (CFNumberRef)refUsage, kCFNumberLongType, &usage ) )
  272. {
  273. switch (usagePage)
  274. {
  275. case kHIDPage_GenericDesktop:
  276. {
  277. switch ( usage )
  278. {
  279. case kHIDUsage_GD_X:
  280. HIDGetElementInfo( refElement, info.m_xaxis );
  281. info.m_nAxisFlags |= 0x3;
  282. break;
  283. case kHIDUsage_GD_Y:
  284. HIDGetElementInfo( refElement, info.m_yaxis );
  285. info.m_nAxisFlags |= 0x3;
  286. break;
  287. case kHIDUsage_GD_Z:
  288. HIDGetElementInfo( refElement, info.m_zaxis );
  289. info.m_nAxisFlags |= 0x4;
  290. break;
  291. case kHIDUsage_GD_Rx:
  292. HIDGetElementInfo( refElement, info.m_uaxis );
  293. info.m_nAxisFlags |= 0x8;
  294. break;
  295. case kHIDUsage_GD_Ry:
  296. HIDGetElementInfo( refElement, info.m_raxis );
  297. info.m_nAxisFlags |= 0x10;
  298. break;
  299. case kHIDUsage_GD_Rz:
  300. HIDGetElementInfo( refElement, info.m_vaxis );
  301. info.m_nAxisFlags |= 0x20;
  302. break;
  303. case kHIDUsage_GD_Slider:
  304. case kHIDUsage_GD_Dial:
  305. case kHIDUsage_GD_Wheel:
  306. // unused
  307. break;
  308. case kHIDUsage_GD_Hatswitch:
  309. HIDGetElementInfo( refElement, info.m_POV );
  310. info.m_bHasPOVControl = true;
  311. break;
  312. default:
  313. break;
  314. }
  315. }
  316. break;
  317. case kHIDPage_Button:
  318. if ( info.m_nButtonCount < MAX_JOYSTICK_BUTTONS )
  319. {
  320. HIDGetElementInfo( refElement, info.m_Buttons[ info.m_nButtonCount ] );
  321. info.m_nButtonCount++;
  322. }
  323. break;
  324. default:
  325. break;
  326. }
  327. }
  328. }
  329. else if (kIOHIDElementTypeCollection == elementType)
  330. {
  331. HIDGetCollectionElements((CFMutableDictionaryRef) refElement, info );
  332. }
  333. }
  334. }
  335. static void HIDGetElementsCFArrayHandler( const void *value, void *parameter )
  336. {
  337. if ( CFGetTypeID(value) == CFDictionaryGetTypeID() )
  338. {
  339. CInputSystem::JoystickInfo_t *info = (CInputSystem::JoystickInfo_t *)parameter;
  340. if ( info )
  341. info->m_pParent->HIDAddElement( (CFTypeRef)value, *info );
  342. }
  343. }
  344. void CInputSystem::HIDGetElements( CFTypeRef refElementCurrent, JoystickInfo_t &info )
  345. {
  346. CFTypeID type = CFGetTypeID(refElementCurrent);
  347. if (type == CFArrayGetTypeID())
  348. {
  349. CFRange range = { 0, CFArrayGetCount((CFArrayRef)refElementCurrent) };
  350. CFArrayApplyFunction((CFArrayRef)refElementCurrent, range, HIDGetElementsCFArrayHandler, &info );
  351. }
  352. }
  353. void CInputSystem::HIDGetCollectionElements( CFMutableDictionaryRef deviceProperties, JoystickInfo_t &info )
  354. {
  355. CFTypeRef refElementTop = CFDictionaryGetValue((CFDictionaryRef)deviceProperties, CFSTR(kIOHIDElementKey));
  356. if (refElementTop)
  357. HIDGetElements( refElementTop, info );
  358. }
  359. void CInputSystem::HIDDisposeDevice( JoystickInfo_t &info )
  360. {
  361. kern_return_t result = KERN_SUCCESS;
  362. if ( info.m_FFInterface )
  363. {
  364. if ( m_bXController && info.m_bXBoxRumbleEnabled )
  365. {
  366. info.m_bXBoxRumbleEnabled = false;
  367. FFEFFESCAPE escape;
  368. char c;
  369. c=0x00;
  370. escape.dwSize=sizeof(escape);
  371. escape.dwCommand=0x00;
  372. escape.cbInBuffer=sizeof(c);
  373. escape.lpvInBuffer=&c;
  374. escape.cbOutBuffer=0;
  375. escape.lpvOutBuffer=NULL;
  376. FFDeviceEscape( info.m_FFInterface ,&escape );
  377. }
  378. FFReleaseDevice( info.m_FFInterface );
  379. }
  380. result = (*info.m_Interface)->close( info.m_Interface );
  381. if ( result == kIOReturnNotOpen )
  382. {
  383. // wasn't open, what?
  384. }
  385. else if ( result != kIOReturnSuccess)
  386. {
  387. HIDReportErrorNum( "Failed to close IOHIDDeviceInterface.", result );
  388. }
  389. result = (*info.m_Interface)->Release( info.m_Interface );
  390. if ( kIOReturnSuccess != result )
  391. {
  392. HIDReportErrorNum( "Failed to release IOHIDDeviceInterface.", result );
  393. }
  394. }
  395. //-----------------------------------------------------------------------------
  396. // Process the event
  397. //-----------------------------------------------------------------------------
  398. void CInputSystem::JoystickButtonEvent( ButtonCode_t button, int sample )
  399. {
  400. // package the key
  401. if ( sample )
  402. {
  403. PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, button, button );
  404. }
  405. else
  406. {
  407. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, button, button );
  408. }
  409. }
  410. //-----------------------------------------------------------------------------
  411. // Update the joystick button state
  412. //-----------------------------------------------------------------------------
  413. void CInputSystem::UpdateJoystickButtonState( int nJoystick )
  414. {
  415. JoystickInfo_t &info = m_pJoystickInfo[nJoystick];
  416. int nButtons = 0;
  417. for ( int i = 0; i < info.m_nButtonCount; i++ )
  418. {
  419. int value = HIDGetElementValue( info, info.m_Buttons[i] ) - info.m_Buttons[i].m_MinVal;
  420. int mask = info.m_nLastPolledButtons & ( 1 << i );
  421. if ( !mask && !value ) // not pressed last time or this
  422. continue;
  423. ButtonCode_t code = (ButtonCode_t)JOYSTICK_BUTTON( nJoystick, i );
  424. if ( m_bXController )
  425. {
  426. // translate the xcontroller xpad buttons into the POV button presses we expect
  427. if ( i == 11 )
  428. code = KEY_XBUTTON_UP;
  429. if ( i == 12 )
  430. code = KEY_XBUTTON_DOWN;
  431. if ( i == 13 )
  432. code = KEY_XBUTTON_LEFT;
  433. if ( i == 14 )
  434. code = KEY_XBUTTON_RIGHT;
  435. }
  436. if ( value )
  437. {
  438. // down event
  439. JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
  440. }
  441. else
  442. {
  443. // up event
  444. JoystickButtonEvent( code, 0 );
  445. }
  446. info.m_nLastPolledButtons &= ~( 1 << i );
  447. if ( value )
  448. info.m_nLastPolledButtons |= ( 1 << i );
  449. }
  450. // Analog axis buttons
  451. for ( int j = 0 ; j < MAX_JOYSTICK_AXES; ++j )
  452. {
  453. if ( ( info.m_nAxisFlags & (1 << j) ) == 0 )
  454. continue;
  455. // Positive side of the axis
  456. int mask = ( 1 << (j << 1) );
  457. ButtonCode_t code = JOYSTICK_AXIS_BUTTON( nJoystick, (j << 1) );
  458. float value = GetAnalogValue( JOYSTICK_AXIS( nJoystick, j ) );
  459. float minValue = joy_axisbutton_threshold.GetFloat();
  460. switch (j)
  461. {
  462. default:
  463. case 0:
  464. minValue *= info.m_xaxis.m_MaxVal;
  465. break;
  466. case 1:
  467. minValue *= info.m_yaxis.m_MaxVal;
  468. break;
  469. case 2:
  470. minValue *= info.m_zaxis.m_MaxVal;
  471. break;
  472. case 3:
  473. minValue *= info.m_raxis.m_MaxVal;
  474. break;
  475. case 4:
  476. minValue *= info.m_uaxis.m_MaxVal;
  477. break;
  478. case 5:
  479. minValue *= info.m_vaxis.m_MaxVal;
  480. break;
  481. }
  482. if ( j == 5 )
  483. code = KEY_XBUTTON_RTRIGGER; // left and right triggers go 0 to 255 under osx, so drag R axis over to z negative
  484. if ( value > minValue && !(info.m_nLastPolledAxisButtons & mask) )
  485. {
  486. info.m_nLastPolledAxisButtons |= mask;
  487. JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
  488. }
  489. if ( value <= minValue && (info.m_nLastPolledAxisButtons & mask) )
  490. {
  491. info.m_nLastPolledAxisButtons &= ~mask;
  492. JoystickButtonEvent( code, 0 );
  493. }
  494. // Negative side of the axis
  495. mask <<= 1;
  496. code = (ButtonCode_t)( code + 1 );
  497. if ( value < -minValue && !(info.m_nLastPolledAxisButtons & mask) )
  498. {
  499. info.m_nLastPolledAxisButtons |= mask;
  500. JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
  501. }
  502. if ( value >= -minValue && (info.m_nLastPolledAxisButtons & mask) )
  503. {
  504. info.m_nLastPolledAxisButtons &= ~mask;
  505. JoystickButtonEvent( code, 0 );
  506. }
  507. }
  508. }
  509. int CInputSystem::HIDGetElementValue( JoystickInfo_t &info, OSXInputValue_t &value )
  510. {
  511. IOReturn result = kIOReturnSuccess;
  512. IOHIDEventStruct hidEvent;
  513. hidEvent.value = 0;
  514. if ( info.m_Interface != NULL )
  515. {
  516. result = (*info.m_Interface)->getElementValue( info.m_Interface, ( IOHIDElementCookie )value.m_Cookie, &hidEvent);
  517. if (kIOReturnSuccess == result)
  518. {
  519. if ( hidEvent.value < value.m_MinReport )
  520. value.m_MinReport = hidEvent.value;
  521. if ( hidEvent.value > value.m_MaxReport )
  522. value.m_MaxReport = hidEvent.value;
  523. }
  524. }
  525. return hidEvent.value;
  526. }
  527. int CInputSystem::HIDScaledCalibratedValue( JoystickInfo_t &info, OSXInputValue_t &value )
  528. {
  529. float deviceScale = value.m_MaxVal - value.m_MinVal;
  530. float readScale = value.m_MaxReport - value.m_MinReport;
  531. int joyvalue = HIDGetElementValue( info, value );
  532. if (readScale == 0)
  533. return joyvalue;
  534. else
  535. return ( (joyvalue - value.m_MinReport) * deviceScale / readScale) + value.m_MinVal;
  536. }
  537. //-----------------------------------------------------------------------------
  538. // Purpose: Get raw joystick sample along axis
  539. //-----------------------------------------------------------------------------
  540. unsigned int CInputSystem::AxisValue( JoystickAxis_t axis, JoystickInfo_t &info )
  541. {
  542. switch (axis)
  543. {
  544. case JOY_AXIS_X:
  545. return (unsigned int)HIDScaledCalibratedValue( info, info.m_xaxis );
  546. case JOY_AXIS_Y:
  547. return (unsigned int)HIDScaledCalibratedValue( info, info.m_yaxis );
  548. case JOY_AXIS_Z:
  549. return (unsigned int)HIDScaledCalibratedValue( info, info.m_zaxis );
  550. case JOY_AXIS_R:
  551. return (unsigned int)HIDScaledCalibratedValue( info, info.m_raxis );
  552. case JOY_AXIS_U:
  553. return (unsigned int)HIDScaledCalibratedValue( info, info.m_uaxis );
  554. case JOY_AXIS_V:
  555. return (unsigned int)HIDScaledCalibratedValue( info, info.m_vaxis );
  556. }
  557. // FIX: need to do some kind of error
  558. return (unsigned int)HIDScaledCalibratedValue( info, info.m_xaxis );
  559. }
  560. //-----------------------------------------------------------------------------
  561. // Update the joystick POV control
  562. //-----------------------------------------------------------------------------
  563. void CInputSystem::UpdateJoystickPOVControl( int nJoystick )
  564. {
  565. JoystickInfo_t &info = m_pJoystickInfo[nJoystick];
  566. if ( !info.m_bHasPOVControl )
  567. return;
  568. // convert POV information into 4 bits of state information
  569. // this avoids any potential problems related to moving from one
  570. // direction to another without going through the center position
  571. unsigned int povstate = 0;
  572. int range = ( info.m_POV.m_MaxVal - info.m_POV.m_MinVal + 1 );
  573. int value = HIDGetElementValue( info, info.m_POV ) - info.m_POV.m_MinVal;
  574. if (range == 4) /* 4 position hatswitch - scale up value */
  575. value *= 2;
  576. else if (range != 8) /* Neither a 4 nor 8 positions - fall back to default position (centered) */
  577. value = -1;
  578. switch (value)
  579. {
  580. case 0:
  581. povstate |= 0x01;
  582. break;
  583. case 1:
  584. povstate |= 0x01;
  585. if ( info.m_bDiagonalPOVControlEnabled )
  586. povstate |= 0x02;
  587. break;
  588. case 2:
  589. povstate |= 0x02;
  590. break;
  591. case 3:
  592. povstate |= 0x02;
  593. if ( info.m_bDiagonalPOVControlEnabled )
  594. povstate |= 0x04;
  595. break;
  596. case 4:
  597. povstate |= 0x04;
  598. break;
  599. case 5:
  600. povstate |= 0x04;
  601. if ( info.m_bDiagonalPOVControlEnabled )
  602. povstate |= 0x08;
  603. break;
  604. case 6:
  605. povstate |= 0x08;
  606. break;
  607. case 7:
  608. povstate |= 0x08;
  609. if ( info.m_bDiagonalPOVControlEnabled )
  610. povstate |= 0x01;
  611. break;
  612. default:
  613. povstate = 0;
  614. break;
  615. }
  616. // determine which bits have changed and key an auxillary event for each change
  617. unsigned int buttons = povstate ^ info.m_nLastPolledPOVState;
  618. if ( buttons )
  619. {
  620. for ( int i = 0; i < JOYSTICK_POV_BUTTON_COUNT; ++i )
  621. {
  622. unsigned int mask = buttons & ( 1 << i );
  623. if ( !mask )
  624. continue;
  625. ButtonCode_t code = (ButtonCode_t)JOYSTICK_POV_BUTTON( nJoystick, i );
  626. if ( mask & povstate )
  627. {
  628. // Keydown on POV buttons
  629. JoystickButtonEvent( code, MAX_BUTTONSAMPLE );
  630. }
  631. else
  632. {
  633. // KeyUp on POV buttons
  634. JoystickButtonEvent( code, 0 );
  635. }
  636. }
  637. // Latch old values
  638. info.m_nLastPolledPOVState = povstate;
  639. }
  640. }
  641. //-----------------------------------------------------------------------------
  642. // Purpose: Sample the joystick
  643. //-----------------------------------------------------------------------------
  644. void CInputSystem::PollJoystick( void )
  645. {
  646. if ( !m_JoysticksEnabled.IsAnyFlagSet() )
  647. return;
  648. InputState_t &state = m_InputState[ m_bIsPolling ];
  649. for ( int i = 0; i < m_nJoystickCount; ++i )
  650. {
  651. if ( !m_JoysticksEnabled.IsFlagSet( 1 << i ) )
  652. continue;
  653. JoystickInfo_t &info = m_pJoystickInfo[i];
  654. if ( info.m_bRemoved )
  655. continue;
  656. // Poll joystick axes
  657. for ( int j = 0; j < MAX_JOYSTICK_AXES; ++j )
  658. {
  659. if ( ( info.m_nAxisFlags & ( 1 << j ) ) == 0 )
  660. continue;
  661. AnalogCode_t code = JOYSTICK_AXIS( i, j );
  662. int nValue = AxisValue( (JoystickAxis_t)j, info );
  663. float minValue = joy_axis_deadzone.GetFloat();
  664. switch (j)
  665. {
  666. default:
  667. case 0:
  668. minValue *= info.m_xaxis.m_MaxVal;
  669. break;
  670. case 1:
  671. minValue *= info.m_yaxis.m_MaxVal;
  672. break;
  673. case 2:
  674. minValue *= info.m_zaxis.m_MaxVal;
  675. break;
  676. case 3:
  677. minValue *= info.m_raxis.m_MaxVal;
  678. break;
  679. case 4:
  680. minValue *= info.m_uaxis.m_MaxVal;
  681. break;
  682. case 5:
  683. minValue *= info.m_vaxis.m_MaxVal;
  684. break;
  685. }
  686. if ( fabs(nValue) < minValue )
  687. nValue = 0;
  688. state.m_pAnalogDelta[ code ] = nValue - state.m_pAnalogValue[ code ];
  689. state.m_pAnalogValue[ code ] = nValue;
  690. if ( state.m_pAnalogDelta[ code ] != 0 )
  691. {
  692. //printf( "Joystick %i %d %d\n", code, state.m_pAnalogValue[ code ], state.m_pAnalogDelta[ code ] );
  693. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, state.m_pAnalogValue[ code ], state.m_pAnalogDelta[ code ] );
  694. }
  695. }
  696. UpdateJoystickButtonState( i );
  697. UpdateJoystickPOVControl( i );
  698. }
  699. }
  700. void CInputSystem::SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId )
  701. {
  702. for ( int i = 0; i < m_nJoystickCount; ++i )
  703. {
  704. JoystickInfo_t &info = m_pJoystickInfo[i];
  705. if ( info.m_bRemoved || !info.m_FFInterface )
  706. continue;
  707. if ( m_bXController && !info.m_bXBoxRumbleEnabled )
  708. {
  709. info.m_bXBoxRumbleEnabled = true;
  710. FFEFFESCAPE escape;
  711. char c;
  712. c=0x01;
  713. escape.dwSize=sizeof(escape);
  714. escape.dwCommand=0x00;
  715. escape.cbInBuffer=sizeof(c);
  716. escape.lpvInBuffer=&c;
  717. escape.cbOutBuffer=0;
  718. escape.lpvOutBuffer=NULL;
  719. FFDeviceEscape( info.m_FFInterface ,&escape );
  720. }
  721. FFEFFESCAPE escape;
  722. char c[2];
  723. c[0]=(unsigned char) (fLeftMotor*255.0);
  724. c[1]=(unsigned char) (fRightMotor*255.0);
  725. escape.dwSize=sizeof(escape);
  726. escape.dwCommand=0x01;
  727. escape.cbInBuffer=sizeof(c);
  728. escape.lpvInBuffer=c;
  729. escape.cbOutBuffer=0;
  730. escape.lpvOutBuffer=NULL;
  731. FFDeviceEscape( info.m_FFInterface ,&escape );
  732. }
  733. }
  734. // [will] BEGIN - These were copied from xcontroller.cpp for X360 button press emulation in OSX.
  735. // We don't want to fully support XController on OSX, just enough to emulate Xbox Controller button presses.
  736. // So instead of #defining out half of xcontroller.cpp for OSX, just copied it here.
  737. #if !defined( _CERT )
  738. #define XBX_MAX_BUTTONSAMPLE 32768
  739. #define XBX_MAX_ANALOGSAMPLE 255
  740. #define XBX_MAX_STICKSAMPLE_LEFT 32768
  741. #define XBX_MAX_STICKSAMPLE_RIGHT 32767
  742. #define XBX_MAX_STICKSAMPLE_DOWN 32768
  743. #define XBX_MAX_STICKSAMPLE_UP 32767
  744. #define XBX_STICK_SCALE_LEFT(x) ( ( float )XBX_MAX_STICKSAMPLE_LEFT/( float )( XBX_MAX_STICKSAMPLE_LEFT-(x) ) )
  745. #define XBX_STICK_SCALE_RIGHT(x) ( ( float )XBX_MAX_STICKSAMPLE_RIGHT/( float )( XBX_MAX_STICKSAMPLE_RIGHT-(x) ) )
  746. #define XBX_STICK_SCALE_DOWN(x) ( ( float )XBX_MAX_STICKSAMPLE_DOWN/( float )( XBX_MAX_STICKSAMPLE_DOWN-(x) ) )
  747. #define XBX_STICK_SCALE_UP(x) ( ( float )XBX_MAX_STICKSAMPLE_UP/( float )( XBX_MAX_STICKSAMPLE_UP-(x) ) )
  748. #define XBX_STICK_SMALL_THRESHOLD ((int)( 0.20f * XBX_MAX_STICKSAMPLE_LEFT ))
  749. // Threshold for counting analog movement as a button press
  750. #define JOYSTICK_ANALOG_BUTTON_THRESHOLD XBX_MAX_STICKSAMPLE_LEFT * 0.4f
  751. //-----------------------------------------------------------------------------
  752. // Purpose: Post Xbox events, ignoring key repeats
  753. //-----------------------------------------------------------------------------
  754. void CInputSystem::PostXKeyEvent( int userId, xKey_t xKey, int nSample )
  755. {
  756. AnalogCode_t code = ANALOG_CODE_LAST;
  757. float value = 0.f;
  758. // Map the physical controller slot to the split screen slot
  759. #if defined( _GAMECONSOLE )
  760. int nMsgSlot = XBX_GetSlotByUserId( userId );
  761. #ifdef _PS3
  762. if ( ( XBX_GetNumGameUsers() <= 1 ) && !ps3_joy_ss.GetBool() )
  763. {
  764. // In PS3 START button identification mode START key notification
  765. // is replaced with INACTIVE_START notification that can identify
  766. // controller that pressed the button
  767. if ( ( xKey == XK_BUTTON_START ) && ( nMsgSlot < 0 )
  768. && ( ( Plat_FloatTime() - g_ps3_flTimeStartButtonIdentificationMode ) < 0.5f ) )
  769. {
  770. xKey = XK_BUTTON_INACTIVE_START;
  771. nMsgSlot = userId;
  772. }
  773. else
  774. {
  775. // When we don't have splitscreen then any controller can
  776. // play and will be visible as controller #0
  777. nMsgSlot = 0;
  778. }
  779. }
  780. #endif
  781. if ( nMsgSlot < 0 )
  782. {
  783. // special case, that if you press start on a controller we've marked inactive, switch it to an
  784. // XK_BUTTON_INACTIVE_START which you can handle joins from inactive controllers
  785. if ( xKey == XK_BUTTON_START )
  786. {
  787. xKey = XK_BUTTON_INACTIVE_START;
  788. nMsgSlot = userId;
  789. }
  790. else
  791. {
  792. return; // We are not listening to this controller (not signed in and assigned)
  793. }
  794. }
  795. #else //defined( _GAMECONSOLE )
  796. int nMsgSlot = userId;
  797. #endif //defined( _GAMECONSOLE )
  798. int nSampleThreshold = 0;
  799. // Look for changes on the analog axes
  800. switch( xKey )
  801. {
  802. case XK_STICK1_LEFT:
  803. case XK_STICK1_RIGHT:
  804. {
  805. code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_X );
  806. value = ( xKey == XK_STICK1_LEFT ) ? -nSample : nSample;
  807. nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
  808. }
  809. break;
  810. case XK_STICK1_UP:
  811. case XK_STICK1_DOWN:
  812. {
  813. code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_Y );
  814. value = ( xKey == XK_STICK1_UP ) ? -nSample : nSample;
  815. nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
  816. }
  817. break;
  818. case XK_STICK2_LEFT:
  819. case XK_STICK2_RIGHT:
  820. {
  821. code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_U );
  822. value = ( xKey == XK_STICK2_LEFT ) ? -nSample : nSample;
  823. nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
  824. }
  825. break;
  826. case XK_STICK2_UP:
  827. case XK_STICK2_DOWN:
  828. {
  829. code = (AnalogCode_t)JOYSTICK_AXIS( nMsgSlot, JOY_AXIS_R );
  830. value = ( xKey == XK_STICK2_UP ) ? -nSample : nSample;
  831. nSampleThreshold = ( int )( JOYSTICK_ANALOG_BUTTON_THRESHOLD );
  832. }
  833. break;
  834. }
  835. // Store the analog event
  836. if ( ANALOG_CODE_LAST != code )
  837. {
  838. InputState_t &state = m_InputState[ m_bIsPolling ];
  839. state.m_pAnalogDelta[ code ] = ( int )( value - state.m_pAnalogValue[ code ] );
  840. state.m_pAnalogValue[ code ] = ( int )value;
  841. if ( state.m_pAnalogDelta[ code ] != 0 )
  842. {
  843. PostEvent( IE_AnalogValueChanged, m_nLastSampleTick, code, ( int )value, state.m_pAnalogDelta[ code ] );
  844. }
  845. }
  846. // store the key
  847. m_appXKeys[userId][xKey].sample = nSample;
  848. if ( nSample > nSampleThreshold )
  849. {
  850. m_appXKeys[userId][xKey].repeats++;
  851. }
  852. else
  853. {
  854. m_appXKeys[userId][xKey].repeats = 0;
  855. nSample = 0;
  856. }
  857. if ( m_appXKeys[userId][xKey].repeats > 1 )
  858. {
  859. // application cannot handle streaming keys
  860. // first keypress is the only edge trigger
  861. return;
  862. }
  863. // package the key
  864. ButtonCode_t buttonCode = XKeyToButtonCode( nMsgSlot, xKey );
  865. if ( nSample )
  866. {
  867. PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
  868. // [dkorus] check whether we're trying to set the current controller
  869. if( ( buttonCode == KEY_XBUTTON_A || buttonCode == XK_BUTTON_START )
  870. && m_setCurrentInputDeviceOnNextButtonPress )
  871. {
  872. if( IsInputDeviceConnected( INPUT_DEVICE_GAMEPAD ) )
  873. {
  874. SetCurrentInputDevice( INPUT_DEVICE_GAMEPAD );
  875. ConVarRef var( "joystick" );
  876. if( var.IsValid( ) )
  877. var.SetValue( 1 );
  878. m_setCurrentInputDeviceOnNextButtonPress = false;
  879. }
  880. }
  881. }
  882. else
  883. {
  884. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
  885. }
  886. }
  887. #endif // _CERT
  888. // [will] END - These were copied from xcontroller.cpp for X360 button press emulation in OSX.