diff --git a/keyboard-controller/hidctrl.scd b/keyboard-controller/hidctrl.scd new file mode 100644 index 0000000..a7bbdb5 --- /dev/null +++ b/keyboard-controller/hidctrl.scd @@ -0,0 +1,44 @@ +~hidCtrl = ( + defName: \hidCtrl, + // Array of HIDInfo objects + devices: [], + // List of HID objects. Maybe should be private later + connected: List.new(), + eventsMap: IdentityDictionary[ + \X -> { | val | val.postln }, + ], + connect: { + |self| + self.devices.do { + |hidinfo| + var device = HID.open( + hidinfo.vendorID, + hidinfo.productID, + hidinfo.path + ); + + if(device.notNil) { + self.connected.add(device); + } + }; + }, + free: { + |self| + if(self.connected.size > 0) { + self.connected.do { |hid| hid.close }; + self.connected.clear; + } + + HIDdef(self.defName).free; + }, + listen: { + |self| + HIDdef.usage(self.defName, { + |value, rawValue, usage, page, elid, ele, devid, device| + var name = HIDUsage.idsToName(page, usage); + var handler = self.eventsMap[name]; + + if(handler.notNil) { handler.(value) }; + }); + } +); diff --git a/keyboard-controller/plan.org b/keyboard-controller/plan.org deleted file mode 100644 index 99acb94..0000000 --- a/keyboard-controller/plan.org +++ /dev/null @@ -1,141 +0,0 @@ -#+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().set(...)~ и ~Tdef().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 параметра. diff --git a/keyboard-controller/questions.org b/keyboard-controller/questions.org deleted file mode 100644 index 8438432..0000000 --- a/keyboard-controller/questions.org +++ /dev/null @@ -1,66 +0,0 @@ -#+TITLE: Keyboard Controller — вопросы - -Вопросы, на которые не ответить чтением репозитория. Ответы помогут -зафиксировать план точнее. Нумерую для удобства ответа. - -* Устройство и события - -1. Какое устройство — приоритет? Тачпад (HID), клавиатура (HID) или - TrackPoint? На ThinkPad TrackPoint обычно PS/2-мышь через evdev и - НЕ виден в HID-слое — если он важен, нужен отдельный обходной - путь (не HIDdef). Тачпад и клавиатура в HID есть. - -В приоритете клавиатура + тачпад, так как это софт скорее для -живых выступлений. - -2. Кнопки мыши/тачпада — нужны все (left/right/middle) или хватит - одной-двух под конкретные действия? - -Хватит двух. Если возможно, конечно, стоит захватить максимум. - -3. Оси тачпада: абсолютные координаты (как pad) или относительное - смещение (как трекпад-жесты)? От этого зависит маппинг на - параметры (pan по X, что по Y?). - -Для начала абсолютные. - -* Цели управления (Ndef/Tdef) - -4. Какие конкретно Ndef/Tdef должны управляться? Это готовые - NodeDictionary-инструменты из bmSClib (какие .scd?), или скетч - должен быть самодостаточным с парой демо-Ndef внутри? - -Скетч должен быть самодостаточным - -5. Есть ли связь с 24_09_2026_gig (печатная машинка + live-electronics), - или это отдельный инструмент? Влияет на набор действий - (например, нужен ли freeze/hold, пресеты, «держать-пока-нажата»). - -Это отдельный инмтрумент, но будет применён в этом перформансе - -* Набор действий - -6. Минимальный достаточный набор действий для первого работающего - скетча: toggle, inc/dec, play/stop, free — хватит? Или сразу - нужны пресеты/переключение сцен? - -Хватит - -7. «Держать-пока-нажата» (gate) — это требование (KeyUp важен для нот) - или KeyUp достаточно для анти-дребезга/фиксации состояния? - -Требование - -* Фидбек и форма - -8. Фидбек: пост-окно (просто), текстовое окно со строками по - Ndef/Tdef, или GUI со слайдерами сразу? Readme говорит «минимальный - визуальный фидбек» — уточнить, где нижняя граница. - -Это может быть gui окно с текстовым фидбеком - -9. Форма результата: отдельный .scd-скелет (несколько файлов) или - один самодостаточный .scd? Или встроить как класс/расширение в - bmSClib (рядом с NodeDictionary)? - -Несколько файлов с одной точкой входа diff --git a/keyboard-controller/readme.org b/keyboard-controller/readme.org index 3217fd1..6f5ae55 100644 --- a/keyboard-controller/readme.org +++ b/keyboard-controller/readme.org @@ -21,3 +21,33 @@ HID устройств. можно поддержать. Основные рабочие машины - ноутбуки ThinkPad. + +* Доступ к HID на Linux (udev) + +sclang без привилегий видит HID-устройства только если может читать +/dev/hidraw*. По умолчанию они root:root 0600, поэтому нужно udev-правило. + +Простое решение «без групп» (читать может любой пользователь): + +#+begin_src sh +# /etc/udev/rules.d/99-hidraw-input.rules +KERNEL=="hidraw*", SUBSYSTEM=="hidraw", MODE="0666" +#+end_src + +Затем применить: + +#+begin_src sh +sudo udevadm control --reload-rules && sudo udevadm trigger +#+end_src + +Проверить: + +#+begin_src sh +ls -l /dev/hidraw* # должно стать crw-rw-rw- +head -c 0 /dev/hidraw0 # чтение от обычного пользователя должно пройти +#+end_src + +Альтернатива «с группой» (безопаснее): MODE="0660", GROUP="input" + +пользователь в группе input (`sudo usermod -aG input $USER`, затем +перелогиниться). При MODE="0660" без членства в input доступа не будет. +