# Ввод — `$.input`

Одна точка входа для всей игры: клавиши, мышь, геймпад, события, перенастройка
биндов. Игровой и SDK-код обращается только к `$`: приватный `engine` скрыт.
Переопределение ввода (агентский режим, ребинд) живёт здесь.

Имена клавиш — «человеческие»: `'space'`, `'w'`, `'left'`, `'escape'`, `'f1'`.
`$.input.on('key')` отдаёт **имя** клавиши (`'Space'`), а не номер скан-кода.

```js
if ($.input.down('jump')) jump();
$.input.bind('jump', ['space', 'w', 'gamepad.a']);
$.input.on('key', (e) => { if (e.key === 'Escape') $.time.pause(); });
$.input.on('mouse', (e) => { if (e.button === 1 && e.pressed) shoot(); });
```

---

## 1. Клавиши и действия

| Вызов | Смысл |
|---|---|
| `down(action)` / `pressed(action)` / `released(action)` | удержание / нажатие / отпускание |
| `axis(negative, positive)` | направление по двум действиям (-1/0/1) |
| `vec('wasd' \| 'arrows' \| 'both')` | вектор `{x, y}` готовой схемы управления |
| `bind(action, keys)` / `unbind(action)` / `bindings()` | бинды |
| `rebind(action, key)` | переназначить одно действие |
| `saveBindings()` / `loadBindings()` | сохранить и вернуть раскладку |
| `actions()` / `deadzone(value?)` | список действий / мёртвая зона стиков |
| `on(name, fn)` / `off(name, fn)` | события `key`, `mouse`, `wheel`, `text` |
| `describe()` | строка состояния для интерфейса |

## 2. Мышь и геймпад

| Вызов | Смысл |
|---|---|
| `mouse()` / `mouseDelta()` / `mouseWorld()` | позиция в окне / смещение за кадр / в мире |
| `mouseDown(button)` / `mousePressed(button)` | кнопка удерживается / нажата сейчас |
| `wheel()` | `{ x, y }` — прокрутка за кадр |
| `padDown(button)` / `padAxis(name)` | кнопка и ось ПЕРВОГО геймпада |
| `gamepad(slot)` | геймпад по номеру: `.down()`, `.pressed()`, `.axis()`, `.connected()`, `.rumble()` |
| `padCount()` / `padSlots()` | сколько подключено / сколько слотов всего |
| `rumble(opts)` / `stopRumble()` / `rumbleSupported()` | виброотклик первого геймпада |

Кнопки мыши: `1` — левая, `2` — средняя, `3` — правая.

**Геймпадов до четырёх** (`$.input.padSlots()`): локальная игра вдвоём-вчетвером
без переподключений. Слот 0 — тот же геймпад, что и у `padDown`/`padAxis`.

```js
const p1 = $.input.gamepad(0), p2 = $.input.gamepad(1);
if (p2.connected() && p2.pressed('a')) p2.rumble({ ms: 120 });
const ax = p1.axis('leftX');
```

## 3. Доступ и курсор

| Вызов | Смысл |
|---|---|
| `text()` | введённые символы за кадр (учитывает раскладку) |
| `cursor(name?)` / `cursorVisible(on?)` | форма и видимость курсора |
| `touches()` / `touchCount()` / `touch(i)` / `touched()` | касания: список, число, один палец, есть ли вообще |
| `taken()` | ввод забран катсценой (`$.cutscene`) |

## 4. Ограничения

* **слотов геймпада четыре**, но настоящих устройств может быть больше: лишние
  не открываются (`$.input.padSlots()` — предел сборки);
* **геймпад подключается к свободному слоту**: после отключения устройства слот
  освобождается, и номер слота у оставшихся НЕ меняется — не полагайтесь на
  «слот = порядок игроков», храните соответствие сами;
* **нет «tap/hold/long»**: `pressed` — один кадр, `down` — удержание; двойной
  клик и удержание собираются игрой;
* **касания — указатели, а не жесты**: движок отдаёт пальцы (позиция, сдвиг за
  кадр, давление), распознавание свайпов и щипков — на игре. Мультитач есть (до
  10 пальцев), и мышь НЕ подменяет пальцы: это разные потоки;
* **жестов и «долгого нажатия» нет**: `touch(i).dx/dy` — сдвиг за кадр;
* **IME зависит от платформы**: native editing state используется legacy
  widgets для предпросмотра композиции; финальный коммит идёт отдельно;
* **вибро зависит от платформы**: `rumbleSupported()` проверяйте перед вызовом.

## 5. Проверка

```bash
# имена клавиш доходят до обработчика, а не номера скан-кодов
python3 tests/agent/highlevel_keyname_test.py
```
