Skip to content

Commit a6af133

Browse files
committed
Bug 1523351 - Part 4: DS4 GamepadTouch and GamepadLightIndicator implementation in Windows. r=aklotz,baku
These should both be blocking reviewers. Differential Revision: https://phabricator.services.mozilla.com/D29291 --HG-- extra : moz-landing-system : lando
1 parent c41523f commit a6af133

File tree

3 files changed

+216
-9
lines changed

3 files changed

+216
-9
lines changed

dom/gamepad/GamepadRemapping.cpp

+97
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ enum CanonicalAxisIndex {
4747
AXIS_INDEX_COUNT
4848
};
4949

50+
float NormalizeTouch(long aValue, long aMin, long aMax) {
51+
return (2.f * (aValue - aMin) / static_cast<float>(aMax - aMin)) - 1.f;
52+
}
53+
5054
bool AxisNegativeAsButton(float input) {
5155
const float value = (input < -0.5f) ? 1.f : 0.f;
5256
return value > 0.1f;
@@ -363,6 +367,92 @@ class Dualshock4Remapper final : public GamepadRemapper {
363367
return DUALSHOCK_BUTTON_COUNT;
364368
}
365369

370+
virtual uint32_t GetLightIndicatorCount() const override {
371+
return LIGHT_INDICATOR_COUNT;
372+
}
373+
374+
virtual void GetLightIndicators(
375+
nsTArray<GamepadLightIndicatorType>& aTypes) const override {
376+
const uint32_t len = GetLightIndicatorCount();
377+
aTypes.SetLength(len);
378+
for (uint32_t i = 0; i < len; ++i) {
379+
aTypes[i] = GamepadLightIndicatorType::Rgb;
380+
}
381+
}
382+
383+
virtual uint32_t GetTouchEventCount() const override {
384+
return TOUCH_EVENT_COUNT;
385+
}
386+
387+
virtual void GetLightColorReport(
388+
uint8_t aRed, uint8_t aGreen, uint8_t aBlue,
389+
std::vector<uint8_t>& aReport) const override {
390+
const size_t report_length = 32;
391+
aReport.resize(report_length);
392+
aReport.assign(report_length, 0);
393+
394+
aReport[0] = 0x05; // report ID USB only
395+
aReport[1] = 0x02; // LED only
396+
aReport[6] = aRed;
397+
aReport[7] = aGreen;
398+
aReport[8] = aBlue;
399+
}
400+
401+
virtual void GetTouchData(uint32_t aIndex, void* aInput) override {
402+
nsTArray<GamepadTouchState> touches(TOUCH_EVENT_COUNT);
403+
touches.SetLength(TOUCH_EVENT_COUNT);
404+
uint8_t* rawData = (uint8_t*)aInput;
405+
406+
bool touch0Pressed = (((rawData[35] & 0xff) >> 7) == 0);
407+
bool touch1Pressed = (((rawData[39] & 0xff) >> 7) == 0);
408+
409+
if (!touch0Pressed && !touch1Pressed) {
410+
return;
411+
}
412+
413+
if ((touch0Pressed && (rawData[35] & 0xff) < mLastTouch0Id) ||
414+
(touch1Pressed && (rawData[39] & 0xff) < mLastTouch1Id)) {
415+
mTouchIdBase += 128;
416+
}
417+
418+
const uint32_t kTouchDimensionX = 1920;
419+
const uint32_t kTouchDimensionY = 942;
420+
421+
touches[0].touchId = mTouchIdBase + (rawData[35] & 0x7f);
422+
touches[0].surfaceId = 0;
423+
touches[0].position[0] = NormalizeTouch(
424+
((rawData[37] & 0xf) << 8) | rawData[36], 0, (kTouchDimensionX - 1));
425+
touches[0].position[1] =
426+
NormalizeTouch(rawData[38] << 4 | ((rawData[37] & 0xf0) >> 4), 0,
427+
(kTouchDimensionY - 1));
428+
touches[0].surfaceDimensions[0] = kTouchDimensionX;
429+
touches[0].surfaceDimensions[1] = kTouchDimensionY;
430+
touches[0].isSurfaceDimensionsValid = true;
431+
mLastTouch0Id = rawData[35] & 0x7f;
432+
433+
touches[1].touchId = mTouchIdBase + (rawData[39] & 0x7f);
434+
touches[1].surfaceId = 0;
435+
touches[1].position[0] =
436+
NormalizeTouch(((rawData[41] & 0xf) << 8) | rawData[40] + 1, 0,
437+
(kTouchDimensionX - 1));
438+
touches[1].position[1] =
439+
NormalizeTouch(rawData[42] << 4 | ((rawData[41] & 0xf0) >> 4), 0,
440+
(kTouchDimensionY - 1));
441+
touches[1].surfaceDimensions[0] = kTouchDimensionX;
442+
touches[1].surfaceDimensions[1] = kTouchDimensionY;
443+
touches[1].isSurfaceDimensionsValid = true;
444+
mLastTouch1Id = rawData[39] & 0x7f;
445+
446+
RefPtr<GamepadPlatformService> service =
447+
GamepadPlatformService::GetParentService();
448+
if (!service) {
449+
return;
450+
}
451+
452+
service->NewMultiTouchEvent(aIndex, 0, touches[0]);
453+
service->NewMultiTouchEvent(aIndex, 1, touches[1]);
454+
}
455+
366456
virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
367457
double aValue) const override {
368458
RefPtr<GamepadPlatformService> service =
@@ -443,6 +533,13 @@ class Dualshock4Remapper final : public GamepadRemapper {
443533
DUALSHOCK_BUTTON_TOUCHPAD = BUTTON_INDEX_COUNT,
444534
DUALSHOCK_BUTTON_COUNT
445535
};
536+
537+
static const uint32_t LIGHT_INDICATOR_COUNT = 1;
538+
static const uint32_t TOUCH_EVENT_COUNT = 2;
539+
540+
unsigned long mLastTouch0Id = 0;
541+
unsigned long mLastTouch1Id = 0;
542+
unsigned long mTouchIdBase = 0;
446543
};
447544

