import { type IKeysManager } from "../../common/keys/IKeysManager";
type KeyHandler = () => void;
/**
 * Реализация `IKeysManager` поверх нативных CCMP-событий `keyDown`/`keyUp`.
 *
 * Контрактные тонкости (зеркало RageMP `mp.keys.bind`):
 *  - `keyHold = false` → handler срабатывает на **нажатие** (`keyDown`).
 *  - `keyHold = true`  → handler срабатывает на **отпускание** (`keyUp`).
 *    Несмотря на название параметра, это НЕ "только когда зажато",
 *    это "при отпускании после нажатия". См. RageMP wiki.
 *
 * Архитектура:
 *  - Делаем **одну** глобальную подписку на `ccmp.on('keyDown')` и одну
 *    на `keyUp` в конструкторе. Дальше сами роутим по `key`-коду через
 *    `Map<key, { down: Set, up: Set }>`. Это намного эффективнее, чем
 *    регистрировать новый `ccmp.on` на каждый `bind`, и убирает проблему
 *    отсутствующего у CCMP `ccmp.off` (наш dispatcher остаётся в силе,
 *    меняется только содержимое внутреннего реестра).
 *  - Параллельно ведём `_pressedKeys: Set<number>` — текущее состояние,
 *    чтобы `isDown`/`isUp` отвечали синхронно (у CCMP нет API запроса
 *    состояния клавиши).
 *
 * Auto-repeat: если CCMP-клиент эмитит `keyDown` повторно при удержании
 * (как WM_KEYDOWN auto-repeat), то и наши `down`-handler'ы будут срабатывать
 * повторно — это зеркало RageMP-поведения. Не маскируем намеренно: gamemod-
 * консьюмеры писались под такую семантику.
 *
 * Snapshot при диспатче (`[...binding.down]`) защищает от модификации Set
 * из handler'а — `bind`/`unbind` внутри callback'а не ломают текущую итерацию.
 */
export declare class CCMPKeysManager implements IKeysManager {
    private readonly _pressedKeys;
    private readonly _bindings;
    constructor();
    isDown(key: number): boolean;
    isUp(key: number): boolean;
    bind(key: number, keyHold: boolean, handler: KeyHandler): void;
    unbind(key: number, keyHold: boolean, handler?: KeyHandler): void;
    private _dispatch;
}
export {};
