diff --git a/nx/include/switch/services/hid.h b/nx/include/switch/services/hid.h index c85e13ca..b179a4a0 100644 --- a/nx/include/switch/services/hid.h +++ b/nx/include/switch/services/hid.h @@ -545,8 +545,8 @@ typedef struct HidControllerMAC { u64 timestamp_2; } HidControllerMAC; -/// HidControllerHeader -typedef struct HidControllerHeader { +/// HidNpadStateHeader +typedef struct HidNpadStateHeader { u32 type; u32 isHalf; u32 singleColorsDescriptor; @@ -557,9 +557,9 @@ typedef struct HidControllerHeader { u32 leftColorButtons; u32 rightColorBody; u32 rightColorButtons; -} HidControllerHeader; +} HidNpadStateHeader; -/// Info struct extracted from HidControllerHeader. +/// Info struct extracted from HidNpadStateHeader. /// Color fields are zero when not set. This can happen even when the *Set fields are set to true. typedef struct HidControllerColors { @@ -576,19 +576,30 @@ typedef struct HidControllerColors /// HidControllerLayoutHeader typedef struct HidControllerLayoutHeader { - u64 timestampTicks; - u64 numEntries; - u64 latestEntry; - u64 maxEntryIndex; + u64 timestamp_ticks; + u64 total_entries; + u64 latest_entry; + u64 max_entry; } HidControllerLayoutHeader; +/// HidNpadStateEntry +typedef struct HidNpadStateEntry { + u64 timestamp; + u64 buttons; + JoystickPosition joysticks[JOYSTICK_NUM_STICKS]; + u64 connectionState; +} HidNpadStateEntry; + +typedef HidNpadStateEntry HidNpadFullKeyState; +typedef HidNpadStateEntry HidNpadHandheldState; +typedef HidNpadStateEntry HidNpadJoyDualState; +typedef HidNpadStateEntry HidNpadJoyLeftState; +typedef HidNpadStateEntry HidNpadJoyRightState; + /// HidControllerInputEntry typedef struct HidControllerInputEntry { u64 timestamp; - u64 timestamp_2; - u64 buttons; - JoystickPosition joysticks[JOYSTICK_NUM_STICKS]; - u64 connectionState; + HidNpadStateEntry state; } HidControllerInputEntry; /// HidControllerLayout @@ -661,7 +672,7 @@ typedef struct { /// HidController typedef struct HidController { - HidControllerHeader header; + HidNpadStateHeader header; HidControllerLayout layouts[7]; HidControllerSixAxisLayout sixaxis[6]; HidControllerMisc misc; @@ -788,6 +799,12 @@ void hidGetControllerPowerInfo(HidControllerID id, HidPowerInfo *info, size_t to void hidScanInput(void); +void hidGetNpadStatesFullKey(u32 id, HidNpadFullKeyState *states, size_t count, size_t *total_out); +void hidGetNpadStatesHandheld(u32 id, HidNpadHandheldState *states, size_t count, size_t *total_out); +void hidGetNpadStatesJoyDual(u32 id, HidNpadJoyDualState *states, size_t count, size_t *total_out); +void hidGetNpadStatesJoyLeft(u32 id, HidNpadJoyLeftState *states, size_t count, size_t *total_out); +void hidGetNpadStatesJoyRight(u32 id, HidNpadJoyRightState *states, size_t count, size_t *total_out); + u64 hidKeysHeld(HidControllerID id); u64 hidKeysDown(HidControllerID id); u64 hidKeysUp(HidControllerID id); diff --git a/nx/source/services/hid.c b/nx/source/services/hid.c index 59e68e37..c5043bda 100644 --- a/nx/source/services/hid.c +++ b/nx/source/services/hid.c @@ -9,6 +9,7 @@ #include "services/applet.h" #include "services/hid.h" #include "runtime/hosversion.h" +#include "runtime/diag.h" static Service g_hidSrv; static Service g_hidIAppletResource; @@ -19,7 +20,7 @@ static HidTouchScreenEntry g_touchEntry; static HidMouseEntry *g_mouseEntry; static HidMouse g_mouse; static HidKeyboardEntry g_keyboardEntry; -static HidControllerHeader g_controllerHeaders[10]; +static HidNpadStateHeader g_controllerHeaders[10]; static HidControllerInputEntry g_controllerEntries[10]; static HidControllerSixAxisLayout g_sixaxisLayouts[10]; static HidControllerMisc g_controllerMisc[10]; @@ -238,14 +239,14 @@ void hidScanInput(void) { for (int i = 0; i < 10; i++) { HidControllerLayout *currentLayout = &sharedMem->controllers[i].layouts[g_controllerLayout[i]]; - memcpy(&g_controllerHeaders[i], &sharedMem->controllers[i].header, sizeof(HidControllerHeader)); - u64 latestControllerEntry = currentLayout->header.latestEntry; + memcpy(&g_controllerHeaders[i], &sharedMem->controllers[i].header, sizeof(HidNpadStateHeader)); + u64 latestControllerEntry = currentLayout->header.latest_entry; HidControllerInputEntry *newInputEntry = ¤tLayout->entries[latestControllerEntry]; if ((s64)(newInputEntry->timestamp - g_controllerTimestamps[i]) >= 0) { memcpy(&g_controllerEntries[i], newInputEntry, sizeof(HidControllerInputEntry)); g_controllerTimestamps[i] = newInputEntry->timestamp; - g_controllerHeld[i] |= g_controllerEntries[i].buttons; + g_controllerHeld[i] |= g_controllerEntries[i].state.buttons; } g_controllerDown[i] = (~g_controllerOld[i]) & g_controllerHeld[i]; @@ -283,7 +284,7 @@ void hidScanInput(void) { } g_controllerP1AutoID = CONTROLLER_HANDHELD; - if (g_controllerEntries[CONTROLLER_PLAYER_1].connectionState & CONTROLLER_STATE_CONNECTED) + if (g_controllerEntries[CONTROLLER_PLAYER_1].state.connectionState & CONTROLLER_STATE_CONNECTED) g_controllerP1AutoID = CONTROLLER_PLAYER_1; rwlockWriteUnlock(&g_hidLock); @@ -308,7 +309,7 @@ void hidGetControllerColors(HidControllerID id, HidControllerColors *colors) { if (id < 0 || id > 9) return; if (colors == NULL) return; - HidControllerHeader *hdr = &g_controllerHeaders[id]; + HidNpadStateHeader *hdr = &g_controllerHeaders[id]; memset(colors, 0, sizeof(HidControllerColors)); @@ -338,7 +339,7 @@ bool hidIsControllerConnected(HidControllerID id) { if (id < 0 || id > 9) return 0; rwlockReadLock(&g_hidLock); - bool flag = (g_controllerEntries[id].connectionState & CONTROLLER_STATE_CONNECTED) != 0; + bool flag = (g_controllerEntries[id].state.connectionState & CONTROLLER_STATE_CONNECTED) != 0; rwlockReadUnlock(&g_hidLock); return flag; } @@ -394,6 +395,82 @@ void hidGetControllerPowerInfo(HidControllerID id, HidPowerInfo *info, size_t to } } +static HidController *_hidNpadSharedmemGetInternalState(u32 id) { + if (id >= 0x8) id = id==0x10 ? 0x9 : 0x8; + + HidSharedMemory *sharedmem = (HidSharedMemory*)hidGetSharedmemAddr(); + if (sharedmem == NULL) return NULL; + return &sharedmem->controllers[id]; +} + +static Result _hidGetNpadStates(u32 id, u32 layout, HidNpadStateEntry *states, size_t count, size_t *total_out) { + if (total_out) *total_out = 0; + + if (id >= 0x8 && (id!=0x10 && id!=0x20)) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + HidController *npad = _hidNpadSharedmemGetInternalState(id); + if (npad == NULL) + return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + + HidControllerLayout *states_buf = &npad->layouts[layout]; + + s32 total_entries = (s32)atomic_load_explicit(&states_buf->header.max_entry, memory_order_acquire); + if (total_entries < 0) total_entries = 0; + if (total_entries > count) total_entries = count; + s32 latest_entry = (s32)atomic_load_explicit(&states_buf->header.latest_entry, memory_order_acquire); + + for (s32 i=0; i<total_entries; i++) { + s32 entrypos = (((latest_entry + 0x12) - total_entries) + i) % 0x11; + + u64 timestamp0=0, timestamp1=0; + + timestamp0 = atomic_load_explicit(&states_buf->entries[entrypos].timestamp, memory_order_acquire); + memcpy(&states[total_entries-i-1], &states_buf->entries[entrypos].state, sizeof(HidNpadStateEntry)); + timestamp1 = atomic_load_explicit(&states_buf->entries[entrypos].timestamp, memory_order_acquire); + + if (timestamp0 != timestamp1 || (i>0 && states[total_entries-i-1].timestamp - states[total_entries-i].timestamp != 1)) { + s32 tmpcount = (s32)atomic_load_explicit(&states_buf->header.max_entry, memory_order_acquire); + tmpcount = total_entries < tmpcount ? tmpcount : total_entries; + total_entries = tmpcount < count ? tmpcount : count; + latest_entry = (s32)atomic_load_explicit(&states_buf->header.latest_entry, memory_order_acquire); + + i=-1; + } + } + + if (total_out) *total_out = total_entries; + + // sdknso would handle button-bitmasking with ControlPadRestriction here. + + return 0; +} + +void hidGetNpadStatesFullKey(u32 id, HidNpadFullKeyState *states, size_t count, size_t *total_out) { + Result rc = _hidGetNpadStates(id, 0, states, count, total_out); + if (R_FAILED(rc)) diagAbortWithResult(rc); +} + +void hidGetNpadStatesHandheld(u32 id, HidNpadHandheldState *states, size_t count, size_t *total_out) { + Result rc = _hidGetNpadStates(id, 1, states, count, total_out); + if (R_FAILED(rc)) diagAbortWithResult(rc); +} + +void hidGetNpadStatesJoyDual(u32 id, HidNpadJoyDualState *states, size_t count, size_t *total_out) { + Result rc = _hidGetNpadStates(id, 2, states, count, total_out); + if (R_FAILED(rc)) diagAbortWithResult(rc); +} + +void hidGetNpadStatesJoyLeft(u32 id, HidNpadJoyLeftState *states, size_t count, size_t *total_out) { + Result rc = _hidGetNpadStates(id, 3, states, count, total_out); + if (R_FAILED(rc)) diagAbortWithResult(rc); +} + +void hidGetNpadStatesJoyRight(u32 id, HidNpadJoyRightState *states, size_t count, size_t *total_out) { + Result rc = _hidGetNpadStates(id, 4, states, count, total_out); + if (R_FAILED(rc)) diagAbortWithResult(rc); +} + u64 hidKeysHeld(HidControllerID id) { if (id==CONTROLLER_P1_AUTO) return hidKeysHeld(g_controllerP1AutoID); if (id < 0 || id > 9) return 0; @@ -569,8 +646,8 @@ void hidJoystickRead(JoystickPosition *pos, HidControllerID id, HidControllerJoy } rwlockReadLock(&g_hidLock); - pos->dx = g_controllerEntries[id].joysticks[stick].dx; - pos->dy = g_controllerEntries[id].joysticks[stick].dy; + pos->dx = g_controllerEntries[id].state.joysticks[stick].dx; + pos->dy = g_controllerEntries[id].state.joysticks[stick].dy; rwlockReadUnlock(&g_hidLock); } }