mirror of
https://github.com/tangerineturtle1/NX2navi.git
synced 2026-11-15 19:19:58 -04:00
With NX2NAVI_RUMBLE_HD=1 (live, off by default) the gamepad the game reads, when it is a Switch Pro Controller, a single Joy-Con or a pair of them, gets each side's low and high band with its own frequency and amplitude in the controller's rumble report, and no dual-motor call any more. A Pro Controller and a single Joy-Con take the report through SDL's effect call, over USB and Bluetooth; SDL refuses that call for a combined pair, so a pair's reports are written to its two HID devices directly. Every other gamepad, the Switch 2 controllers included, keeps the motors. The game's thread posts its newest values and a thread of input.c writes the reports: at most one every 15 ms (rumble_hd_ms), a held value again every 30 ms, and the neutral value when the game stops sending for 250 ms, when another gamepad takes over, when the setting is turned off and before the process ends. The rumble strength scales the amplitude and rumble = 0 silences this path as well. With the setting off nothing is written to the controller: the log line at the take names it by its ids, and the first report is the trial. Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
129 lines
9.9 KiB
C
129 lines
9.9 KiB
C
// Host input sampling (runtime/input.c, with input_win.c or input_sdl.c): SDL3 gamepad and the keyboard as one Switch npad.
|
|
#pragma once
|
|
#include <stdint.h>
|
|
|
|
#include "rumble_hd.h"
|
|
|
|
// Bits of HidNpadButton (A=0 B=1 X=2 Y=3 StickL=4 StickR=5 L=6 R=7 ZL=8 ZR=9 Plus=10 Minus=11 Left=12 Up=13 Right=14
|
|
// Down=15) and the stick directions (StickLLeft..Down 16-19, StickRLeft..Down 20-23, derived from the sticks).
|
|
// Sticks are -32767..32767, up is +y. NX2NAVI_INPUT=none reports no input. Callable from any thread.
|
|
void input_poll(uint64_t *buttons, int32_t *lx, int32_t *ly, int32_t *rx, int32_t *ry);
|
|
// 1 when keyboard and mouse are the device the player used last, 0 when the gamepad is: what is shown for pointing
|
|
// (menu_cursor.c shows the pointer in the game's menus only for the keyboard and mouse)
|
|
int input_kbm_last(void);
|
|
// Direct mouse input is consumed by the game's camera-input update, after its stick response. While that update
|
|
// accepts camera input, the HID right stick contains only the controller and keyboard. Selectors route mouse input
|
|
// separately: a persistent radial cursor for the ability wheel, discrete steps for horizontal quick lists.
|
|
int input_direct_mouse_active(void);
|
|
int input_direct_mouse_enabled(void);
|
|
int input_direct_mouse_available(void); // the game version has the patch set and it is on: the mode can be chosen
|
|
// Ocarina 1.1.1's camera input update (mouse_direct.c camera_input_oot; recomp/patches-v111.txt). Measured with a
|
|
// scripted stick, its processed stick at one turns the camera 1 degree up or down and 1.78 degrees sideways
|
|
// (StickSensitivityLat2LngRatio) per 60 Hz frame; the game's own camera sensitivity option scales both.
|
|
// INPUT_OOT_COUNT_X and _Y, what one mouse count adds to the processed stick per frame at sensitivity 1, make a count
|
|
// 0.08 degrees on either axis.
|
|
#define INPUT_OOT_COUNT_X (0.08 / 1.78)
|
|
#define INPUT_OOT_COUNT_Y 0.08
|
|
// A radial selector uses a persistent virtual cursor while its opening button is held.
|
|
void input_direct_mouse_radial(int32_t dx, int32_t dy, float sensitivity, float cursor[2], int32_t out[2]);
|
|
// Horizontal quick selectors receive short, separated steps instead of a latched radial-cursor direction.
|
|
typedef struct { float distance; uint64_t pulse_until, neutral_until; int direction; } InputMouseRow;
|
|
void input_direct_mouse_row(int32_t dx, float sensitivity, uint64_t now, InputMouseRow *row, int32_t out[2]);
|
|
int input_direct_mouse_selector_active(void);
|
|
// Consume direct mouse counts, including one-shot MOUSE events from an input script. enabled=0 discards scripted
|
|
// counts while leaving the real mouse for menu input. Live counts require cursor capture and foreground focus.
|
|
void input_direct_mouse_sample(int enabled, int32_t *dx, int32_t *dy);
|
|
// menu_cursor.c: the pointer in the game's menus (1.4.3). poll: once per pad read, adds a click's A or B; pointer: a
|
|
// menu has the pointer, so the mouse camera's capture is released
|
|
void menu_cursor_poll(uint64_t *buttons);
|
|
int menu_cursor_pointer(void);
|
|
// The same for host UI (the text entry): never blocked. input_block(1) while a text entry is open: input_poll then
|
|
// reports nothing; input_block(0) when it closes: nothing until all buttons and stick directions are released.
|
|
void input_poll_ui(uint64_t *buttons, int32_t *lx, int32_t *ly, int32_t *rx, int32_t *ry);
|
|
void input_block(int on);
|
|
// The in-game menu (ui/menu.h): input_menu(1) when it opens (input_poll reports nothing, the cursor is released), (0) when
|
|
// it closes (nothing until every button and stick direction is released). Independent of input_block.
|
|
void input_menu(int open);
|
|
// The gamepad alone, for the menu: Switch button bits and the left stick (up +y). 0 without a connected gamepad.
|
|
// Minus+Plus combo and the neutral pad while the menu is open (input_map.c, window-system free): *held is the caller's memory
|
|
// of the combo, sticks are lx, ly, rx, ry. Returns 1 in the poll where the combo goes down (toggle the menu once).
|
|
int input_menu_gate(int *held, int menu_open, uint64_t *buttons, int32_t sticks[4]);
|
|
int input_overlay_gate(int *held, uint64_t *buttons); // L3+R3: 1 in the poll both go down
|
|
int input_pad_read(uint64_t *buttons, int32_t *lx, int32_t *ly);
|
|
// The gamepad layout in effect (PAD_LAYOUT_*, input_map.h; NX2NAVI_PAD_LAYOUT, live); -1 without a gamepad. Any thread.
|
|
int input_pad_layout(void);
|
|
// How far the open gamepad's left (pct[0]) and right (pct[1]) sticks are from the centre now, in percent of full
|
|
// deflection, before the dead zone: the menu shows it on the dead zone rows, so a drifting stick's resting value can be
|
|
// read off. 0 (and both 0) without a connected gamepad. Any thread.
|
|
int input_pad_stick_percent(double pct[2]);
|
|
// The label on a face button of the open gamepad (input_map.h PAD_SOUTH..PAD_NORTH, as SDL reports it); PAD_LABEL_NONE for another
|
|
// button, an unknown label or no gamepad. Any thread.
|
|
enum { PAD_LABEL_NONE, PAD_LABEL_A, PAD_LABEL_B, PAD_LABEL_X, PAD_LABEL_Y, PAD_LABEL_CROSS, PAD_LABEL_CIRCLE,
|
|
PAD_LABEL_SQUARE, PAD_LABEL_TRIANGLE };
|
|
int input_pad_label(int pad);
|
|
// The menu's remapping rows (Controls tab). input_map_snapshot copies the tables in effect (after any changed NX2NAVI_BIND_* line).
|
|
// input_capture polls the keys, the mouse buttons and the gamepad for a press: reset 1 takes what is held now as the
|
|
// start (a held key counts once released and pressed again) and returns NONE; then it returns KEY or PAD with *id the
|
|
// key or pad id (input_map.h) pressed since the last call, or CANCEL for Esc. Keys count only while the game window is in front.
|
|
enum { INPUT_CAPTURE_NONE, INPUT_CAPTURE_KEY, INPUT_CAPTURE_PAD, INPUT_CAPTURE_CANCEL };
|
|
struct InputMap; // input_map.h (not included here: swkbd_win.c has button names of its own)
|
|
void input_map_snapshot(struct InputMap *m);
|
|
int input_capture(int reset, int *id);
|
|
// The character on a punctuation key (KEY_MINUS..KEY_GRAVE) in the keyboard layout in use, 0 for another key.
|
|
uint32_t input_key_char(int key);
|
|
// F8, the flight recorder's key (no KEY_ id), held while the game window is in front.
|
|
int input_flight_key(void);
|
|
// Insert, the frame dump's key (vk_dump.c, NX2NAVI_DUMP=key), held. On Windows also while another window is in front:
|
|
// a player on a gamepad often leaves the focus elsewhere. Unbound in the game's default layout.
|
|
int input_dump_key(void);
|
|
|
|
// Mouse camera and cursor (input_win.c, Win32 window thread). The window procedure calls input_window_msg first for raw
|
|
// mouse input, cursor hiding and confinement; it returns 1 with *res set when the message is handled. input_cursor_suspend(1)
|
|
// releases the cursor around an F11 window change, (0) captures it again once the client area has its new size.
|
|
void input_cursor_suspend(int on);
|
|
int input_cursor_released(void); // Esc or a non-client activation released the cursor (probe)
|
|
|
|
// Rumble: SDL_RumbleGamepad on the open gamepad, low (heavy, left) and high (light, right) motor 0..65535 for ms
|
|
// milliseconds. 1 if a gamepad with rumble took it. Callable from any thread.
|
|
int input_rumble(uint16_t low, uint16_t high, uint32_t ms);
|
|
// Motor value for a normalised amplitude: pow(a, curve) * strength, saturated to 0..65535. Pure.
|
|
uint16_t rumble_motor(float a, float curve, float strength);
|
|
// NX2NAVI_RUMBLE_TRIGGERS=1: input_rumble also drives the impulse triggers (Xbox) with the high motor's value.
|
|
int input_rumble_triggers_enabled(void);
|
|
// HD rumble (input.c "HD rumble", rumble_hd.h): the game's vibration values as they are, for a gamepad with linear
|
|
// actuators (a Switch Pro Controller, a Joy-Con or a pair of them). All callable from any thread.
|
|
// input_rumble_hd_available: the gamepad the game reads is one of these (the menu's row).
|
|
// input_rumble_hd_on: ... and NX2NAVI_RUMBLE_HD is 1 (live) and NX2NAVI_RUMBLE is not 0: input_rumble then leaves that
|
|
// gamepad's motors alone, and the caller posts every new value with input_rumble_hd.
|
|
// input_rumble_hd: the game's newest value per side (v[0] left, v[1] right; live[s] 0: that side is at rest), the
|
|
// amplitudes scaled by strength (1 = as they are). Returns at once: a thread of input.c writes the reports, the newest
|
|
// value only, and the neutral value once the game stops posting.
|
|
// input_rumble_hd_stop: before the process ends, the neutral value for a controller that is still vibrating.
|
|
int input_rumble_hd_available(void);
|
|
int input_rumble_hd_on(void);
|
|
void input_rumble_hd(const RumbleHdValue v[2], const int live[2], float strength);
|
|
void input_rumble_hd_stop(void);
|
|
|
|
// Motion (NX2NAVI_MOTION=0 turns it off, live). input_motion_start starts the sensor thread once: every 4 ms it reads the
|
|
// gamepad's gyro and accelerometer (when it has them) and feeds motion_global(). Callable from any thread.
|
|
void input_motion_start(void);
|
|
// NX2NAVI_MOTION is not "0" (a live setting: read again when cfg_version changes). Off: the sensor reads at rest.
|
|
int input_motion_enabled(void);
|
|
// SDL sensor values (accelerometer m/s^2, gyro rad/s, SDL gamepad axes) to the Switch controller frame motion_feed
|
|
// takes (acceleration in G as gravity reads, rotation rad/s). The mapping is documented in input_win.c.
|
|
void input_motion_axes(const float sdl_acc[3], const float sdl_gyro[3], double acc_g[3], double gyro[3]);
|
|
|
|
// motion_fusion.c: nn::hid::SixAxisSensorState and the sensor fusion that produces it.
|
|
typedef struct {
|
|
int64_t delta_ns, sampling;
|
|
float acc[3], gyro[3], angle[3], dir[9]; // G, revolutions/s, revolutions, controller axes in the reference frame
|
|
uint32_t attributes, reserved; // bit 0 connected
|
|
} MotionState;
|
|
_Static_assert(sizeof(MotionState) == 0x60, "SixAxisSensorState ABI");
|
|
typedef struct MotionFusion MotionFusion;
|
|
MotionFusion *motion_new(void);
|
|
MotionFusion *motion_global(void); // the gamepad's, fed by input_win.c
|
|
void motion_feed(MotionFusion *m, const double acc_g[3], const double gyro_rad_s[3], uint64_t now_ns);
|
|
int motion_read(MotionFusion *m, MotionState *out, int max, uint64_t now_ns); // newest first; 0 = none in 200 ms
|
|
void motion_restart(MotionFusion *m); // angle and direction start over at the next sample
|