MacOSの迅速な窓管理ライブラリ
過去数年間、以前はLinuxとWindowsの多くの開発者が、「機能するだけ」の優れたハードウェアとUNIXベースのOSのためにMacに移行しました。
しかし、途中で私たちは私たちにとって大切な何かをあきらめました:私たちのデスクトップ環境を制御します。
Swindlerの目標は、私たちがそのコントロールを取り戻し、私たちに両方の世界の最高を与えるのを助けることです。
MacOSのウィンドウマネージャーを書くのは難しいです。限られた、文書化されていないAPIなど、多くの体系的な課題があります。 MACOSのすべてのウィンドウマネージャーは、使用が難しく、驚くほどバグがあるCベースのアクセシビリティAPIを使用する必要があります。
その結果、ウィンドウマネージャーの選択はかなり限られており、そこにあるものの多くは、フリーズ、レース条件、「ファントムウィンドウ」などの迷惑なバグを持っています。ウィンドウマネージャーが洗練されているほど、これらのAPIに依存し、これらのバグが表示され始めます。
Swindlerの仕事は、十分に文書化されたSwift APIと抽象化レイヤーを使用して、強力なウィンドウマネージャーを簡単に作成できるようにすることです。これらの機能を使用して、アクセシビリティAPIの問題に対処します。
SwindlerのAPIは、Swiftのおかげで完全に文書化されており、タイプセーフです。 CベースのアクセシビリティAPIよりもはるかに簡単で安全です。 (以下の例を参照してください。)
MacOSのウィンドウマネージャーはIPCに依存しています。ウィンドウの位置を申請し、応答するのを待ち、移動または集中するように要求し、アプリケーションが従う(またはそうでない)を待ちます。ほとんどの場合、これは大丈夫ですが、リモートアプリケーションのイベントループに翻弄されて機能します。
Swindlerは、すべてのアプリケーションとウィンドウ状態のモデルを維持しているため、コードは画面上のウィンドウに関するすべてを知っています。すべての状態がアプリケーションのプロセス内でキャッシュされ、最新の状態にあるため、読み取りは瞬時です。 Swindlerは、あらゆる状況でシステムと一致することを確認するために広範囲にテストされています。
たとえば、多くのWindowsを同時に変更する必要がある場合は、1つの反応しないアプリケーションが他のすべてを保持することを恐れることなくそうすることができます。書き込みリクエストは非同期的かつ同時に派遣され、Swindlerの約束ベースのAPIにより、運用状態に簡単に追いつくことができます。
より洗練されたウィンドウマネージャーは、Windowsでのイベントを観察する必要がありますが、オブザーバーAPIは十分に文書化されておらず、多くの場合、予想されるイベントを除外したり、間違った順序で配信したりします。たとえば、新しいウィンドウが表示されると、次の状況が一般的です。
1. MainWindowChanged on com.google.chrome to <window1>
2. WindowCreated on com.google.chrome: <window1>
問題がわかりますか? Swindlerを使用すると、すべてのイベントが予想される順序で放出され、行方不明のイベントが記入されます。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
}アプリケーションは、信頼できるAX APIへのアクセスを要求する必要があります。これを行うには、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ドキュメント(メイン)
SwindlerはSwift Package Managerを使用しています。
プロジェクトをクローンし、シェルで実行します。
$ cd Swindler
$ git submodule init
$ git submodule update
この時点で、XcodeでSwindlerを構築し、途中で開始できるはずです!
コマンドラインからサンプルプロジェクトを実行できます。
swift run
ギッターでチャットすることができます。
Twitterでフォローしてください:@tmandry
SwindlerはAxswiftに建てられています。