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). Про те, як оверлей поводиться під час дзвінків, читайте в як лишатися невидимим під час демонстрації екрана.

Схожі статті

Тиша була несуча
Steven
Steven7 хв читання

Тиша була несуча

Два останні релізи GeekBye v1 — про одну й ту саму незручну правду: транскрипція в реальному часі через справжню мережу не є безвтратною, і чесний хід — перестати вдавати, що вона така. v1.8.20 зберігав копію кожного фрагмента звуку на диску, перш ніж відкинути його під час перепідключення, і почав уголос позначати пропуски в транскрипті. v1.9.0 перестав надсилати тишу, щоб заощадити трафік — і виявив, що тиша була саме тим сигналом, за яким транскрайбер розумів, що речення скінчилося. Два релізи про ціну викидання речей.

Інженерія
Аудіо
Надійність
Три дієслова, що тримають Web Audio живим
Steven
Steven8 хв читання

Три дієслова, що тримають Web Audio живим

Два точкові релізи GeekBye, розділені двома місяцями і в двох різних файлах, дали нашому аудіокоду той самий урок із протилежних кінців: перестань поводитися з браузерним AudioContext як з одноразовим. Один реліз навчився робити resume() контексту, який macOS тихо призупинив посеред запису; інший навчився suspend() замість close(), щоб сесії одна за одною перестали врізатися в накинуту Chromium стелю приблизно в шість контекстів. Resume, suspend, close — ось і весь сюжет.

Інженерія
Аудіо
Десктоп
Відрізнити дзвінок від відкритого застосунку
Steven
Steven8 хв читання

Відрізнити дзвінок від відкритого застосунку

GeekBye вміє помітити, що ти приєднався до відеозустрічі, і запропонувати її записати. Виявлення виявляється легшою половиною — нативний Swift-бінарник читає заголовки вікон кожні десять секунд. Важка половина — точність: не спрацьовувати, коли Zoom просто відкритий, не пропонувати записати зустріч, яку ти вже записуєш, і не глушити мікрофон у дзвінку, в якому ти насправді сидиш. Три релізи, і кожен із них — запобіжник, якому довелося навчитися не перемагати самого себе.

Інженерія
macOS
Десктоп