Мгновенное переключение клавиатуры и трекпада между двумя Mac по локальной сети (software KVM). Девайсы остаются спарены по Bluetooth с одним Mac — Bluetooth не трогается вообще. Ввод форвардится по TCP, переключение по хоткею мгновенное.
Симметрично: на обоих Mac стоит одно и то же приложение, каждый может и отдавать ввод, и принимать. Всё управление — из иконки в меню-баре, без терминала.
Apple Magic Trackpad/Keyboard хранят бонд только с одним хостом — любой свитчер переспаривает девайс на каждом переключении (10–15 с и отвалы). KVM это обходит.
./build.sh # нужен Xcode CLT: xcode-select --install
open "KVM Switch.app"Перетащи KVM Switch.app и включи:
- Accessibility — обязательно (и для перехвата, и для воспроизведения ввода)
- Input Monitoring — обязательно (перехват клавиатуры)
- Локальная сеть — macOS спросит при первом запуске (для авто-поиска)
build.sh использует сертификат «KVM Switch Signing», если он установлен.
С ним подпись остаётся стабильной. При fallback на ad-hoc после пересборки может
понадобиться заново выдать права. Сертификат создаётся через ./setup-signing.sh. Для существующей другой подписи
можно передать KVM_SIGNING_IDENTITY="имя сертификата" ./build.sh; это позволяет
сохранить identity, уже разрешённую на конкретном Mac.
- 🔴 связи нет
- 🟢 связь есть, ввод на этом Mac
- 🔵 связь есть, ввод уходит на второй Mac
Клик по иконке показывает: имя/IP этого Mac, состояние исходящей связи (→) и входящей (←), порт, хоткей.
Иконка → Настройки…:
- Второй Mac — выбор из найденных в сети (Bonjour) либо вручную IP/host + порт
- Хоткей — нажми кнопку и набери свою комбинацию (по умолчанию
⌃⌥⌘S) - Край экрана — тумблер + направление (где стоит второй Mac). Курсор уходит на второй Mac, когда упрёшься в крайнее ребро ВСЕХ твоих экранов (внутренние стыки мониторов не триггерят — между своими экранами ходишь свободно). Возврат — упереться в обратный край на втором Mac. Нужен непрерывный push (~120 px): набор текста, остановка/смена направления и пауза между движениями более 250 мс сбрасывают накопленное движение к краю.
- Автозапуск — тумблер «запускать при логине»
Выбрать второй Mac можно и быстро: иконка → Второй Mac → клик по имени.
- Запусти приложение на обоих Mac, выдай права.
- При первой настройке единственный найденный Mac выбирается автоматически. Если найдено несколько, выбери второй в подменю «Второй Mac». Существующий выбор сохраняется.
- Иконка станет 🟢 — связь есть.
- Жми хоткей (
⌃⌥⌘S) — ввод мгновенно уходит на второй Mac (🔵), ещё раз — назад (🟢). Можно и через иконку → «Переключить ввод».
Экстренный возврат: ⌃⌥⌘Esc на ноутбуке, к которому подключена клавиатура. Это сочетание фиксировано и не зависит от настроенного хоткея.
Подключать клавиатуру и мышь можно к любому из двух Mac: роли сервера и клиента не назначаются. Физический ввод на принимающем Mac прекращает текущую удалённую сессию; следующий переход через край или хоткей может передавать ввод в обратную сторону. Программные события от приложений (например, вставка текста) и события мыши без движения не прекращают удалённую сессию. Приложение не переключает Bluetooth-сопряжение устройств само.
Обновить нужно оба Mac. Старый peer не отвечает на новый протокол проверки работоспособности: с ним новая версия оставляет ввод локальным.
- Каждые 250 мс запрашивается ответ от потока приложения, обрабатывающего ввод. Без свежего ответа передача прекращается: проверка выполняется на каждом физическом событии и таймером. Срок ответа — 1,5 с; планировщик ОС может задержать выполнение таймера. Одного открытого TCP-соединения недостаточно.
- Перехват начинается только после подтверждения сессии вторым Mac. Пока ответ не пришёл, обычные события проходят локально. Просроченные подтверждения игнорируются.
- Ошибки отправки, EOF, смена настроек и отключение event tap возвращают локальный режим. Старые callbacks соединений не могут отменить новое соединение.
- При удалённом управлении локальный курсор скрывает отдельный помощник. Он не удерживает курсор и не отключает связь мыши с курсором. Основной процесс продлевает разрешение на скрытие каждые 250 мс только при здоровой связи; без продления через 1 секунду помощник показывает курсор и завершает работу. Остановка всего основного процесса не останавливает этот таймер.
- Перехват начинается после подтверждения и второго Mac, и помощника курсора. Если помощник завис, основное приложение возвращается в local и завершает именно свой дочерний процесс; его скрытие снимается WindowServer.
- Для скрытия курсора из фонового menu-bar приложения используется свойство
WindowServer
SetsCursorInBackground, как в распространённых macOS KVM. Это частный API: при отсутствии нужных символов передача остаётся выключенной. После крупных обновлений macOS скрытие и восстановление требуют перепроверки. - При завершении сессии, разрыве связи и физическом перехвате управления освобождаются клавиши, модификаторы и кнопки мыши, нажатые через приложение.
- После задержки главного потока или сна старые сессии закрываются. Возобновление соединения само по себе не включает перехват: нужен новый переход/хоткей.
- Инжектируемые события помечаются и не возвращаются в сеть.
python3 tests/run.py
./build.shАвтоматические проверки подменяют вывод событий, чтобы не нажимать клавиши и не
двигать курсор пользователя. Они не заменяют проверку на двух Mac с реальными
правами Accessibility/Input Monitoring. План такой проверки — в VALIDATION.md.
Передаются курсор, клики, drag, прокрутка и жесты трекпада, обычные клавиши macOS,
F1–F20, навигация, цифровой блок и левые/правые модификаторы. Сохраняются
автоповтор и тип клавиатуры. Мультимедийные и специальные клавиши передаются
как события systemDefined subtype 8; поддержаны документированные коды
IOKit 0–23, кроме Power (6), а также Menu (25). Их фактическое действие зависит
от оборудования и настроек принимающего Mac. Caps Lock не считается постоянно
зажатой клавишей; нажатия хоткея переключения зарезервированы до отпускания.
Touch ID, Power, неизвестные системные события и функции, которые macOS не выдаёт event tap, остаются локальными. Защищённый ввод macOS не обходится. Раскладку определяет принимающий Mac. Прокрутка трекпада передаётся вместе с фазами, инерцией и дробными дельтами, а мультитач-жесты приложений (pinch, поворот, smart zoom, свайп между страницами, force click) — как сериализованное событие CG type 29, которое второй Mac воспроизводит у своего курсора. Жесты Dock (свайпы Spaces/Mission Control, Launchpad) обрабатывает не event tap, а служба мультитача, поэтому они остаются локальными.
Для активной передачи запрашивается точность таймеров и I/O, а для TCP — приоритет интерактивного трафика. Геометрия экранов обновляется при изменении конфигурации дисплеев вместо запроса на каждом движении. Это уменьшает работу в пути обработки событий, но не устраняет задержки и потери Wi-Fi.
Приложение ищет второй Mac через Bonjour в общей Wi-Fi/Ethernet сети.
includePeerToPeer выключен для поиска, приёма и исходящей связи: KVM больше
не запрашивает прямое радиообнаружение Apple. Системные AirDrop/Continuity и
настройки Wi-Fi не изменяются. Для соединения обоим Mac нужна общая LAN;
прямой режим без общей сети в этой версии не используется.
Последовательные движения без нажатой кнопки, уже пришедшие одной пачкой, объединяются в одно обновление курсора. Геометрия и возврат через край считаются для каждого движения. Клики, перетаскивание, клавиши, модификаторы и управляющие сообщения разделяют пачки. Дополнительного таймера сглаживания или задержки нет. Это сокращает воспроизведение накопившихся движений, но не скрывает сетевые паузы.