Steven
Steven4 мин. чтения

Почему запись экрана захватывает не тот монитор (и как мы это исправили)

На конфигурации с двумя мониторами GeekBye записывал и снимал скриншоты с основного дисплея независимо от того, на каком экране вы работали. Фикс уместился в одну небольшую функцию — но первая её версия была неверной, и код-ревью поймало почему.

Запись экрана
Несколько дисплеев
Инженерия
Релизы GeekBye
Почему запись экрана захватывает не тот монитор (и как мы это исправили)

Вот баг, который существует, только если у вас два монитора, — потому он какое-то время и жил тихо. Вы работаете на боковом дисплее, запускаете запись GeekBye, а она записывает ваш основной монитор. Тот, что с меню-баром. Тот, на который вы не смотрели.

Тот же дефект, тише и хуже, ударил по скриншотам, которые GeekBye отправляет ИИ для контекста: жмёте горячую клавишу скриншота на втором мониторе, а ИИ получает картинку первого. Никакого визуального признака — ассистент просто отвечает про не тот экран, и вы недоумеваете, почему он в замешательстве. GeekBye v2.0.10 исправил оба.

Два конвейера, один ленивый дефолт

Захват экрана на десктопе — это два отдельных пути кода, и оба независимо выбрали основной дисплей:

  • Запись видео перечисляла доступные источники экрана и брала первый — sources[0]. На macOS это фактически всегда основной дисплей. Ни выбора, ни логики о том, где вы на самом деле были. Комментарий в нашем собственном коде буквально гласил «auto-select first screen source» (автовыбор первого источника экрана).
  • Скриншоты использовали macOS-команду screencapture с флагом -m. У этого флага ровно одно значение: только основной дисплей. Зашито намертво.

Ни один из путей ни разу не задал вопрос, который важен: на каком экране пользователь?

Одно, что никогда не было сломано, и это стоит прояснить, потому что люди думают иначе: переключение между пространствами (Spaces) macOS на том же мониторе всегда работало. Захват происходит на уровне дисплея — он берёт то пространство, что видно на выбранном дисплее. Баг никогда не был про пространства. Он всегда был про выбор не того физического дисплея.

Фикс, который выглядел очевидным — и был неверным

Правильный сигнал кажется простым: захвати дисплей, на котором пользователь. Наша первая реализация привязалась к окну оверлея GeekBye — захватывай тот дисплей, на котором живёт оверлей.

Код-ревью справедливо его зарубило. Оверлей GeekBye создаётся как окно на всю рабочую область на основном дисплее, в позиции (0,0). Он перемещается на другой монитор, только если вы физически перетащите туда его «пилюлю» — а горячие клавиши, которые его подвигают, зажаты размерами основного дисплея, так что они не могут перенести его на второй монитор вообще. Привязка захвата к оверлею означала: для каждого пользователя, который не перетащил оверлей на свой рабочий экран, «фикс» разрешался обратно в основной дисплей. Он не исправил бы почти никого — а при быстром тесте на одномониторной машине разработчика выглядел бы как работающий.

Правильная точка привязки — курсор. Где бы ни была ваша мышь, это и есть дисплей, на котором вы работаете, — и это верно для любого способа начать захват: горячая клавиша срабатывает там, куда вы указываете, а клик по кнопке «Запись» по определению помещает ваш курсор на этот дисплей. Итоговый фикс — двухстрочная функция: дисплей, ближайший к курсору. Видео сопоставляет свой источник захвата с id этого дисплея; скриншоты передают границы этого дисплея в screencapture -R (конкретный прямоугольник) вместо флага -m (только основной дисплей).

Мы намеренно выбрали -R (явный прямоугольник в глобальных координатах экрана) вместо -D (индекс дисплея): индекс дисплея в ОС не имеет гарантированного соответствия порядку дисплеев во фреймворке, так что индекс был бы второй игрой в угадайку. Прямоугольник из реальных границ дисплея однозначен — и поведение флага мы проверили, включая отрицательные координаты для дисплеев слева от основного, на настоящей многомониторной сборке перед выпуском.

Почему это хороший учебный баг

  1. «Захвати экран» скрывает решение. На одном дисплее решения нет, поэтому решение никогда не проектируется — оно назначается по умолчанию. Несколько мониторов — это место, где всплывает каждый неявный дефолт.
  2. Тихо-неверно хуже, чем видимо-неверно. Баг с видео раздражал людей. Баг со скриншотом вводил в заблуждение ИИ, незримо. Когда вы строите функции, питающие модель контекстом, неверный вход даёт уверенно неверный выход без единой ошибки где-либо. Именно такие отказы стоит выслеживать усерднее всего.
  3. Фикс, проходящий на вашей машине, может провалиться на любой чужой. Версия, привязанная к оверлею, работала в одномониторном тесте. Весь смысл бага — несколько мониторов, — а ревьюер рассудил о реальном положении окна, вместо того чтобы доверять зелёному тесту. Ревью — не штамп на работающем коде; это вторая модель того, почему код работает.

GeekBye v2.0.10 выпускает основанный на курсоре фикс и для записи, и для скриншотов. Если у вас несколько дисплеев, захват теперь следует за вами.

Соседние релизы этой серии смотрите в почему ваш ИИ-ассистент для заметок останавливает запись посреди встречи (v2.0.9) и почему ИИ-транскрипция перевирает технические термины (v2.0.11). О том, как оверлей ведёт себя во время звонков, читайте в как оставаться невидимым во время демонстрации экрана.

Похожие статьи

Транскрипция в реальном времени, когда файрвол блокирует WebSocket
Steven
Steven4 мин. чтения

Транскрипция в реальном времени, когда файрвол блокирует WebSocket

Корпоративные сети обожают пропускать HTTPS и втихую убивать upgrade до WebSocket. Это незаметно ломает транскрипцию в реальном времени. GeekBye v2.0.8 автоматически переключается на чисто-HTTPS транспорт — а его выпуск вскрыл баг, который сделал бы всю функцию бесполезной.

Транскрипция
Сети
Инженерия
День, когда наше приложение устроило DDoS самому себе
Steven
Steven5 мин. чтения

День, когда наше приложение устроило DDoS самому себе

Копившиеся отложенные загрузки, выпущенные все разом при запуске, превратили каждый клиент GeekBye в маленькую атаку denial-of-service на наши собственные серверы. Исправление — и лестница liveness соединения, которую оно заставило нас построить — одна из самых полезных вещей, которым научил нас v2.

Надёжность
Сети
Инженерия
Чего на самом деле стоит версия 2: 206 коммитов честных состояний
Steven
Steven6 мин. чтения

Чего на самом деле стоит версия 2: 206 коммитов честных состояний

GeekBye v2 не был релизом новых функций. Это были 206 коммитов, нацеленных на одну идею: приложение никогда не должно врать о собственном состоянии. Вот чего это стоит — включая однострочную ошибку в lockfile, которая едва не помешала нам выпустить хоть что-то из этого.

Надёжность
Инженерия
Релиз