Библиотека управления Wish Windows для MacOS
В последние несколько лет многие разработчики, ранее в Linux и Windows, мигрировали в Mac для своего превосходного оборудования и ОС на основе UNIX, которая «просто работает».
Но по пути мы отказались от нас чего -то дорогого: контроль над нашим рабочим столом.
Цель Swindler - помочь нам вернуть этот контроль и дать нам лучшее из обоих миров.
Писать управляющие окнами для MacOS сложно. Есть много системных проблем, в том числе ограниченные и плохо документированные API. Все управляющие окнами на MacOS должны использовать API-интерфейсы на основе C, которые трудно использовать и удивительно сами.
В результате выбор управляющих окон довольно ограничен, и у многих из них есть раздражающие ошибки, такие как замораживание, условия гонки, «Призрачные окна», а не «видят» окна, которые на самом деле есть. Чем более сложный менеджер окна, тем больше он опирается на эти API, и тем больше начинают появляться эти ошибки.
Работа Swindler состоит в том, чтобы легко писать мощных управляющих окон, используя хорошо документированный Swift API и слой абстракции. Он решает проблемы API доступности с этими функциями:
API Swindler полностью задокументирован и безопасен для типа благодаря Swift. Это гораздо проще и безопаснее использовать, чем API-интерфейсы на основе C. (См. Пример ниже.)
Управляющие окнами на MacOS полагаются на IPC: вы просите приложение для позиции окна, ждите , пока он ответит, просит его перемещение или сфокусирован, а затем ждите , пока приложение будет соблюдать (или нет). Большую часть времени это работает нормально, но он работает во власти цикла удаленного приложения, что может привести к длинным многосекундным задержкам.
Swindler поддерживает модель всех приложений и состояний окон, поэтому ваш код знает все о окнах на экране. Чтения мгновенные , потому что все состояние кэшируется в рамках процесса вашего заявления и остается в курсе. Swindler тестируется, чтобы убедиться, что он остается в соответствии с системой в любой ситуации.
Например, если вам нужно изменить размер много окон, вы можете сделать это, не опасаясь одного не отвечающего применения, удерживающего все остальное. Запросы на записи направляются асинхронно и одновременно, а API на основе обещаний Swindler позволяет легко не отставать от состояния операций.
Более сложные управляющие окон должны наблюдать за событиями в Windows, но API наблюдателя не очень хорошо документирован и часто оставляет события, которые вы можете ожидать, или доставляет их в неправильном порядке. Например, следующая ситуация распространена, когда появляется новое окно:
1. MainWindowChanged on com.google.chrome to <window1>
2. WindowCreated on com.google.chrome: <window1>
Увидеть проблему? С помощью Swindler все события испускаются в ожидаемом порядке, а пропущенные.
В качестве бонуса события, вызванные вашим кодом, помечены как таковые, поэтому вы не реагируете на них как действия пользователя. Одна только эта функция делает возможным совершенно новый уровень изысканности.
Следующий код назначает все окна на экране на сетку. Обратите внимание на простоту и силу API на основе обещаний. Запросы отправляются одновременно и на заднем плане, а не последовательно.
Swindler . initialize ( ) . then { state -> Void in
let screen = state . screens . first!
let allPlacedOnGrid = screen . knownWindows . enumerate ( ) . map { index , window in
let rect = gridRect ( screen , index )
return window . frame . set ( rect )
}
when ( allPlacedOnGrid ) { _ in
print ( " all done! " )
}
} . catch { error in
// ...
}
func gridRect ( screen : Swindler . Screen , index : Int ) -> CGRect {
let gridSteps = 3
let position = CGSize ( width : screen . width / gridSteps ,
height : screen . height / gridSteps )
let size = CGPoint ( x : gridSize . width * ( index % gridSteps ) ,
y : gridSize . height * ( index / gridSteps ) )
return CGRect ( origin : position , size : size )
}Наблюдать за событиями просто. Вот как бы вы внедрили Snap-to-Grid:
swindlerState . on { ( event : WindowMovedEvent ) in
guard event . external == true else {
// Ignore events that were caused by us.
return
}
let snapped = closestGridPosition ( event . window . frame . value )
event . window . frame . value = snapped
}Ваше заявление должно запросить доступ к API доверенного AX. Для этого просто используйте этот код в своем AppDelegate:
func applicationDidFinishLaunching ( _ aNotification : Notification ) {
guard AXSwift . checkIsProcessTrusted ( prompt : true ) else {
print ( " Not trusted as an AX process; please authorize and re-launch " )
NSApp . terminate ( self )
return
}
// your code here
}Многие вспомогательные или иные «специальные» компоненты приложения не отвечают на запросы AX и не отвечают ошибкой. В результате ожидается, что он увидит несколько сообщений, подобных этим:
<Debug>: Window <AXUnknown "<AXUIElement 0x610000054eb0> {pid=464}" (pid=464)> has subrole AXUnknown, unwatching
<Debug>: Application invalidated: com.apple.dock
<Debug>: Couldn't initialize window for element <AXUnknown "<AXUIElement 0x610000054eb0> {pid=464}" (pid=464)> () of com.google.Chrome: windowIgnored(<AXUnknown "<AXUIElement 0x610000054eb0> {pid=464}" (pid=464)>)
<Notice>: Could not watch application com.apple.dock (pid=308): invalidObject(AXError.NotificationUnsupported)
<Debug>: Couldn't initialize window for element <AXScrollArea "<AXUIElement 0x61800004ed90> {pid=312}" (pid=312)> (desktop) of com.apple.finder: AXError.NotificationUnsupported
В настоящее время они зарегистрированы, потому что трудно определить, должно ли приложение «не удалось» (особенно на тайм -аутах). Пока вещи, кажется, работают, вы можете игнорировать их.
Swindler находится в разработке и находится в Альфе . Вот состояние его основных особенностей:
Вы можете увидеть весь запланированный API здесь.
Документация API (последний выпуск)
Документация API (Main)
Swindler использует Swift Package Manager.
Клонировать проект, затем в вашем раковине:
$ cd Swindler
$ git submodule init
$ git submodule update
На этом этапе вы должны быть в состоянии построить Swindler в XCode и начать с вашего пути!
Вы можете запустить пример проекта из командной строки.
swift run
Вы можете поболтать с нами на джакете.
Следуйте за мной в Твиттере: @tmandry
Swindler построен на Axswift.