@@ -318,7 +318,9 @@ class WindowsGamepadService {
318
318
void Shutdown ();
319
319
// Parse gamepad input from a WM_INPUT message.
320
320
bool HandleRawInput (HRAWINPUT handle);
321
-
321
+ void SetLightIndicatorColor (uint32_t aControllerIdx, uint32_t aLightIndex,
322
+ uint8_t aRed, uint8_t aGreen, uint8_t aBlue);
323
+ size_t WriteOutputReport (const std::vector<uint8_t >& aReport);
322
324
static void XInputMessageLoopOnceCallback (nsITimer* aTimer, void * aClosure);
323
325
static void DevicesChangeCallback (nsITimer* aTimer, void * aService);
324
326
@@ -342,6 +344,7 @@ class WindowsGamepadService {
342
344
nsTArray<Gamepad> mGamepads ;
343
345
344
346
HIDLoader mHID ;
347
+ nsAutoHandle mHidHandle ;
345
348
XInputLoader mXInput ;
346
349
347
350
nsCOMPtr<nsITimer> mDirectInputTimer ;
@@ -434,7 +437,7 @@ bool WindowsGamepadService::ScanForXInputDevices() {
434
437
gamepad.state = state;
435
438
gamepad.id = service->AddGamepad (
436
439
" xinput" , GamepadMappingType::Standard, GamepadHand::_empty,
437
- kStandardGamepadButtons , kStandardGamepadAxes ,
440
+ kStandardGamepadButtons , kStandardGamepadAxes , 0 , 0 ,
438
441
0 ); // TODO: Bug 680289, implement gamepad haptics for Windows.
439
442
mGamepads .AppendElement (gamepad);
440
443
}
@@ -625,18 +628,19 @@ bool WindowsGamepadService::GetRawGamepad(HANDLE handle) {
625
628
wchar_t name[128 ] = {0 };
626
629
size = sizeof (name);
627
630
nsTArray<char > gamepad_name;
628
- const HANDLE hid_handle = CreateFile (
629
- devname .Elements (), GENERIC_READ | GENERIC_WRITE,
630
- FILE_SHARE_READ | FILE_SHARE_WRITE, NULL , OPEN_EXISTING, 0 , NULL );
631
- if (hid_handle) {
632
- if (mHID .mHidD_GetProductString (hid_handle, &name, size)) {
631
+ // Creating this file with FILE_FLAG_OVERLAPPED to perform
632
+ // an asynchronous request in WriteOutputReport.
633
+ mHidHandle .own (CreateFile (devname .Elements (), GENERIC_READ | GENERIC_WRITE,
634
+ FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr ,
635
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, nullptr ));
636
+ if (mHidHandle != INVALID_HANDLE_VALUE) {
637
+ if (mHID .mHidD_GetProductString (mHidHandle , &name, size)) {
633
638
int bytes = WideCharToMultiByte (CP_UTF8, 0 , name, -1 , nullptr , 0 , nullptr ,
634
639
nullptr );
635
640
gamepad_name.SetLength (bytes);
636
641
WideCharToMultiByte (CP_UTF8, 0 , name, -1 , gamepad_name.Elements (), bytes,
637
642
nullptr , nullptr );
638
643
}
639
- CloseHandle (hid_handle);
640
644
}
641
645
if (gamepad_name.Length () == 0 || !gamepad_name[0 ]) {
642
646
const char kUnknown [] = " Unknown Gamepad" ;
@@ -717,9 +721,21 @@ bool WindowsGamepadService::GetRawGamepad(HANDLE handle) {
717
721
}
718
722
719
723
gamepad.remapper = remapper.forget ();
724
+ // TODO: Bug 680289, implement gamepad haptics for Windows.
720
725
gamepad.id = service->AddGamepad (
721
726
gamepad_id, gamepad.remapper ->GetMappingType (), GamepadHand::_empty,
722
- gamepad.remapper ->GetButtonCount (), gamepad.remapper ->GetAxisCount (), 0 );
727
+ gamepad.remapper ->GetButtonCount (), gamepad.remapper ->GetAxisCount (), 0 ,
728
+ gamepad.remapper ->GetLightIndicatorCount (),
729
+ gamepad.remapper ->GetTouchEventCount ());
730
+
731
+ nsTArray<GamepadLightIndicatorType> lightTypes;
732
+ gamepad.remapper ->GetLightIndicators (lightTypes);
733
+ for (uint32_t i = 0 ; i < lightTypes.Length (); ++i) {
734
+ if (lightTypes[i] != GamepadLightIndicator::DefaultType ()) {
735
+ service->NewLightIndicatorTypeEvent (gamepad.id , i, lightTypes[i]);
736
+ }
737
+ }
738
+
723
739
mGamepads .AppendElement (gamepad);
724
740
return true ;
725
741
}
@@ -833,9 +849,81 @@ bool WindowsGamepadService::HandleRawInput(HRAWINPUT handle) {
833
849
}
834
850
}
835
851
852
+ BYTE* rawData = raw->data .hid .bRawData ;
853
+ gamepad->remapper ->GetTouchData (gamepad->id , rawData);
854
+
836
855
return true ;
837
856
}
838
857
858
+ void WindowsGamepadService::SetLightIndicatorColor (uint32_t aControllerIdx,
859
+ uint32_t aLightColorIndex,
860
+ uint8_t aRed, uint8_t aGreen,
861
+ uint8_t aBlue) {
862
+ // We get aControllerIdx from GamepadPlatformService::AddGamepad(),
863
+ // It begins from 1 and is stored at Gamepad.id.
864
+ const Gamepad* gamepad = nullptr ;
865
+ for (const auto & pad : mGamepads ) {
866
+ if (pad.id == aControllerIdx) {
867
+ gamepad = &pad;
868
+ break ;
869
+ }
870
+ }
871
+ if (!gamepad) {
872
+ MOZ_ASSERT (false );
873
+ return ;
874
+ }
875
+
876
+ RefPtr<GamepadRemapper> remapper = gamepad->remapper ;
877
+ if (!remapper || remapper->GetLightIndicatorCount () <= aLightColorIndex) {
878
+ MOZ_ASSERT (false );
879
+ return ;
880
+ }
881
+
882
+ std::vector<uint8_t > report;
883
+ remapper->GetLightColorReport (aRed, aGreen, aBlue, report);
884
+ WriteOutputReport (report);
885
+ }
886
+
887
+ size_t WindowsGamepadService::WriteOutputReport (
888
+ const std::vector<uint8_t >& aReport) {
889
+ DCHECK (static_cast <const void *>(aReport.data ()));
890
+ DCHECK_GE (aReport.size (), 1U );
891
+ if (!mHidHandle ) return 0 ;
892
+
893
+ nsAutoHandle eventHandle (::CreateEvent (nullptr , FALSE , FALSE , nullptr ));
894
+ OVERLAPPED overlapped = {0 };
895
+ overlapped.hEvent = eventHandle;
896
+
897
+ // Doing an asynchronous write to allows us to time out
898
+ // if the write takes too long.
899
+ DWORD bytesWritten = 0 ;
900
+ BOOL writeSuccess =
901
+ ::WriteFile (mHidHandle , static_cast <const void *>(aReport.data()),
902
+ aReport.size(), &bytesWritten, &overlapped);
903
+ if (!writeSuccess) {
904
+ DWORD error = ::GetLastError ();
905
+ if (error == ERROR_IO_PENDING) {
906
+ // Wait for the write to complete. This causes WriteOutputReport to behave
907
+ // synchronously but with a timeout.
908
+ DWORD wait_object = ::WaitForSingleObject (overlapped.hEvent , 100 );
909
+ if (wait_object == WAIT_OBJECT_0) {
910
+ if (!::GetOverlappedResult (mHidHandle , &overlapped, &bytesWritten,
911
+ TRUE )) {
912
+ return 0 ;
913
+ }
914
+ } else {
915
+ // Wait failed, or the timeout was exceeded before the write completed.
916
+ // Cancel the write request.
917
+ if (::CancelIo (mHidHandle )) {
918
+ wait_object = ::WaitForSingleObject (overlapped.hEvent , INFINITE);
919
+ MOZ_ASSERT (wait_object == WAIT_OBJECT_0);
920
+ }
921
+ }
922
+ }
923
+ }
924
+ return writeSuccess ? bytesWritten : 0 ;
925
+ }
926
+
839
927
void WindowsGamepadService::Startup () { ScanForDevices (); }
840
928
841
929
void WindowsGamepadService::Shutdown () { Cleanup (); }
@@ -851,6 +939,7 @@ void WindowsGamepadService::Cleanup() {
851
939
if (mDeviceChangeTimer ) {
852
940
mDeviceChangeTimer ->Cancel ();
853
941
}
942
+
854
943
mGamepads .Clear ();
855
944
}
856
945
@@ -1001,5 +1090,16 @@ void StopGamepadMonitoring() {
1001
1090
gMonitorThread = nullptr ;
1002
1091
}
1003
1092
1093
+ void SetGamepadLightIndicatorColor (uint32_t aControllerIdx,
1094
+ uint32_t aLightColorIndex, uint8_t aRed,
1095
+ uint8_t aGreen, uint8_t aBlue) {
1096
+ MOZ_ASSERT (gService );
1097
+ if (!gService ) {
1098
+ return ;
1099
+ }
1100
+ gService ->SetLightIndicatorColor (aControllerIdx, aLightColorIndex, aRed,
1101
+ aGreen, aBlue);
1102
+ }
1103
+
1004
1104
} // namespace dom
1005
1105
} // namespace mozilla
0 commit comments