You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

142 lines
9.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#+TITLE: Keyboard Controller — план реализации
План для приложения из readme.org: HID-контроллер (клавиатура, мышь,
тачпад) поверх SuperCollider, управление Ndef/Tdef, минимальный
визуальный фидбек. Целевая платформа — Linux, ноутбуки ThinkPad.
* Решения (зафиксированы из questions.org)
- Приоритет устройств: клавиатура + тачпад. TrackPoint не приоритет.
- Кнопки мыши/тачпада: минимум две, по возможности захватить максимум.
- Оси тачпада: для начала абсолютные.
- Скетч самодостаточный — демо-Ndef внутри, не тянет готовые
NodeDictionary-инструменты.
- Отдельный инструмент, но будет применён в перформансе
(24_09_2026_gig).
- Минимальный набор действий достаточен: toggle / inc / dec /
play/stop / free.
- «Держать-пока-нажата» (gate) — требование, KeyUp обязателен.
- Фидбек: GUI-окно с текстовым содержимым (не пост-окно).
- Форма: несколько файлов с одной точкой входа.
* Принцип
Не «демо», а рабочий шаблон-каркас: одна точка входа, декларативная
привязка HID-событий к действиям, слои разделены. Основа — HIDdef
(аналог MIDIdef) плюс словари привязок. Всё состояние — в глобалах
~ (прокси-пространстве), чтобы быстро править из REPL.
* Этапы
** 1. Доступ к HID-устройствам из-под Linux
Основная и часто незаметная проблема: sclang без привилегий не видит
/dev/hidraw*. Нужно решить ДО кода:
- проверить, что устройства видны: ~HID.findAvailable~ и ~HID.postAvailable~ из REPL;
- если пусто — udev-правило, разрешающее чтение hidraw для нужной
группы (или запуск от рута для прототипа);
- зафиксировать точные ID устройств (vendor/product) ThinkPad, чтобы
не открывать «всё подряд».
** 2. Слой открытия устройств
- Сканирование ~HID.findAvailable~;
- выбор целевого устройства по vendor/product, а не по индексу;
- ~HID.open(id)~ → держим объект устройства в ~~device~;
- graceful-fallback: если устройства нет, скетч не падает, а печатает
предупреждение и ждёт.
** 3. Слой привязок (словарь обработчиков)
Декларативная таблица: ключ → действие. Значение — либо event-словарь
(структурированное действие над Ndef/Tdef), либо напрямую функция
(произвольный обработчик). Примерная форма:
#+begin_src sclang
~bindings = IdentityDictionary[
// клавиша -> Ndef-цель + параметр (event-словарь)
\q -> (type: \toggle, ndef: \d, param: \gate),
\w -> (type: \inc, ndef: \d, param: \freq, step: 10),
\e -> (type: \dec, ndef: \d, param: \freq, step: 10),
\r -> (type: \playTdef, tdef: \loop),
// клавиша -> произвольная функция (просто выполняется)
\space -> { "space hit".postln; ~something.value },
];
#+end_src
Разделяем сами обработчики (механику) и карту привязок (настройку),
чтобы менять раскладку не трогая логику. Диспетчер действия различает
значение по типу: `isFunction` → вызвать напрямую; иначе — разобрать
по `type` (event-словарь). Это даёт «сырой» побег: можно вешать на
клавишу любую функцию без добавления нового типа действия.
** 4. HIDdef: KeyDown / KeyUp
- клавиатура: value == 1 (нажатие) / value == 0 (отпускание);
- поддержка обеих фаз — это требование из readme (KeyUp нужен,
например, для gate=0 у нот / для «держать-пока-нажата»);
- HIDdef c фильтром по usagePage/usage клавиатуры.
** 5. Мышь / тачпад
- кнопки мыши/тачпада — как клавиши (нажатие/отпускание);
- движение тачпада: оси X/Y (Generic Desktop usage 0x30/0x31),
маппинг на параметры (например, pan, freq, амплитуда);
- прочие события тачпада (wheel, multitouch) — опционально, по
наличию в device.elements конкретного устройства.
** 6. Действия над Ndef/Tdef (плюс «сырые» функции)
Типы действий из карты привязок (event-словари):
- toggle параметра (gate, bypass);
- inc/dec значения параметра с шагом и clamp по ControlSpec;
- play/stop Tdef;
- free Ndef;
- переключение пресетов.
Используем ~Ndef(<name>).set(...)~ и ~Tdef(<name>).play/stop~.
Помимо структурированных действий, значение привязки может быть
произвольной Function — диспетчер её просто вызывает (`~action.value`).
Это «побег» для любых обработчиков, не влезающих в типы выше: запуск
отдельных функций, посыл OSC, манипуляции с ~env, что угодно. Разбор
в диспетчере: `if (action.isFunction) { action.value } { ~dispatch[action.type].(action) }`.
** 7. Минимальный визуальный фидбек
GUI-окно с текстовым содержимым (Window + StaticText): одна строка =
один активный Ndef/Tdef и его текущие параметры; подсветка изменённого
параметра. Без полноценных слайдеров — только текстовый фидбек
состояния.
** 8. Структура файлов (внутри keyboard-controller/)
#+begin_src text
readme.org — описание (уже есть)
plan.org — этот план
controller.scd — точка входа, скелет скетча
bindings.scd — карта привязок (словарь)
actions.scd — реализация действий (toggle/inc/dec/...)
#+end_src
Либо всё в одном .scd на старте — разделить позже, когда вырастет.
* Порядок работ
1. Поднять доступ к HID (udev), подтвердить видимость устройств.
2. Минимальный скетч: открыть устройство, повесить один HIDdef на
одну клавишу, печатать нажатия.
3. Добавить карту привязок + действия toggle/inc/dec.
4. Подключить Ndef (например, из NodeDictionary) как управляемую цель.
5. Добавить KeyUp-семантику (gate / держать-пока-нажата).
6. Добавить оси тачпада и кнопки мыши.
7. Визуальный фидбек.
8. Отполировать fallback-пути (устройство отвалилось, нет HID).
* Риски / на что смотреть
- TrackPoint на ThinkPad часто НЕ HID-тачпад, а PS/2-мышь через evdev —
его HID-слой может не видеть. По решениям — не приоритет, можно
не покрывать.
- Имена классов: в SC это HIDdef / HIDFunc (строчная d), HIDDeviceService,
GeneralHID. В readme написано «HIDDef» — сверять с документацией
конкретной версии SC 3.14.
- usagePage/usage клавиш могут отличаться от ожидаемых — печатать
сырые event-поля при отладке (page, usage, value) прежде чем
зашивать их в привязки.
- Несколько одинаковых устройств (внешняя клавиатура + встроенная) —
отличать по vendor/product/id, не по индексу списка.
- Абсолютные оси тачпада на разных устройствах дают разные диапазоны —
нормировать (делить на логический максимум) прежде чем маппить на
ControlSpec параметра.