PageRenderTime 181ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/wii_yourself/wiimote.cpp

https://bitbucket.org/lrsschtz/powerwall-viewer
C++ | 2819 lines | 2033 code | 322 blank | 464 comment | 394 complexity | ebb8705c75be2892e483e026066c312d MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. // _______________________________________________________________________________
  2. //
  3. // - WiiYourself! - native C++ Wiimote library v1.15
  4. // (c) gl.tter 2007-10 - http://gl.tter.org
  5. //
  6. // see License.txt for conditions of use. see History.txt for change log.
  7. // _______________________________________________________________________________
  8. //
  9. // wiimote.cpp (tab = 4 spaces)
  10. // VC-specifics:
  11. #ifdef _MSC_VER
  12. // disable warning "C++ exception handler used, but unwind semantics are not enabled."
  13. // in <xstring> (I don't use it - or just enable C++ exceptions)
  14. # pragma warning(disable: 4530)
  15. // auto-link with the necessary libs
  16. # pragma comment(lib, "setupapi.lib")
  17. # pragma comment(lib, "hid.lib") // for HID API (from DDK)
  18. # pragma comment(lib, "winmm.lib") // for timeGetTime()
  19. #endif // _MSC_VER
  20. #include "wiimote.h"
  21. #include <setupapi.h>
  22. extern "C" {
  23. # ifdef __MINGW32__
  24. # include <ddk/hidsdi.h>// from WinDDK
  25. # else
  26. # include <hidsdi.h>
  27. # endif
  28. }
  29. #include <sys/types.h> // for _stat
  30. #include <sys/stat.h> // "
  31. #include <process.h> // for _beginthreadex()
  32. #ifdef __BORLANDC__
  33. # include <cmath.h> // for orientation
  34. #else
  35. # include <math.h> // "
  36. #endif
  37. #include <mmreg.h> // for WAVEFORMATEXTENSIBLE
  38. #include <mmsystem.h> // for timeGetTime()
  39. // apparently not defined in some compilers:
  40. #ifndef min
  41. # define min(a,b) (((a) < (b)) ? (a) : (b))
  42. #endif
  43. // ------------------------------------------------------------------------------------
  44. // helpers
  45. // ------------------------------------------------------------------------------------
  46. template<class T> inline T sign (const T& val) { return (val<0)? T(-1) : T(1); }
  47. template<class T> inline T square(const T& val) { return val*val; }
  48. #define ARRAY_ENTRIES(array) (sizeof(array)/sizeof(array[0]))
  49. // ------------------------------------------------------------------------------------
  50. // Tracing & Debugging
  51. // ------------------------------------------------------------------------------------
  52. #define PREFIX _T("WiiYourself! : ")
  53. // comment these to auto-strip their code from the library:
  54. // (they currently use OutputDebugString() via _TRACE() - change to suit)
  55. #if (_MSC_VER >= 1400) // VC 2005+ (earlier versions don't support variable args)
  56. # define TRACE(fmt, ...) _TRACE(PREFIX fmt _T("\n"), __VA_ARGS__)
  57. # define WARN(fmt, ...) _TRACE(PREFIX _T("* ") fmt _T(" *") _T("\n"), __VA_ARGS__)
  58. #elif defined(__MINGW32__)
  59. # define TRACE(fmt, ...) _TRACE(PREFIX fmt _T("\n") , ##__VA_ARGS__)
  60. # define WARN(fmt, ...) _TRACE(PREFIX _T("* ") fmt _T(" *") _T("\n") , ##__VA_ARGS__)
  61. #endif
  62. // uncomment any of these for deeper debugging:
  63. //#define DEEP_TRACE(fmt, ...) _TRACE(PREFIX _T("|") fmt _T("\n"), __VA_ARGS__) // VC 2005+
  64. //#define DEEP_TRACE(fmt, ...) _TRACE(PREFIX _T("|") fmt _T("\n") , ##__VA_ARGS__) // mingw
  65. //#define BEEP_DEBUG_READS
  66. //#define BEEP_DEBUG_WRITES
  67. //#define BEEP_ON_ORIENTATION_ESTIMATE
  68. //#define BEEP_ON_PERIODIC_STATUSREFRESH
  69. // internals: auto-strip code from the macros if they weren't defined
  70. #ifndef TRACE
  71. # define TRACE
  72. #endif
  73. #ifndef DEEP_TRACE
  74. # define DEEP_TRACE
  75. #endif
  76. #ifndef WARN
  77. # define WARN
  78. #endif
  79. // ------------------------------------------------------------------------------------
  80. static void _cdecl _TRACE (const TCHAR* fmt, ...)
  81. {
  82. static TCHAR buffer[256];
  83. if (!fmt) return;
  84. va_list argptr;
  85. va_start (argptr, fmt);
  86. #if (_MSC_VER >= 1400) // VC 2005+
  87. _vsntprintf_s(buffer, ARRAY_ENTRIES(buffer), _TRUNCATE, fmt, argptr);
  88. #else
  89. _vsntprintf (buffer, ARRAY_ENTRIES(buffer), fmt, argptr);
  90. #endif
  91. va_end (argptr);
  92. OutputDebugString(buffer);
  93. }
  94. // ------------------------------------------------------------------------------------
  95. // wiimote
  96. // ------------------------------------------------------------------------------------
  97. // class statics
  98. HMODULE wiimote::HidDLL = NULL;
  99. unsigned wiimote::_TotalCreated = 0;
  100. unsigned wiimote::_TotalConnected = 0;
  101. hidwrite_ptr wiimote::_HidD_SetOutputReport = NULL;
  102. // (keep in sync with 'speaker_freq'):
  103. const unsigned wiimote::FreqLookup [TOTAL_FREQUENCIES] =
  104. { 0, 4200, 3920, 3640, 3360,
  105. 3130, 2940, 2760, 2610, 2470 };
  106. const TCHAR* wiimote::ButtonNameFromBit [TOTAL_BUTTON_BITS] =
  107. { _T("Left") , _T("Right"), _T("Down"), _T("Up"),
  108. _T("Plus") , _T("??") , _T("??") , _T("??") ,
  109. _T("Two") , _T("One") , _T("B") , _T("A") ,
  110. _T("Minus"), _T("??") , _T("??") , _T("Home") };
  111. const TCHAR* wiimote::ClassicButtonNameFromBit [TOTAL_BUTTON_BITS] =
  112. { _T("??") , _T("TrigR") , _T("Plus") , _T("Home"),
  113. _T("Minus"), _T("TrigL") , _T("Down") , _T("Right") ,
  114. _T("Up") , _T("Left") , _T("ZR") , _T("X") ,
  115. _T("A") , _T("Y") , _T("B") , _T("ZL") };
  116. // ------------------------------------------------------------------------------------
  117. wiimote::wiimote ()
  118. :
  119. DataRead (CreateEvent(NULL, FALSE, FALSE, NULL)),
  120. Handle (INVALID_HANDLE_VALUE),
  121. ReportType (IN_BUTTONS),
  122. bStatusReceived (false), // for output method detection
  123. bConnectInProgress (true ),
  124. bInitInProgress (false),
  125. bEnablingMotionPlus (false),
  126. bConnectionLost (false), // set if write fails after connection
  127. bMotionPlusDetected (false),
  128. bMotionPlusEnabled (false),
  129. bMotionPlusExtension (false),
  130. bCalibrateAtRest (false),
  131. bUseHIDwrite (false), // if OS supports it
  132. ChangedCallback (NULL),
  133. CallbackTriggerFlags (CHANGED_ALL),
  134. InternalChanged (NO_CHANGE),
  135. CurrentSample (NULL),
  136. HIDwriteThread (NULL),
  137. ReadParseThread (NULL),
  138. SampleThread (NULL),
  139. AsyncRumbleThread (NULL),
  140. AsyncRumbleTimeout (0),
  141. UniqueID (0) // not _guaranteed_ unique, see comments in header
  142. #ifdef ID2_FROM_DEVICEPATH // (see comments in header)
  143. // UniqueID2 (0)
  144. #endif
  145. {
  146. _ASSERT(DataRead != INVALID_HANDLE_VALUE);
  147. // if this is the first wiimote object, detect & enable HID write support
  148. if(++_TotalCreated == 1)
  149. {
  150. HidDLL = LoadLibrary(_T("hid.dll"));
  151. _ASSERT(HidDLL);
  152. if(!HidDLL)
  153. WARN(_T("Couldn't load hid.dll - shouldn't happen!"));
  154. else{
  155. _HidD_SetOutputReport = (hidwrite_ptr)
  156. GetProcAddress(HidDLL, "HidD_SetOutputReport");
  157. if(_HidD_SetOutputReport)
  158. TRACE(_T("OS supports HID writes."));
  159. else
  160. TRACE(_T("OS doesn't support HID writes."));
  161. }
  162. }
  163. // clear our public and private state data completely (including deadzones)
  164. Clear (true);
  165. Internal.Clear(true);
  166. // and the state recording vars
  167. memset(&Recording, 0, sizeof(Recording));
  168. // for overlapped IO (Read/WriteFile)
  169. memset(&Overlapped, 0, sizeof(Overlapped));
  170. Overlapped.hEvent = DataRead;
  171. Overlapped.Offset =
  172. Overlapped.OffsetHigh = 0;
  173. // for async HID output method
  174. InitializeCriticalSection(&HIDwriteQueueLock);
  175. // for polling
  176. InitializeCriticalSection(&StateLock);
  177. // request millisecond timer accuracy
  178. timeBeginPeriod(1);
  179. }
  180. // ------------------------------------------------------------------------------------
  181. wiimote::~wiimote ()
  182. {
  183. Disconnect();
  184. // events & critical sections are kept open for the lifetime of the object,
  185. // so tidy them up here:
  186. if(DataRead != INVALID_HANDLE_VALUE)
  187. CloseHandle(DataRead);
  188. DeleteCriticalSection(&HIDwriteQueueLock);
  189. DeleteCriticalSection(&StateLock);
  190. // tidy up timer accuracy request
  191. timeEndPeriod(1);
  192. // release HID DLL (for dynamic HID write method)
  193. if((--_TotalCreated == 0) && HidDLL)
  194. {
  195. FreeLibrary(HidDLL);
  196. HidDLL = NULL;
  197. _HidD_SetOutputReport = NULL;
  198. }
  199. }
  200. // ------------------------------------------------------------------------------------
  201. bool wiimote::Connect (unsigned wiimote_index, bool force_hidwrites)
  202. {
  203. if(wiimote_index == FIRST_AVAILABLE)
  204. TRACE(_T("Connecting first available Wiimote:"));
  205. else
  206. TRACE(_T("Connecting Wiimote %u:"), wiimote_index);
  207. // auto-disconnect if user is being naughty
  208. if(IsConnected())
  209. Disconnect();
  210. // get the GUID of the HID class
  211. GUID guid;
  212. HidD_GetHidGuid(&guid);
  213. // get a handle to all devices that are part of the HID class
  214. // Brian: Fun fact: DIGCF_PRESENT worked on my machine just fine. I reinstalled
  215. // Vista, and now it no longer finds the Wiimote with that parameter enabled...
  216. HDEVINFO dev_info = SetupDiGetClassDevs(&guid, NULL, NULL, DIGCF_DEVICEINTERFACE);// | DIGCF_PRESENT);
  217. if(!dev_info) {
  218. WARN(_T("couldn't get device info"));
  219. return false;
  220. }
  221. // enumerate the devices
  222. SP_DEVICE_INTERFACE_DATA didata;
  223. didata.cbSize = sizeof(didata);
  224. unsigned index = 0;
  225. unsigned wiimotes_found = 0;
  226. while(SetupDiEnumDeviceInterfaces(dev_info, NULL, &guid, index, &didata))
  227. {
  228. // get the buffer size for this device detail instance
  229. DWORD req_size = 0;
  230. SetupDiGetDeviceInterfaceDetail(dev_info, &didata, NULL, 0, &req_size, NULL);
  231. // (bizarre way of doing it) create a buffer large enough to hold the
  232. // fixed-size detail struct components, and the variable string size
  233. SP_DEVICE_INTERFACE_DETAIL_DATA *didetail =
  234. (SP_DEVICE_INTERFACE_DETAIL_DATA*) new BYTE[req_size];
  235. _ASSERT(didetail);
  236. didetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
  237. // now actually get the detail struct
  238. if(!SetupDiGetDeviceInterfaceDetail(dev_info, &didata, didetail,
  239. req_size, &req_size, NULL)) {
  240. WARN(_T("couldn't get devinterface info for %u"), index);
  241. break;
  242. }
  243. // open a shared handle to the device to query it (this will succeed even
  244. // if the wiimote is already Connect()'ed)
  245. DEEP_TRACE(_T(".. querying device %s"), didetail->DevicePath);
  246. Handle = CreateFile(didetail->DevicePath, 0, FILE_SHARE_READ|FILE_SHARE_WRITE,
  247. NULL, OPEN_EXISTING, 0, NULL);
  248. if(Handle == INVALID_HANDLE_VALUE) {
  249. DEEP_TRACE(_T(".... failed with err %x (probably harmless)."),
  250. GetLastError());
  251. goto skip;
  252. }
  253. // get the device attributes
  254. HIDD_ATTRIBUTES attrib;
  255. attrib.Size = sizeof(attrib);
  256. if(HidD_GetAttributes(Handle, &attrib))
  257. {
  258. // is this a wiimote?
  259. if((attrib.VendorID != VID) || (attrib.ProductID != PID))
  260. goto skip;
  261. // yes, but is it the one we're interested in?
  262. ++wiimotes_found;
  263. if((wiimote_index != FIRST_AVAILABLE) &&
  264. (wiimote_index != wiimotes_found))
  265. goto skip;
  266. // the wiimote is installed, but it may not be currently paired:
  267. if(wiimote_index == FIRST_AVAILABLE)
  268. TRACE(_T(".. opening Wiimote %u:"), wiimotes_found);
  269. else
  270. TRACE(_T(".. opening:"));
  271. // re-open the handle, but this time we don't allow write sharing
  272. // (that way subsequent calls can still _discover_ wiimotes above, but
  273. // will correctly fail here if they're already connected)
  274. CloseHandle(Handle);
  275. // note this also means that if another application has already opened
  276. // the device, the library can no longer connect it (this may happen
  277. // with software that enumerates all joysticks in the system, because
  278. // even though the wiimote is not a standard joystick (and can't
  279. // be read as such), it unfortunately announces itself to the OS
  280. // as one. The SDL library was known to do grab wiimotes like this.
  281. // If you cannot stop the application from doing it, you may change the
  282. // call below to open the device in full shared mode - but then the
  283. // library can no longer detect if you've already connected a device
  284. // and will allow you to connect it twice! So be careful ...
  285. Handle = CreateFile(didetail->DevicePath, GENERIC_READ|GENERIC_WRITE,
  286. FILE_SHARE_READ,
  287. NULL, OPEN_EXISTING,
  288. FILE_FLAG_OVERLAPPED, NULL);
  289. if(Handle == INVALID_HANDLE_VALUE) {
  290. TRACE(_T(".... failed with err %x"), GetLastError());
  291. goto skip;
  292. }
  293. // clear the wiimote state & buffers
  294. Clear (false); // preserves existing deadzones
  295. Internal.Clear(false); // "
  296. InternalChanged = NO_CHANGE;
  297. memset(ReadBuff , 0, sizeof(ReadBuff));
  298. bConnectionLost = false;
  299. bConnectInProgress = true; // don't parse extensions or request regular
  300. // updates until complete
  301. // enable async reading
  302. BeginAsyncRead();
  303. // autodetect which write method the Bluetooth stack supports,
  304. // by requesting the wiimote status report:
  305. if(force_hidwrites && !_HidD_SetOutputReport) {
  306. TRACE(_T(".. can't force HID writes (not supported)"));
  307. force_hidwrites = false;
  308. }
  309. if(force_hidwrites)
  310. TRACE(_T(".. (HID writes forced)"));
  311. else{
  312. // - try WriteFile() first as it's the most efficient (it uses
  313. // harware interrupts where possible and is async-capable):
  314. bUseHIDwrite = false;
  315. RequestStatusReport();
  316. // and wait for the report to arrive:
  317. DWORD last_time = timeGetTime();
  318. while(!bStatusReceived && ((timeGetTime()-last_time) < 500))
  319. Sleep(10);
  320. TRACE(_T(".. WriteFile() %s."), bStatusReceived? _T("succeeded") :
  321. _T("failed"));
  322. }
  323. // try HID write method (if supported)
  324. if(!bStatusReceived && _HidD_SetOutputReport)
  325. {
  326. bUseHIDwrite = true;
  327. RequestStatusReport();
  328. // wait for the report to arrive:
  329. DWORD last_time = timeGetTime();
  330. while(!bStatusReceived && ((timeGetTime()-last_time) < 500))
  331. Sleep(10);
  332. // did we get it?
  333. TRACE(_T(".. HID write %s."), bStatusReceived? _T("succeeded") :
  334. _T("failed"));
  335. }
  336. // still failed?
  337. if(!bStatusReceived) {
  338. WARN(_T("output failed - wiimote is not connected (or confused)."));
  339. Disconnect();
  340. goto skip;
  341. }
  342. //Sleep(500);
  343. // reset it
  344. Reset();
  345. // read the wiimote calibration info
  346. ReadCalibration();
  347. // allow the result(s) to come in (so that the caller can immediately test
  348. //MotionPlusConnected();
  349. Sleep(300); // note, don't need it on my system, better to be safe though
  350. // connected succesfully:
  351. _TotalConnected++;
  352. // use the first incomding analogue sensor values as the 'at rest'
  353. // offsets (only supports the Balance Board currently)
  354. bCalibrateAtRest = true;
  355. // refresh the public state from the internal one (so that everything
  356. // is available straight away
  357. RefreshState();
  358. // attempt to construct a unique hardware ID from the calibration
  359. // data bytes (this is obviously not guaranteed to be unique across
  360. // all devices, but may work fairly well in practice... ?)
  361. memcpy(&UniqueID, &CalibrationInfo, sizeof(CalibrationInfo));
  362. _ASSERT(UniqueID != 0); // if this fires, the calibration data didn't
  363. // arrive - this shouldn't happen
  364. #ifdef ID2_FROM_DEVICEPATH // (see comments in header)
  365. // create a 2nd alternative id by simply adding all the characters
  366. // in the device path to create a single number
  367. UniqueID2 = 0;
  368. for(unsigned index=0; index<_tcslen(didetail->DevicePath); index++)
  369. UniqueID2 += didetail->DevicePath[index];
  370. #endif
  371. // and show when we want to trigger the next periodic status request
  372. // (for battery level and connection loss detection)
  373. NextStatusTime = timeGetTime() + REQUEST_STATUS_EVERY_MS;
  374. NextMPlusDetectTime = timeGetTime() + DETECT_MPLUS_EVERY_MS;
  375. MPlusDetectCount = DETECT_MPLUS_COUNT;
  376. // tidy up
  377. delete[] (BYTE*)didetail;
  378. break;
  379. }
  380. skip:
  381. // tidy up
  382. delete[] (BYTE*)didetail;
  383. if(Handle != INVALID_HANDLE_VALUE) {
  384. CloseHandle(Handle);
  385. Handle = INVALID_HANDLE_VALUE;
  386. }
  387. // if this was the specified wiimote index, abort
  388. if((wiimote_index != FIRST_AVAILABLE) &&
  389. (wiimote_index == (wiimotes_found-1)))
  390. break;
  391. index++;
  392. }
  393. // clean up our list
  394. SetupDiDestroyDeviceInfoList(dev_info);
  395. bConnectInProgress = false;
  396. if(IsConnected()) {
  397. TRACE(_T(".. connected!"));
  398. // notify the callbacks (if requested to do so)
  399. if(CallbackTriggerFlags & WII_CONNECTED)
  400. {
  401. ChangedNotifier(WII_CONNECTED, Internal);
  402. if(ChangedCallback)
  403. ChangedCallback(*this, WII_CONNECTED, Internal);
  404. }
  405. return true;
  406. }
  407. TRACE(_T(".. connection failed."));
  408. return false;
  409. }
  410. // ------------------------------------------------------------------------------------
  411. void wiimote::CalibrateAtRest ()
  412. {
  413. _ASSERT(IsConnected());
  414. if(!IsConnected())
  415. return;
  416. // the app calls this to remove 'at rest' offsets from the analogue sensor
  417. // values (currently only works for the Balance Board):
  418. if(IsBalanceBoard()) {
  419. TRACE(_T(".. removing 'at rest' BBoard offsets."));
  420. Internal.BalanceBoard.AtRestKg = Internal.BalanceBoard.Kg;
  421. RefreshState();
  422. }
  423. }
  424. // ------------------------------------------------------------------------------------
  425. void wiimote::Disconnect ()
  426. {
  427. if(Handle == INVALID_HANDLE_VALUE)
  428. return;
  429. TRACE(_T("Disconnect()."));
  430. if(IsConnected())
  431. {
  432. _ASSERT(_TotalConnected > 0); // sanity
  433. _TotalConnected--;
  434. if(!bConnectionLost)
  435. Reset();
  436. }
  437. CloseHandle(Handle);
  438. Handle = INVALID_HANDLE_VALUE;
  439. UniqueID = 0;
  440. #ifdef ID2_FROM_DEVICEPATH // (see comments in header)
  441. UniqueID2 = 0;
  442. #endif
  443. // close the read thread
  444. if(ReadParseThread) {
  445. // unblock it so it can realise we're closing and exit straight away
  446. SetEvent(DataRead);
  447. WaitForSingleObject(ReadParseThread, 3000);
  448. CloseHandle(ReadParseThread);
  449. ReadParseThread = NULL;
  450. }
  451. // close the rumble thread
  452. if(AsyncRumbleThread) {
  453. WaitForSingleObject(AsyncRumbleThread, 3000);
  454. CloseHandle(AsyncRumbleThread);
  455. AsyncRumbleThread = NULL;
  456. AsyncRumbleTimeout = 0;
  457. }
  458. // and the sample streaming thread
  459. if(SampleThread) {
  460. WaitForSingleObject(SampleThread, 3000);
  461. CloseHandle(SampleThread);
  462. SampleThread = NULL;
  463. }
  464. #ifndef USE_DYNAMIC_HIDQUEUE
  465. HID.Deallocate();
  466. #endif
  467. bStatusReceived = false;
  468. // and clear the state
  469. Clear (false); // (preserves deadzones)
  470. Internal.Clear(false); // "
  471. InternalChanged = NO_CHANGE;
  472. }
  473. // ------------------------------------------------------------------------------------
  474. void wiimote::Reset ()
  475. {
  476. TRACE(_T("Resetting wiimote."));
  477. if(bMotionPlusEnabled)
  478. DisableMotionPlus();
  479. // stop updates (by setting report type to non-continuous, buttons-only)
  480. if(IsBalanceBoard())
  481. SetReportType(IN_BUTTONS_BALANCE_BOARD, false);
  482. else
  483. SetReportType(IN_BUTTONS, false);
  484. SetRumble (false);
  485. SetLEDs (0x00);
  486. // MuteSpeaker (true);
  487. EnableSpeaker(false);
  488. Sleep(150); // avoids loosing the extension calibration data on Connect()
  489. }
  490. // ------------------------------------------------------------------------------------
  491. unsigned __stdcall wiimote::ReadParseThreadfunc (void* param)
  492. {
  493. // this thread waits for the async ReadFile() to deliver data & parses it.
  494. // it also requests periodic status updates, deals with connection loss
  495. // and ends state recordings with a specific duration:
  496. _ASSERT(param);
  497. wiimote &remote = *(wiimote*)param;
  498. OVERLAPPED &overlapped = remote.Overlapped;
  499. unsigned exit_code = 0; // (success)
  500. while(1)
  501. {
  502. // wait until the overlapped read completes, or the timeout is reached:
  503. DWORD wait = WaitForSingleObject(overlapped.hEvent, 500);
  504. // before we deal with the result, let's do some housekeeping:
  505. // if we were recently Disconect()ed, exit now
  506. if(remote.Handle == INVALID_HANDLE_VALUE) {
  507. DEEP_TRACE(_T("read thread: wiimote was disconnected"));
  508. break;
  509. }
  510. // ditto if the connection was lost (eg. through a failed write)
  511. if(remote.bConnectionLost)
  512. {
  513. connection_lost:
  514. TRACE(_T("read thread: connection to wiimote was lost"));
  515. remote.Disconnect();
  516. remote.InternalChanged = (state_change_flags)
  517. (remote.InternalChanged | CONNECTION_LOST);
  518. // report via the callback (if any)
  519. if(remote.CallbackTriggerFlags & CONNECTION_LOST)
  520. {
  521. remote.ChangedNotifier(CONNECTION_LOST, remote.Internal);
  522. if(remote.ChangedCallback)
  523. remote.ChangedCallback(remote, CONNECTION_LOST, remote.Internal);
  524. }
  525. break;
  526. }
  527. DWORD time = timeGetTime();
  528. // periodic events (but not if we're streaming audio,
  529. // we don't want to cause a glitch)
  530. if(remote.IsConnected() && !remote.bInitInProgress &&
  531. !remote.IsPlayingAudio())
  532. {
  533. // status request due?
  534. if(time > remote.NextStatusTime)
  535. {
  536. #ifdef BEEP_ON_PERIODIC_STATUSREFRESH
  537. Beep(2000,50);
  538. #endif
  539. remote.RequestStatusReport();
  540. // and schedule the next one
  541. remote.NextStatusTime = time + REQUEST_STATUS_EVERY_MS;
  542. }
  543. // motion plus detection due?
  544. if(!remote.IsBalanceBoard() &&
  545. // !remote.bConnectInProgress &&
  546. !remote.bMotionPlusExtension &&
  547. (remote.Internal.ExtensionType != MOTION_PLUS) &&
  548. (remote.Internal.ExtensionType != PARTIALLY_INSERTED) &&
  549. (time > remote.NextMPlusDetectTime))
  550. {
  551. remote.DetectMotionPlusExtensionAsync();
  552. // we try several times in quick succession before the next
  553. // delay:
  554. if(--remote.MPlusDetectCount == 0) {
  555. remote.NextMPlusDetectTime = time + DETECT_MPLUS_EVERY_MS;
  556. remote.MPlusDetectCount = DETECT_MPLUS_COUNT;
  557. #ifdef _DEBUG
  558. TRACE(_T("--"));
  559. #endif
  560. }
  561. }
  562. }
  563. // if we're state recording and have reached the specified duration, stop
  564. if(remote.Recording.bEnabled && (remote.Recording.EndTimeMS != UNTIL_STOP) &&
  565. (time >= remote.Recording.EndTimeMS))
  566. remote.Recording.bEnabled = false;
  567. // now handle the wait result:
  568. // did the wait time out?
  569. if(wait == WAIT_TIMEOUT) {
  570. DEEP_TRACE(_T("read thread: timed out"));
  571. continue; // wait again
  572. }
  573. // did an error occurr?
  574. if(wait != WAIT_OBJECT_0) {
  575. DEEP_TRACE(_T("read thread: error waiting!"));
  576. remote.bConnectionLost = true;
  577. // deal with it straight away to avoid a longer delay
  578. goto connection_lost;
  579. }
  580. // data was received:
  581. #ifdef BEEP_DEBUG_READS
  582. Beep(500,1);
  583. #endif
  584. DWORD read = 0;
  585. // get the data read result
  586. GetOverlappedResult(remote.Handle, &overlapped, &read, TRUE);
  587. // if we read data, parse it
  588. if(read) {
  589. DEEP_TRACE(_T("read thread: parsing data"));
  590. remote.OnReadData(read);
  591. }
  592. else
  593. DEEP_TRACE(_T("read thread: didn't get any data??"));
  594. }
  595. TRACE(_T("(ending read thread)"));
  596. #ifdef BEEP_DEBUG_READS
  597. if(exit_code != 0)
  598. Beep(200,1000);
  599. #endif
  600. return exit_code;
  601. }
  602. // ------------------------------------------------------------------------------------
  603. bool wiimote::BeginAsyncRead ()
  604. {
  605. // (this is also called before we're fully connected)
  606. if(Handle == INVALID_HANDLE_VALUE)
  607. return false;
  608. DEEP_TRACE(_T(".. starting async read"));
  609. #ifdef BEEP_DEBUG_READS
  610. Beep(1000,1);
  611. #endif
  612. DWORD read;
  613. if (!ReadFile(Handle, ReadBuff, REPORT_LENGTH, &read, &Overlapped)) {
  614. DWORD err = GetLastError();
  615. if(err != ERROR_IO_PENDING) {
  616. DEEP_TRACE(_T(".... ** ReadFile() failed! **"));
  617. return false;
  618. }
  619. }
  620. // launch the completion wait/callback thread
  621. if(!ReadParseThread) {
  622. ReadParseThread = (HANDLE)_beginthreadex(NULL, 0, ReadParseThreadfunc,
  623. this, 0, NULL);
  624. DEEP_TRACE(_T(".... creating read thread"));
  625. _ASSERT(ReadParseThread);
  626. if(!ReadParseThread)
  627. return false;
  628. SetThreadPriority(ReadParseThread, WORKER_THREAD_PRIORITY);
  629. }
  630. // if ReadFile completed while we called, signal the thread to proceed
  631. if(read) {
  632. DEEP_TRACE(_T(".... got data right away"));
  633. SetEvent(DataRead);
  634. }
  635. return true;
  636. }
  637. // ------------------------------------------------------------------------------------
  638. void wiimote::OnReadData (DWORD bytes_read)
  639. {
  640. _ASSERT(bytes_read == REPORT_LENGTH);
  641. // copy our input buffer
  642. BYTE buff [REPORT_LENGTH];
  643. memcpy(buff, ReadBuff, bytes_read);
  644. // start reading again
  645. BeginAsyncRead();
  646. // parse it
  647. ParseInput(buff);
  648. }
  649. // ------------------------------------------------------------------------------------
  650. void wiimote::SetReportType (input_report type, bool continuous)
  651. {
  652. _ASSERT(IsConnected());
  653. if(!IsConnected())
  654. return;
  655. // the balance board only uses one type of report
  656. _ASSERT(!IsBalanceBoard() || type == IN_BUTTONS_BALANCE_BOARD);
  657. if(IsBalanceBoard() && (type != IN_BUTTONS_BALANCE_BOARD))
  658. return;
  659. #ifdef TRACE
  660. #define TYPE2NAME(_type) (type==_type)? _T(#_type)
  661. const TCHAR* name = TYPE2NAME(IN_BUTTONS) :
  662. TYPE2NAME(IN_BUTTONS_ACCEL_IR) :
  663. TYPE2NAME(IN_BUTTONS_ACCEL_EXT) :
  664. TYPE2NAME(IN_BUTTONS_ACCEL_IR_EXT) :
  665. TYPE2NAME(IN_BUTTONS_BALANCE_BOARD) :
  666. _T("(unknown??)");
  667. TRACE(_T("ReportType: %s (%s)"), name, (continuous? _T("continuous") :
  668. _T("non-continuous")));
  669. #endif
  670. ReportType = type;
  671. switch(type)
  672. {
  673. case IN_BUTTONS_ACCEL_IR:
  674. EnableIR(wiimote_state::ir::EXTENDED);
  675. break;
  676. case IN_BUTTONS_ACCEL_IR_EXT:
  677. EnableIR(wiimote_state::ir::BASIC);
  678. break;
  679. default:
  680. DisableIR();
  681. break;
  682. }
  683. BYTE buff [REPORT_LENGTH] = {0};
  684. buff[0] = OUT_TYPE;
  685. buff[1] = (continuous ? 0x04 : 0x00) | GetRumbleBit();
  686. buff[2] = (BYTE)type;
  687. WriteReport(buff);
  688. // Sleep(15);
  689. }
  690. // ------------------------------------------------------------------------------------
  691. void wiimote::SetLEDs (BYTE led_bits)
  692. {
  693. _ASSERT(IsConnected());
  694. if(!IsConnected() || bInitInProgress)
  695. return;
  696. _ASSERT(led_bits <= 0x0f);
  697. led_bits &= 0xf;
  698. BYTE buff [REPORT_LENGTH] = {0};
  699. buff[0] = OUT_LEDs;
  700. buff[1] = (led_bits<<4) | GetRumbleBit();
  701. WriteReport(buff);
  702. Internal.LED.Bits = led_bits;
  703. }
  704. // ------------------------------------------------------------------------------------
  705. void wiimote::SetRumble (bool on)
  706. {
  707. _ASSERT(IsConnected());
  708. if(!IsConnected())
  709. return;
  710. if(Internal.bRumble == on)
  711. return;
  712. Internal.bRumble = on;
  713. // if we're streaming audio, we don't need to send a report (sending it makes
  714. // the audio glitch, and the rumble bit is sent with every report anyway)
  715. if(IsPlayingAudio())
  716. return;
  717. BYTE buff [REPORT_LENGTH] = {0};
  718. buff[0] = OUT_STATUS;
  719. buff[1] = on? 0x01 : 0x00;
  720. WriteReport(buff);
  721. }
  722. // ------------------------------------------------------------------------------------
  723. unsigned __stdcall wiimote::AsyncRumbleThreadfunc (void* param)
  724. {
  725. // auto-disables rumble after x milliseconds:
  726. _ASSERT(param);
  727. wiimote &remote = *(wiimote*)param;
  728. while(remote.IsConnected())
  729. {
  730. if(remote.AsyncRumbleTimeout)
  731. {
  732. DWORD current_time = timeGetTime();
  733. if(current_time >= remote.AsyncRumbleTimeout)
  734. {
  735. if(remote.Internal.bRumble)
  736. remote.SetRumble(false);
  737. remote.AsyncRumbleTimeout = 0;
  738. }
  739. Sleep(1);
  740. }
  741. else
  742. Sleep(4);
  743. }
  744. return 0;
  745. }
  746. // ------------------------------------------------------------------------------------
  747. void wiimote::RumbleForAsync (unsigned milliseconds)
  748. {
  749. // rumble for a fixed amount of time
  750. _ASSERT(IsConnected());
  751. if(!IsConnected())
  752. return;
  753. SetRumble(true);
  754. // show how long thread should wait to disable rumble again
  755. // (it it's currently rumbling it will just extend the time)
  756. AsyncRumbleTimeout = timeGetTime() + milliseconds;
  757. // create the thread?
  758. if(AsyncRumbleThread)
  759. return;
  760. AsyncRumbleThread = (HANDLE)_beginthreadex(NULL, 0, AsyncRumbleThreadfunc, this,
  761. 0, NULL);
  762. _ASSERT(AsyncRumbleThread);
  763. if(!AsyncRumbleThread) {
  764. WARN(_T("couldn't create rumble thread!"));
  765. return;
  766. }
  767. SetThreadPriority(AsyncRumbleThread, WORKER_THREAD_PRIORITY);
  768. }
  769. // ------------------------------------------------------------------------------------
  770. void wiimote::RequestStatusReport ()
  771. {
  772. // (this can be called before we're fully connected)
  773. _ASSERT(Handle != INVALID_HANDLE_VALUE);
  774. if(Handle == INVALID_HANDLE_VALUE)
  775. return;
  776. BYTE buff [REPORT_LENGTH] = {0};
  777. buff[0] = OUT_STATUS;
  778. buff[1] = GetRumbleBit();
  779. WriteReport(buff);
  780. }
  781. // ------------------------------------------------------------------------------------
  782. bool wiimote::ReadAddress (int address, short size)
  783. {
  784. // asynchronous
  785. BYTE buff [REPORT_LENGTH] = {0};
  786. buff[0] = OUT_READMEMORY;
  787. buff[1] = (BYTE)(((address & 0xff000000) >> 24) | GetRumbleBit());
  788. buff[2] = (BYTE)( (address & 0x00ff0000) >> 16);
  789. buff[3] = (BYTE)( (address & 0x0000ff00) >> 8);
  790. buff[4] = (BYTE)( (address & 0x000000ff));
  791. buff[5] = (BYTE)( (size & 0xff00 ) >> 8);
  792. buff[6] = (BYTE)( (size & 0xff));
  793. return WriteReport(buff);
  794. }
  795. // ------------------------------------------------------------------------------------
  796. void wiimote::WriteData (int address, BYTE size, const BYTE* buff)
  797. {
  798. // asynchronous
  799. BYTE write [REPORT_LENGTH] = {0};
  800. write[0] = OUT_WRITEMEMORY;
  801. write[1] = (BYTE)(((address & 0xff000000) >> 24) | GetRumbleBit());
  802. write[2] = (BYTE)( (address & 0x00ff0000) >> 16);
  803. write[3] = (BYTE)( (address & 0x0000ff00) >> 8);
  804. write[4] = (BYTE)( (address & 0x000000ff));
  805. write[5] = size;
  806. memcpy(write+6, buff, size);
  807. WriteReport(write);
  808. }
  809. // ------------------------------------------------------------------------------------
  810. int wiimote::ParseInput (BYTE* buff)
  811. {
  812. int changed = 0;
  813. // lock our internal state (so RefreshState() is blocked until we're done
  814. EnterCriticalSection(&StateLock);
  815. switch(buff[0])
  816. {
  817. case IN_BUTTONS:
  818. DEEP_TRACE(_T(".. parsing buttons."));
  819. changed |= ParseButtons(buff);
  820. break;
  821. case IN_BUTTONS_ACCEL:
  822. DEEP_TRACE(_T(".. parsing buttons/accel."));
  823. changed |= ParseButtons(buff);
  824. if(!IsBalanceBoard())
  825. changed |= ParseAccel(buff);
  826. break;
  827. case IN_BUTTONS_ACCEL_EXT:
  828. DEEP_TRACE(_T(".. parsing extenion/accel."));
  829. changed |= ParseButtons(buff);
  830. changed |= ParseExtension(buff, 6);
  831. if(!IsBalanceBoard())
  832. changed |= ParseAccel(buff);
  833. break;
  834. case IN_BUTTONS_ACCEL_IR:
  835. DEEP_TRACE(_T(".. parsing ir/accel."));
  836. changed |= ParseButtons(buff);
  837. if(!IsBalanceBoard()) {
  838. changed |= ParseAccel(buff);
  839. changed |= ParseIR(buff);
  840. }
  841. break;
  842. case IN_BUTTONS_ACCEL_IR_EXT:
  843. DEEP_TRACE(_T(".. parsing ir/extenion/accel."));
  844. changed |= ParseButtons(buff);
  845. changed |= ParseExtension(buff, 16);
  846. if(!IsBalanceBoard()) {
  847. changed |= ParseAccel(buff);
  848. changed |= ParseIR (buff);
  849. }
  850. break;
  851. case IN_BUTTONS_BALANCE_BOARD:
  852. DEEP_TRACE(_T(".. parsing buttson/balance."));
  853. changed |= ParseButtons(buff);
  854. changed |= ParseExtension(buff, 3);
  855. break;
  856. case IN_READADDRESS:
  857. DEEP_TRACE(_T(".. parsing read address."));
  858. changed |= ParseButtons (buff);
  859. changed |= ParseReadAddress(buff);
  860. break;
  861. case IN_STATUS:
  862. DEEP_TRACE(_T(".. parsing status."));
  863. changed |= ParseStatus(buff);
  864. // show that we received the status report (used for output method
  865. // detection during Connect())
  866. bStatusReceived = true;
  867. break;
  868. default:
  869. DEEP_TRACE(_T(".. ** unknown input ** (happens)."));
  870. ///_ASSERT(0);
  871. //Debug.WriteLine("Unknown report type: " + type.ToString());
  872. LeaveCriticalSection(&StateLock);
  873. return false;
  874. }
  875. // if we're recording and some state we care about has changed, insert it into
  876. // the state history
  877. if(Recording.bEnabled && (changed & Recording.TriggerFlags))
  878. {
  879. DEEP_TRACE(_T(".. adding state to history"));
  880. state_event event;
  881. event.time_ms = timeGetTime();
  882. event.state = *(wiimote_state*)this;
  883. Recording.StateHistory->push_back(event);
  884. }
  885. // for polling: show which state has changed since the last RefreshState()
  886. InternalChanged = (state_change_flags)(InternalChanged | changed);
  887. LeaveCriticalSection(&StateLock);
  888. // callbacks: call it (if set & state the app is interested in has changed)
  889. if(changed & CallbackTriggerFlags)
  890. {
  891. DEEP_TRACE(_T(".. calling state change callback"));
  892. ChangedNotifier((state_change_flags)changed, Internal);
  893. if(ChangedCallback)
  894. ChangedCallback(*this, (state_change_flags)changed, Internal);
  895. }
  896. DEEP_TRACE(_T(".. parse complete."));
  897. return true;
  898. }
  899. // ------------------------------------------------------------------------------------
  900. state_change_flags wiimote::RefreshState ()
  901. {
  902. // nothing changed since the last call?
  903. if(InternalChanged == NO_CHANGE)
  904. return NO_CHANGE;
  905. // copy the internal state to our public data members:
  906. // synchronise the interal state with the read/parse thread (we don't want
  907. // values changing during the copy)
  908. EnterCriticalSection(&StateLock);
  909. // remember which state changed since the last call
  910. state_change_flags changed = InternalChanged;
  911. // preserve the application-set deadzones (if any)
  912. joystick::deadzone nunchuk_deadzone = Nunchuk.Joystick.DeadZone;
  913. joystick::deadzone classic_joyl_deadzone = ClassicController.JoystickL.DeadZone;
  914. joystick::deadzone classic_joyr_deadzone = ClassicController.JoystickR.DeadZone;
  915. // copy the internal state to the public one
  916. *(wiimote_state*)this = Internal;
  917. InternalChanged = NO_CHANGE;
  918. // restore the application-set deadzones
  919. Nunchuk.Joystick.DeadZone = nunchuk_deadzone;
  920. ClassicController.JoystickL.DeadZone = classic_joyl_deadzone;
  921. ClassicController.JoystickR.DeadZone = classic_joyr_deadzone;
  922. LeaveCriticalSection(&StateLock);
  923. return changed;
  924. }
  925. // ------------------------------------------------------------------------------------
  926. void wiimote::DetectMotionPlusExtensionAsync ()
  927. {
  928. #ifdef _DEBUG
  929. TRACE(_T("(looking for motion plus)"));
  930. #endif
  931. // show that we're expecting the result shortly
  932. MotionPlusDetectCount++;
  933. // MotionPLus reports at a different address than other extensions (until
  934. // activated, when it maps itself into the usual extension registers), so
  935. // try to detect it first:
  936. ReadAddress(REGISTER_MOTIONPLUS_DETECT, 6);
  937. }
  938. // ------------------------------------------------------------------------------------
  939. bool wiimote::EnableMotionPlus ()
  940. {
  941. TRACE(_T("Enabling Motion Plus:"));
  942. bMotionPlusExtension = false;
  943. bInitInProgress = true;
  944. bEnablingMotionPlus = true;
  945. // Initialize it:
  946. WriteData(REGISTER_MOTIONPLUS_INIT , 0x55);
  947. // Sleep(50);
  948. // Enable it (this maps it to the standard extension port):
  949. WriteData(REGISTER_MOTIONPLUS_ENABLE, 0x04);
  950. // Sleep(50);
  951. Sleep(500);
  952. return true;
  953. }
  954. // ------------------------------------------------------------------------------------
  955. bool wiimote::DisableMotionPlus ()
  956. {
  957. if(!bMotionPlusDetected || !bMotionPlusEnabled)
  958. return false;
  959. TRACE(_T("Disabling Motion Plus:"));
  960. // disable it (this makes standard extensions visible again)
  961. WriteData(REGISTER_EXTENSION_INIT1, 0x55);
  962. return true;
  963. }
  964. // ------------------------------------------------------------------------------------
  965. void wiimote::InitializeExtension ()
  966. {
  967. TRACE(_T("Initialising Extension."));
  968. // wibrew.org: The new way to initialize the extension is by writing 0x55 to
  969. // 0x(4)A400F0, then writing 0x00 to 0x(4)A400FB. It works on all extensions, and
  970. // makes the extension type bytes unencrypted. This means that you no longer have
  971. // to decrypt the extension bytes using the transform listed above.
  972. bInitInProgress = true;
  973. _ASSERT(Internal.bExtension);
  974. // only initialize if it's not a MotionPlus
  975. if(!bEnablingMotionPlus) {
  976. WriteData (REGISTER_EXTENSION_INIT1, 0x55);
  977. WriteData (REGISTER_EXTENSION_INIT2, 0x00);
  978. }
  979. else
  980. bEnablingMotionPlus = false;
  981. ReadAddress(REGISTER_EXTENSION_TYPE , 6);
  982. }
  983. // ------------------------------------------------------------------------------------
  984. int wiimote::ParseStatus (BYTE* buff)
  985. {
  986. // parse the buttons
  987. int changed = ParseButtons(buff);
  988. // get the battery level
  989. BYTE battery_raw = buff[6];
  990. if(Internal.BatteryRaw != battery_raw)
  991. changed |= BATTERY_CHANGED;
  992. Internal.BatteryRaw = battery_raw;
  993. // it is estimated that ~200 is the maximum battery level
  994. Internal.BatteryPercent = battery_raw / 2;
  995. // there is also a flag that shows if the battery is nearly empty
  996. bool drained = buff[3] & 0x01;
  997. if(drained != bBatteryDrained)
  998. {
  999. bBatteryDrained = drained;
  1000. if(drained)
  1001. changed |= BATTERY_DRAINED;
  1002. }
  1003. // leds
  1004. BYTE leds = buff[3] >> 4;
  1005. if(leds != Internal.LED.Bits)
  1006. changed |= LEDS_CHANGED;
  1007. Internal.LED.Bits = leds;
  1008. // don't handle extensions until a connection is complete
  1009. // if(bConnectInProgress)
  1010. // return changed;
  1011. bool extension = ((buff[3] & 0x02) != 0);
  1012. // TRACE(_T("(extension = %s)"), (extension? _T("TRUE") : _T("false")));
  1013. if(extension != Internal.bExtension)
  1014. {
  1015. if(!Internal.bExtension)
  1016. {
  1017. TRACE(_T("Extension connected:"));
  1018. Internal.bExtension = true;
  1019. InitializeExtension();
  1020. }
  1021. else{
  1022. TRACE(_T("Extension disconnected."));
  1023. Internal.bExtension = false;
  1024. Internal.ExtensionType = wiimote_state::NONE;
  1025. bMotionPlusEnabled = false;
  1026. bMotionPlusExtension = false;
  1027. bMotionPlusDetected = false;
  1028. bInitInProgress = false;
  1029. bEnablingMotionPlus = false;
  1030. changed |= EXTENSION_DISCONNECTED;
  1031. // renable reports
  1032. // SetReportType(ReportType);
  1033. }
  1034. }
  1035. return changed;
  1036. }
  1037. // ------------------------------------------------------------------------------------
  1038. int wiimote::ParseButtons (BYTE* buff)
  1039. {
  1040. int changed = 0;
  1041. // WORD bits = *(WORD*)(buff+1);
  1042. WORD bits = *(WORD*)(buff+1) & Button.ALL;
  1043. if(bits != Internal.Button.Bits)
  1044. changed |= BUTTONS_CHANGED;
  1045. Internal.Button.Bits = bits;
  1046. return changed;
  1047. }
  1048. // ------------------------------------------------------------------------------------
  1049. bool wiimote::EstimateOrientationFrom (wiimote_state::acceleration &accel)
  1050. {
  1051. // Orientation estimate from acceleration data (shared between wiimote and nunchuk)
  1052. // return true if the orientation was updated
  1053. // assume the controller is stationary if the acceleration vector is near
  1054. // 1g for several updates (this may not always be correct)
  1055. float length_sq = square(accel.X) + square(accel.Y) + square(accel.Z);
  1056. // TODO: as I'm comparing _squared_ length, I really need different
  1057. // min/max epsilons...
  1058. #define DOT(x1,y1,z1, x2,y2,z2) ((x1*x2) + (y1*y2) + (z1*z2))
  1059. static const float epsilon = 0.2f;
  1060. if((length_sq >= (1.f-epsilon)) && (length_sq <= (1.f+epsilon)))
  1061. {
  1062. if(++WiimoteNearGUpdates < 2)
  1063. return false;
  1064. // wiimote seems to be stationary: normalize the current acceleration
  1065. // (ie. the assumed gravity vector)
  1066. float inv_len = 1.f / sqrt(length_sq);
  1067. float x = accel.X * inv_len;
  1068. float y = accel.Y * inv_len;
  1069. float z = accel.Z * inv_len;
  1070. // copy the values
  1071. accel.Orientation.X = x;
  1072. accel.Orientation.Y = y;
  1073. accel.Orientation.Z = z;
  1074. // and extract pitch & roll from them:
  1075. // (may not be optimal)
  1076. float pitch = -asin(y) * 57.2957795f;
  1077. // float roll = asin(x) * 57.2957795f;
  1078. float roll = atan2(x,z) * 57.2957795f;
  1079. if(z < 0) {
  1080. pitch = (y < 0)? 180 - pitch : -180 - pitch;
  1081. roll = (x < 0)? -180 - roll : 180 - roll;
  1082. }
  1083. accel.Orientation.Pitch = pitch;
  1084. accel.Orientation.Roll = roll;
  1085. // show that we just updated orientation
  1086. accel.Orientation.UpdateAge = 0;
  1087. #ifdef BEEP_ON_ORIENTATION_ESTIMATE
  1088. Beep(2000, 1);
  1089. #endif
  1090. return true; // updated
  1091. }
  1092. // not updated this time:
  1093. WiimoteNearGUpdates = 0;
  1094. // age the last orientation update
  1095. accel.Orientation.UpdateAge++;
  1096. return false;
  1097. }
  1098. // ------------------------------------------------------------------------------------
  1099. void wiimote::ApplyJoystickDeadZones (wiimote_state::joystick &joy)
  1100. {
  1101. // apply the deadzones to each axis (if set)
  1102. if((joy.DeadZone.X > 0.f) && (joy.DeadZone.X <= 1.f))
  1103. {
  1104. if(fabs(joy.X) <= joy.DeadZone.X)
  1105. joy.X = 0;
  1106. else{
  1107. joy.X -= joy.DeadZone.X * sign(joy.X);
  1108. joy.X /= 1.f - joy.DeadZone.X;
  1109. }
  1110. }
  1111. if((joy.DeadZone.Y > 0.f) && (joy.DeadZone.Y <= 1.f))
  1112. {
  1113. if(fabs(joy.Y) <= joy.DeadZone.Y)
  1114. joy.Y = 0;
  1115. else{
  1116. joy.Y -= joy.DeadZone.Y * sign(joy.Y);
  1117. joy.Y /= 1.f - joy.DeadZone.Y;
  1118. }
  1119. }
  1120. }
  1121. // ------------------------------------------------------------------------------------
  1122. int wiimote::ParseAccel (BYTE* buff)
  1123. {
  1124. int changed = 0;
  1125. BYTE raw_x = buff[3];
  1126. BYTE raw_y = buff[4];
  1127. BYTE raw_z = buff[5];
  1128. if((raw_x != Internal.Acceleration.RawX) ||
  1129. (raw_y != Internal.Acceleration.RawY) ||
  1130. (raw_z != Internal.Acceleration.RawZ))
  1131. changed |= ACCEL_CHANGED;
  1132. Internal.Acceleration.RawX = raw_x;
  1133. Internal.Acceleration.RawY = raw_y;
  1134. Internal.Acceleration.RawZ = raw_z;
  1135. // avoid / 0.0 when calibration data hasn't arrived yet
  1136. if(Internal.CalibrationInfo.X0)
  1137. {
  1138. Internal.Acceleration.X =
  1139. ((float)Internal.Acceleration.RawX - Internal.CalibrationInfo.X0) /
  1140. ((float)Internal.CalibrationInfo.XG - Internal.CalibrationInfo.X0);
  1141. Internal.Acceleration.Y =
  1142. ((float)Internal.Acceleration.RawY - Internal.CalibrationInfo.Y0) /
  1143. ((float)Internal.CalibrationInfo.YG - Internal.CalibrationInfo.Y0);
  1144. Internal.Acceleration.Z =
  1145. ((float)Internal.Acceleration.RawZ - Internal.CalibrationInfo.Z0) /
  1146. ((float)Internal.CalibrationInfo.ZG - Internal.CalibrationInfo.Z0);
  1147. }
  1148. else{
  1149. Internal.Acceleration.X =
  1150. Internal.Acceleration.Y =
  1151. Internal.Acceleration.Z = 0.f;
  1152. }
  1153. // see if we can estimate the orientation from the current values
  1154. if(EstimateOrientationFrom(Internal.Acceleration))
  1155. changed |= ORIENTATION_CHANGED;
  1156. return changed;
  1157. }
  1158. // ------------------------------------------------------------------------------------
  1159. int wiimote::ParseIR (BYTE* buff)
  1160. {
  1161. if(Internal.IR.Mode == wiimote_state::ir::OFF)
  1162. return NO_CHANGE;
  1163. // avoid garbage values when the MotionPlus is enabled, but the app is
  1164. // still using the extended IR report type
  1165. if(bMotionPlusEnabled && (Internal.IR.Mode == wiimote_state::ir::EXTENDED))
  1166. return NO_CHANGE;
  1167. // take a copy of the existing IR state (so we can detect changes)
  1168. wiimote_state::ir prev_ir = Internal.IR;
  1169. // only updates the other values if the dots are visible (so that the last
  1170. // valid values stay unmodified)
  1171. switch(Internal.IR.Mode)
  1172. {
  1173. case wiimote_state::ir::BASIC:
  1174. // 2 dots are encoded in 5 bytes, so read 2 at a time
  1175. for(unsigned step=0; step<2; step++)
  1176. {
  1177. ir::dot &dot0 = Internal.IR.Dot[step*2 ];
  1178. ir::dot &dot1 = Internal.IR.Dot[step*2+1];
  1179. const unsigned offs = 6 + (step*5); // 5 bytes for 2 dots
  1180. dot0.bVisible = !(buff[offs ] == 0xff && buff[offs+1] == 0xff);
  1181. dot1.bVisible = !(buff[offs+3] == 0xff && buff[offs+4] == 0xff);
  1182. if(dot0.bVisible) {
  1183. dot0.RawX = buff[offs ] | ((buff[offs+2] >> 4) & 0x03) << 8;;
  1184. dot0.RawY = buff[offs+1] | ((buff[offs+2] >> 6) & 0x03) << 8;;
  1185. dot0.X = 1.f - (dot0.RawX / (float)wiimote_state::ir::MAX_RAW_X);
  1186. dot0.Y = (dot0.RawY / (float)wiimote_state::ir::MAX_RAW_Y);
  1187. }
  1188. if(dot1.bVisible) {
  1189. dot1.RawX = buff[offs+3] | ((buff[offs+2] >> 0) & 0x03) << 8;
  1190. dot1.RawY = buff[offs+4] | ((buff[offs+2] >> 2) & 0x03) << 8;
  1191. dot1.X = 1.f - (dot1.RawX / (float)wiimote_state::ir::MAX_RAW_X);
  1192. dot1.Y = (dot1.RawY / (float)wiimote_state::ir::MAX_RAW_Y);
  1193. }
  1194. }
  1195. break;
  1196. case wiimote_state::ir::EXTENDED:
  1197. // each dot is encoded into 3 bytes
  1198. for(unsigned index=0; index<4; index++)
  1199. {
  1200. ir::dot &dot = Internal.IR.Dot[index];
  1201. const unsigned offs = 6 + (index * 3);
  1202. dot.bVisible = !(buff[offs ]==0xff && buff[offs+1]==0xff &&
  1203. buff[offs+2]==0xff);
  1204. if(dot.bVisible) {
  1205. dot.RawX = buff[offs ] | ((buff[offs+2] >> 4) & 0x03) << 8;
  1206. dot.RawY = buff[offs+1] | ((buff[offs+2] >> 6) & 0x03) << 8;
  1207. dot.X = 1.f - (dot.RawX / (float)wiimote_state::ir::MAX_RAW_X);
  1208. dot.Y = (dot.RawY / (float)wiimote_state::ir::MAX_RAW_Y);
  1209. dot.Size = buff[offs+2] & 0x0f;
  1210. }
  1211. }
  1212. break;
  1213. case wiimote_state::ir::FULL:
  1214. _ASSERT(0); // not supported yet;
  1215. break;
  1216. }
  1217. return memcmp(&prev_ir, &Internal.IR, sizeof(Internal.IR))? IR_CHANGED : 0;
  1218. }
  1219. // ------------------------------------------------------------------------------------
  1220. inline float wiimote::GetBalanceValue (short sensor, short min, short mid, short max)
  1221. {
  1222. if(max == mid || mid == min)
  1223. return 0;
  1224. float val = (sensor < mid)?
  1225. 68.0f * ((float)(sensor - min) / (mid - min)) :
  1226. 68.0f * ((float)(sensor - mid) / (max - mid)) + 68.0f;
  1227. // divide by four (so that each sensor is correct)
  1228. return val * 0.25f;
  1229. }
  1230. // ------------------------------------------------------------------------------------
  1231. int wiimote::ParseExtension (BYTE *buff, unsigned offset)
  1232. {
  1233. int changed = 0;
  1234. switch(Internal.ExtensionType)
  1235. {
  1236. case wiimote_state::NUNCHUK:
  1237. {
  1238. // buttons
  1239. bool c = (buff[offset+5] & 0x02) == 0;
  1240. bool z = (buff[offset+5] & 0x01) == 0;
  1241. if((c != Internal.Nunchuk.C) || (z != Internal.Nunchuk.Z))
  1242. changed |= NUNCHUK_BUTTONS_CHANGED;
  1243. Internal.Nunchuk.C = c;
  1244. Internal.Nunchuk.Z = z;
  1245. // acceleration
  1246. {
  1247. wiimote_state::acceleration &accel = Internal.Nunchuk.Acceleration;
  1248. BYTE raw_x = buff[offset+2];
  1249. BYTE raw_y = buff[offset+3];
  1250. BYTE raw_z = buff[offset+4];
  1251. if((raw_x != accel.RawX) || (raw_y != accel.RawY) || (raw_z != accel.RawZ))
  1252. changed |= NUNCHUK_ACCEL_CHANGED;
  1253. accel.RawX = raw_x;
  1254. accel.RawY = raw_y;
  1255. accel.RawZ = raw_z;
  1256. wiimote_state::nunchuk::calibration_info &calib =
  1257. Internal.Nunchuk.CalibrationInfo;
  1258. accel.X = ((float)raw_x - calib.X0) / ((float)calib.XG - calib.X0);
  1259. accel.Y = ((float)raw_y - calib.Y0) / ((float)calib.YG - calib.Y0);
  1260. accel.Z = ((float)raw_z - calib.Z0) / ((float)calib.ZG - calib.Z0);
  1261. // try to extract orientation from the accel:
  1262. if(EstimateOrientationFrom(accel))
  1263. changed |= NUNCHUK_ORIENTATION_CHANGED;
  1264. }
  1265. {
  1266. // joystick:
  1267. wiimote_state::joystick &joy = Internal.Nunchuk.Joystick;
  1268. float raw_x = buff[offset+0];
  1269. float raw_y = buff[offset+1];
  1270. if((raw_x != joy.RawX) || (raw_y != joy.RawY))
  1271. changed |= NUNCHUK_JOYSTICK_CHANGED;
  1272. joy.RawX = raw_x;
  1273. joy.RawY = raw_y;
  1274. // apply the calibration data
  1275. wiimote_state::nunchuk::calibration_info &calib =
  1276. Internal.Nunchuk.CalibrationInfo;
  1277. if(Internal.Nunchuk.CalibrationInfo.MaxX != 0x00)
  1278. joy.X = ((float)raw_x - calib.MidX) / ((float)calib.MaxX - calib.MinX);
  1279. if(calib.MaxY != 0x00)
  1280. joy.Y = ((float)raw_y - calib.MidY) / ((float)calib.MaxY - calib.MinY);
  1281. // i prefer the outputs to range -1 - +1 (note this also affects the
  1282. // deadzone calculations)
  1283. joy.X *= 2; joy.Y *= 2;
  1284. // apply the public deadzones to the internal state (if set)
  1285. joy.DeadZone = Nunchuk.Joystick.DeadZone;
  1286. ApplyJoystickDeadZones(joy);
  1287. }
  1288. }
  1289. break;
  1290. case wiimote_state::CLASSIC:
  1291. case wiimote_state::GH3_GHWT_GUITAR:
  1292. case wiimote_state::GHWT_DRUMS:
  1293. {
  1294. // buttons:
  1295. WORD bits = *(WORD*)(buff+offset+4);
  1296. bits = ~bits; // need to invert bits since 0 is down, and 1 is up
  1297. if(bits != Internal.ClassicController.Button.Bits)
  1298. changed |= CLASSIC_BUTTONS_CHANGED;
  1299. Internal.ClassicController.Button.Bits = bits;
  1300. // joysticks:
  1301. wiimote_state::joystick &joyL = Internal.ClassicController.JoystickL;
  1302. wiimote_state::joystick &joyR = Internal.ClassicController.JoystickR;
  1303. float l_raw_x = (float) (buff[offset+0] & 0x3f);
  1304. float l_raw_y = (float) (buff[offset+1] & 0x3f);
  1305. float r_raw_x = (float)((buff[offset+2] >> 7) |
  1306. ((buff[offset+1] & 0xc0) >> 5) |
  1307. ((buff[offset+0] & 0xc0) >> 3));
  1308. float r_raw_y = (float) (buff[offset+2] & 0x1f);
  1309. if((joyL.RawX != l_raw_x) || (joyL.RawY != l_raw_y))
  1310. changed |= CLASSIC_JOYSTICK_L_CHANGED;
  1311. if((joyR.RawX != r_raw_x) || (joyR.RawY != r_raw_y))
  1312. changed |= CLASSIC_JOYSTICK_R_CHANGED;
  1313. joyL.RawX = l_raw_x; joyL.RawY = l_raw_y;
  1314. joyR.RawX = r_raw_x; joyR.RawY = r_raw_y;
  1315. // apply calibration
  1316. wiimote_state::classic_controller::calibration_info &calib =
  1317. Internal.ClassicController.CalibrationInfo;
  1318. if(calib.MaxXL != 0x00)
  1319. joyL.X = (joyL.RawX - calib.MidXL) / ((float)calib.MaxXL - calib.MinXL);
  1320. if(calib.MaxYL != 0x00)
  1321. joyL.Y = (joyL.RawY - calib.MidYL) / ((float)calib.MaxYL - calib.MinYL);
  1322. if(calib.MaxXR != 0x00)
  1323. joyR.X = (joyR.RawX - calib.MidXR) / ((float)calib.MaxXR - calib.MinXR);
  1324. if(calib.MaxYR != 0x00)
  1325. joyR.Y = (joyR.RawY - calib.MidYR) / ((float)calib.MaxYR - calib.MinYR);
  1326. // i prefer the joystick outputs to range -1 - +1 (note this also affects
  1327. // the deadzone calculations)
  1328. joyL.X *= 2; joyL.Y *= 2; joyR.X *= 2; joyR.Y *= 2;
  1329. // apply the public deadzones to the internal state (if set)
  1330. joyL.DeadZone = ClassicController.JoystickL.DeadZone;
  1331. joyR.DeadZone = ClassicController.JoystickR.DeadZone;
  1332. ApplyJoystickDeadZones(joyL);
  1333. ApplyJoystickDeadZones(joyR);
  1334. // triggers
  1335. BYTE raw_trigger_l = ((buff[offset+2] & 0x60) >> 2) |
  1336. (buff[offset+3] >> 5);
  1337. BYTE raw_trigger_r = buff[offset+3] & 0x1f;
  1338. if((raw_trigger_l != Internal.ClassicController.RawTriggerL) ||
  1339. (raw_trigger_r != Internal.ClassicController.RawTriggerR))
  1340. changed |= CLASSIC_TRIGGERS_CHANGED;
  1341. Internal.ClassicController.RawTriggerL = raw_trigger_l;
  1342. Internal.ClassicController.RawTriggerR = raw_trigger_r;
  1343. if(calib.MaxTriggerL != 0x00)
  1344. Internal.ClassicController.TriggerL =
  1345. (float)Internal.ClassicController.RawTriggerL /
  1346. ((float)calib.MaxTriggerL - calib.MinTriggerL);
  1347. if(calib.MaxTriggerR != 0x00)
  1348. Internal.ClassicController.TriggerR =
  1349. (float)Internal.ClassicController.RawTriggerR /
  1350. ((float)calib.MaxTriggerR - calib.MinTriggerR);
  1351. }
  1352. break;
  1353. case BALANCE_BOARD:
  1354. {
  1355. wiimote_state::balance_board::sensors_raw prev_raw =
  1356. Internal.BalanceBoard.Raw;
  1357. Internal.BalanceBoard.Raw.TopR =
  1358. (short)((short)buff[offset+0] << 8 | buff[offset+1]);
  1359. Internal.BalanceBoard.Raw.BottomR =
  1360. (short)((short)buff[offset+2] << 8 | buff[offset+3]);
  1361. Internal.BalanceBoard.Raw.TopL =
  1362. (short)((short)buff[offset+4] << 8 | buff[offset+5]);
  1363. Internal.BalanceBoard.Raw.BottomL =
  1364. (short)((short)buff[offset+6] << 8 | buff[offset+7]);
  1365. if((Internal.BalanceBoard.Raw.TopL != prev_raw.TopL) ||
  1366. (Internal.BalanceBoard.Raw.TopR != prev_raw.TopR) ||
  1367. (Internal.BalanceBoard.Raw.BottomL != prev_raw.BottomL) ||
  1368. (Internal.BalanceBoard.Raw.BottomR != prev_raw.BottomR))
  1369. changed |= BALANCE_WEIGHT_CHANGED;
  1370. Internal.BalanceBoard.Kg.TopL =
  1371. GetBalanceValue(Internal.BalanceBoard.Raw.TopL,
  1372. Internal.BalanceBoard.CalibrationInfo.Kg0 .TopL,
  1373. Internal.BalanceBoard.CalibrationInfo.Kg17.TopL,
  1374. Internal.BalanceBoard.CalibrationInfo.Kg34.TopL);
  1375. Internal.BalanceBoar

Large files files are truncated, but you can click here to view the full file