// Keyboard, mouse and gamepad bindings, free of any window system: the tables map platform-neutral key and pad ids to // Switch actions. input_win.c translates Win32 virtual keys and SDL buttons to these ids (one place); a Linux port // translates SDL scancodes the same way. The remapping rows of the menu's Controls tab (ui/binds_page.cpp) edits a copy of the tables and writes // the bind_ lines; input_win.c applies changed lines while the game runs. Layout in docs/controls.md. #pragma once #include #include // Actions are the bit numbers of libnx HidNpadButton (input.h); 16..23 are the stick directions, which keys drive as // stick deflection. B_SHAKE is a key of its own, never a button bit: a press shakes the right stick left and right for // the game (input.c shake_stick), which detaches a fused part held with Ultrahand. A pad shakes its own stick. enum { B_A = 0, B_B, B_X, B_Y, B_STICK_L, B_STICK_R, B_L, B_R, B_ZL, B_ZR, B_PLUS, B_MINUS, B_LEFT, B_UP, B_RIGHT, B_DOWN, B_SL_LEFT = 16, B_SL_UP, B_SL_RIGHT, B_SL_DOWN, B_SR_LEFT, B_SR_UP, B_SR_RIGHT, B_SR_DOWN, B_SHAKE = 24, ACTION_COUNT = 25 }; // Keys and mouse buttons. F1, F10, F11 and Esc belong to the menu and the window, F8 to the flight recorder: no ids. enum { KEY_NONE = 0, KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L, KEY_M, // letters and digits are KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z, // consecutive KEY_0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_SPACE, KEY_TAB, KEY_LSHIFT, KEY_LCTRL, KEY_UP, KEY_DOWN, KEY_LEFT, KEY_RIGHT, KEY_MOUSE_L, KEY_MOUSE_R, KEY_MOUSE_M, KEY_MOUSE_X1, KEY_MOUSE_X2, // the mouse buttons are consecutive KEY_ENTER, KEY_BACKSPACE, KEY_RSHIFT, KEY_RCTRL, KEY_LALT, KEY_CAPSLOCK, KEY_INSERT, KEY_DELETE, KEY_HOME, KEY_END, KEY_PAGEUP, KEY_PAGEDOWN, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F9, KEY_F12, KEY_NUM0, KEY_NUM1, KEY_NUM2, KEY_NUM3, KEY_NUM4, KEY_NUM5, KEY_NUM6, KEY_NUM7, KEY_NUM8, KEY_NUM9, // consecutive KEY_NUM_ADD, KEY_NUM_SUB, KEY_NUM_MUL, KEY_NUM_DIV, KEY_NUM_DECIMAL, // the punctuation keys of a US keyboard, by position; the character on the key follows the keyboard layout KEY_MINUS, KEY_EQUALS, KEY_LBRACKET, KEY_RBRACKET, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_COMMA, KEY_PERIOD, KEY_SLASH, KEY_BACKSLASH, KEY_GRAVE, KEY_COUNT }; // Gamepad inputs by position (Nintendo layout, as SDL names them), platform-neutral like the key ids. ZL and ZR are the // analog triggers; input_win.c translates SDL buttons and axes to these (one place). The paddles are the back buttons // of an Xbox Elite, DualSense Edge or Steam Controller (SDL's LEFT_PADDLE1 and so on); no Switch button has them by // default, a bind line or the remapping rows add them. enum { PAD_NONE = 0, PAD_SOUTH, PAD_EAST, PAD_WEST, PAD_NORTH, PAD_LSTICK, PAD_RSTICK, PAD_L, PAD_R, PAD_START, PAD_BACK, PAD_DPAD_LEFT, PAD_DPAD_UP, PAD_DPAD_RIGHT, PAD_DPAD_DOWN, PAD_ZL, PAD_ZR, PAD_LPADDLE1, PAD_RPADDLE1, PAD_LPADDLE2, PAD_RPADDLE2, PAD_COUNT }; // Gamepad layouts (TOTK_PAD_LAYOUT): which face button is Switch A. Nintendo: by position, east A, south B, west Y, // north X. Xbox: south A, east B, west X, north Y, as an Xbox pad labels them (cross A, circle B on a PlayStation pad). enum { PAD_LAYOUT_NINTENDO, PAD_LAYOUT_XBOX, PAD_LAYOUT_COUNT }; enum { INPUT_BINDING_MAX = 96, INPUT_PAD_BINDING_MAX = 48 }; typedef struct { uint8_t key; uint8_t action; } Binding; typedef struct { uint8_t pad; uint8_t action; } PadBinding; // The keyboard and mouse table and one gamepad table per layout. typedef struct InputMap { Binding keys[INPUT_BINDING_MAX]; int nkeys; PadBinding pads[PAD_LAYOUT_COUNT][INPUT_PAD_BINDING_MAX]; int npads[PAD_LAYOUT_COUNT]; } InputMap; // The tables in effect. Defaults are the layout in docs/controls.md; input_map_reset restores them. extern InputMap input_map; void input_map_reset(void); // The script and totk.ini names, any case when parsed; NULL for an id without one. Actions: A B X Y LCLICK RCLICK L R ZL // ZR PLUS MINUS LEFT UP RIGHT DOWN, then LSTICK_LEFT LSTICK_UP LSTICK_RIGHT LSTICK_DOWN and the same for RSTICK (the // stick directions the keys drive), then SHAKE. Keys: a-z 0-9 space tab lshift ... (key_names in input_map.c). Pads: south east ... const char *input_action_name(int action); const char *input_key_name(int key); const char *input_pad_name(int pad); // Replaces every binding of the action `button` (an action name) with `list`: comma separated "pad:" and // "key:" entries, "key:default" / "pad:default" for that part's defaults, or "none" for no input. A part the list // names neither way is left unbound. Pad names are physical buttons, so pad entries go into the gamepad table of every // layout; pad:default keeps each layout's own button. Stick directions and SHAKE take keys only. Invalid entries are logged and // skipped; when none is valid the action keeps its bindings. 1 when applied. int input_map_bind(const char *button, const char *list); // Applies the TOTK_BIND_ settings (cfg_get) over the defaults: at start, and again when one of them changes. // A key a line binds leaves the defaults of the actions that have no line. void input_map_configure(void); // The layout for a TOTK_PAD_LAYOUT value: "nintendo" or "xbox" (any case); "xbox_prompts", 0.1.9's name for the Nintendo // layout with the Xbox prompts, maps the buttons as "nintendo". Anything else ("auto", NULL) picks Nintendo when the // pad's east button is labelled A (a Switch Pro Controller, a GuliKit) and Xbox otherwise. int input_pad_layout_pick(const char *setting, int east_labelled_a); // Button prompts (TOTK_PROMPTS): the controller whose buttons the game's prompts show. Nintendo: the game's own. Xbox: // LB RB LT RT, Menu, View and a d-pad cross for the other buttons, and the face buttons named by the label of the // button the pad layout presses (runtime/romfs.c overlay_init, tools/make_romfs_patches.py --prompts). PC: the key or // mouse button the keyboard defaults press each button with, the mouse for the right stick. enum { PROMPTS_NINTENDO, PROMPTS_XBOX, PROMPTS_PC }; // The prompts for a TOTK_PROMPTS value ("nintendo", "xbox" or "pc", any case). Unset or empty: Xbox while the pad // layout setting is "xbox_prompts", else Nintendo. int input_prompts_pick(const char *prompts, const char *pad_layout); // The folder next to romfs-index.bin that holds those prompts for a TOTK_PAD_LAYOUT value, NULL for the game's own. The // Nintendo layout plays an Xbox pad by position, so its folder names the face buttons the other way round // (romfs-patches-xbox: the right button does A and is labelled B); xbox and auto put every Switch button on the button // labelled with its letter (auto picks the layout per pad by its labels), so they share romfs-patches-xbox-label. The PC // prompts follow no pad: romfs-patches-pc whatever the layout (romfs.c overlay_init serves romfs-patches-pc-x // under that name at a game resolution of another shape). const char *input_prompts_folder(int prompts, const char *pad_layout); // down[pad] != 0 for the gamepad inputs held. Returns the Switch button bits (0..15) the layout's pad table presses. uint64_t input_map_pad_buttons(const uint8_t down[PAD_COUNT], int layout); // down[key] != 0 for the keys held. Returns the Switch button bits (0..15) the table presses. uint64_t input_map_buttons(const uint8_t down[KEY_COUNT]); // 1 when a key of action is held. int input_map_held(const uint8_t down[KEY_COUNT], int action); // out = x, y of the stick (0 left, 1 right) from its four direction actions; up is +y; a diagonal is scaled to the // unit circle. void input_map_stick(const uint8_t down[KEY_COUNT], int stick, int32_t out[2]); // Mouse motion in raw counts over dt_s seconds to right-stick deflection: full deflection at 3000 / sens counts/s. // Clamped to the unit circle, up (negative dy) is +y. void input_mouse_stick(int32_t dx, int32_t dy, double dt_s, float sens, int32_t out[2]); // A gamepad stick (x, y in -32768..32767, up is +y) through a round dead zone of `dead` (fraction of full deflection, // 0..0.5): inside the circle the stick reads 0, and the rest of the travel is scaled to start at 0 at the edge, so a // drifting stick rests at 0 and full deflection still reaches full. The result stays inside the unit square. void input_pad_stick(int32_t x, int32_t y, float dead, int32_t out[2]); // The dead zone of gamepad stick 0 (left) or 1 (right) for input_pad_stick, as a fraction of full deflection: // TOTK_LSTICK_DEADZONE or TOTK_RSTICK_DEADZONE when set, else TOTK_STICK_DEADZONE (both sticks), percent clamped to 0..50. float input_pad_deadzone(int stick); // How far a stick (x, y in -32768..32767) is from the centre, in percent of full deflection (100 at an axis's end; a // square gate's corner reads more). double input_stick_percent(int32_t x, int32_t y); // ---- the remapping rows' edits, on a copy of input_map. Pure. // action's keys (table order) / its gamepad buttons in layout, at most max; returns how many. int input_map_keys_of(const InputMap *m, int action, uint8_t *out, int max); int input_map_pads_of(const InputMap *m, int action, int layout, uint8_t *out, int max); // Key slot (0 or 1) of action becomes key; KEY_NONE clears the slot. A key another action has moves here: returns that // action, -1 when none lost it. int input_map_set_key(InputMap *m, int action, int slot, int key); // action's gamepad button in layout becomes pad; PAD_NONE clears it. The action that had pad takes action's old button // (a swap): returns it, -1 when none. A remapped button is physical, so the face buttons (A B X Y) and the changed actions // get the result in every layout: the layout setting no longer moves them. A change that puts every button of layout // back at its default restores the defaults of every layout instead. int input_map_set_pad(InputMap *m, int action, int layout, int pad); // The bind_ value that gives action its bindings of m: "" when they are the defaults (no line needed), else the entries, // with key:default / pad:default for a part at its defaults, or "none". void input_map_line(const InputMap *m, int action, char *out, size_t n);