Files
totk-pc-recomp/runtime/input_map.h
T
Wintego cb628b08b7 Add keyboard and mouse button prompts (prompts = pc)
The Button prompts row gets a third choice, PC: every button glyph of the game's button font and every image of the
button guide is drawn as the key or mouse button the keyboard defaults press it with (E, Shift, Space, left click, Q,
R, right click, F, Tab, M, 1 to 4, W A S D, Ctrl, C) and the right stick as the mouse, whatever the pad layout. The
setup writes romfs-patches-pc next to the Xbox folders (make_romfs_patches.py --prompts pc, button_icons.py pc_icons);
--binds <totk.ini> draws the keys of its bind_ lines instead. At a game resolution of another shape the game takes
romfs-patches-pc-<w>x<h> (--prompts pc --aspect) in place of romfs-patches-<w>x<h> when it is there.

(cherry picked from commit 3f81cf02be664100bd9c569c53588d620d90b527)
2026-11-04 08:36:28 +03:00

144 lines
10 KiB
C

// 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 <stddef.h>
#include <stdint.h>
// 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:<name>" and
// "key:<name>" 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_<action> settings (cfg_get) over the defaults: at start, and again when one of them changes.
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-<w>x<h>
// 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);