448545
class LogitechDInputRemapper final : public GamepadRemapper {

dom/gamepad/GamepadRemapping.h

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#ifndef mozilla_dom_GamepadRemapping_h_
88
#define mozilla_dom_GamepadRemapping_h_
99

10+
#include "mozilla/dom/GamepadLightIndicator.h"
11+
1012
namespace mozilla {
1113
namespace dom {
1214

@@ -70,11 +72,19 @@ class GamepadRemapper {
7072
public:
7173
virtual uint32_t GetAxisCount() const = 0;
7274
virtual uint32_t GetButtonCount() const = 0;
75+
virtual uint32_t GetLightIndicatorCount() const { return 0; }
76+
virtual void GetLightIndicators(
77+
nsTArray<GamepadLightIndicatorType>& aTypes) const {}
78+
virtual uint32_t GetTouchEventCount() const { return 0; }
79+
virtual void GetLightColorReport(uint8_t aRed, uint8_t aGreen, uint8_t aBlue,
80+
std::vector<uint8_t>& aReport) const {}
81+
7382
virtual void SetAxisCount(uint32_t aButtonCount) {}
7483
virtual void SetButtonCount(uint32_t aButtonCount) {}
7584
virtual GamepadMappingType GetMappingType() const {
7685
return GamepadMappingType::Standard;
7786
}
87+
virtual void GetTouchData(uint32_t aIndex, void* aInput) {}
7888
virtual void RemapAxisMoveEvent(uint32_t aIndex, uint32_t aAxis,
7989
double aValue) const = 0;
8090
virtual void RemapButtonEvent(uint32_t aIndex, uint32_t aButton,

dom/gamepad/windows/WindowsGamepad.cpp

+109-9
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,9 @@ class WindowsGamepadService {
318318
void Shutdown();
319319
// Parse gamepad input from a WM_INPUT message.
320320
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);
322324
static void XInputMessageLoopOnceCallback(nsITimer* aTimer, void* aClosure);
323325
static void DevicesChangeCallback(nsITimer* aTimer, void* aService);
324326

@@ -342,6 +344,7 @@ class WindowsGamepadService {
342344
nsTArray<Gamepad> mGamepads;
343345

344346
HIDLoader mHID;
347+
nsAutoHandle mHidHandle;
345348
XInputLoader mXInput;
346349

347350
nsCOMPtr<nsITimer> mDirectInputTimer;
@@ -434,7 +437,7 @@ bool WindowsGamepadService::ScanForXInputDevices() {
434437
gamepad.state = state;
435438
gamepad.id = service->AddGamepad(
436439
"xinput", GamepadMappingType::Standard, GamepadHand::_empty,
437-
kStandardGamepadButtons, kStandardGamepadAxes,
440+
kStandardGamepadButtons, kStandardGamepadAxes, 0, 0,
438441
0); // TODO: Bug 680289, implement gamepad haptics for Windows.
439442
mGamepads.AppendElement(gamepad);
440443
}
@@ -625,18 +628,19 @@ bool WindowsGamepadService::GetRawGamepad(HANDLE handle) {
625628
wchar_t name[128] = {0};
626629
size = sizeof(name);
627630
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)) {
633638
int bytes = WideCharToMultiByte(CP_UTF8, 0, name, -1, nullptr, 0, nullptr,
634639
nullptr);
635640
gamepad_name.SetLength(bytes);
636641
WideCharToMultiByte(CP_UTF8, 0, name, -1, gamepad_name.Elements(), bytes,
637642
nullptr, nullptr);
638643
}
639-
CloseHandle(hid_handle);
640644
}
641645
if (gamepad_name.Length() == 0 || !gamepad_name[0]) {
642646
const char kUnknown[] = "Unknown Gamepad";
@@ -717,9 +721,21 @@ bool WindowsGamepadService::GetRawGamepad(HANDLE handle) {
717721
}
718722

719723
gamepad.remapper = remapper.forget();
724+
// TODO: Bug 680289, implement gamepad haptics for Windows.
720725
gamepad.id = service->AddGamepad(
721726
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+
723739
mGamepads.AppendElement(gamepad);
724740
return true;
725741
}
@@ -833,9 +849,81 @@ bool WindowsGamepadService::HandleRawInput(HRAWINPUT handle) {
833849
}
834850
}
835851

852+
BYTE* rawData = raw->data.hid.bRawData;
853+
gamepad->remapper->GetTouchData(gamepad->id, rawData);
854+
836855
return true;
837856
}
838857

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+
839927
void WindowsGamepadService::Startup() { ScanForDevices(); }
840928

841929
void WindowsGamepadService::Shutdown() { Cleanup(); }
@@ -851,6 +939,7 @@ void WindowsGamepadService::Cleanup() {
851939
if (mDeviceChangeTimer) {
852940
mDeviceChangeTimer->Cancel();
853941
}
942+
854943
mGamepads.Clear();
855944
}
856945

@@ -1001,5 +1090,16 @@ void StopGamepadMonitoring() {
10011090
gMonitorThread = nullptr;
10021091
}
10031092

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+
10041104
} // namespace dom
10051105
} // namespace mozilla

0 commit comments

Comments
 (0)