OS Xでのウィンドウの移動およびサイズ変更API
質問
ウィンドウサーバーからウィンドウのリストを照会し、ウィンドウを移動およびサイズ変更するために、OS Xで文書化された(または、それが唯一のオプションであれば文書化されていない)APIを見つけようとしています。誰かが私を正しい方向に向けることができますか?私はWin32でFindWindowExやMoveWindowのようなものから始めていると思います。
外部プロセスからこれを実行したいことに注意してください-自分のアプリのウィンドウサイズと位置だけを制御する方法を尋ねているのではありません。
解決
Accessibility APIを使用します。このAPIを使用すると、プロセスに接続し、ウィンドウのリスト(実際には配列)を取得し、各ウィンドウの位置とサイズを取得し、必要に応じてウィンドウのプロパティを変更することもできます。
ただし、ユーザーが設定で補助デバイスへのアクセスを有効にしている場合(システム設定->ユニバーサルアクセス)、またはすべてのアプリケーションがこのAPIを使用できる場合、またはアプリケーションが信頼できる補助アプリケーション(信頼できる場合、このオプションがチェックされていなくてもAPIを使用する場合があります)。 Accessibility API自体は、アプリケーションを信頼できるようにするために必要な機能を提供します-基本的には、ルートになり(セキュリティサービスを使用してユーザーのルート権限を要求)、プロセスを信頼済みとしてマークする必要があります信頼できる状態は起動時にのみチェックされ、アプリの実行中は変更できないため、アプリケーションを信頼済みとしてマークしたら、再起動する必要があります。ユーザーがアプリケーションを別の場所に移動したり、アプリケーションバイナリのハッシュを変更(更新後など)したりしない限り、信頼状態は永続的です。ユーザーが自分の設定で補助デバイスを有効にしている場合、すべてのアプリケーションは信頼できるものとして扱われます。通常、アプリはこのオプションが有効になっているかどうかを確認し、有効になっている場合は処理を続行します。そうでない場合は、すでに信頼されているかどうかを確認し、信頼されている場合は、もう一度やり直します。ユーザーがルート認証を拒否しない限り、自分自身を信頼させてからアプリケーションを再起動しない場合。 APIは、これをすべてチェックするために必要なすべての機能を提供します。
Mac OSウィンドウマネージャーを使用して同じことを行うプライベート関数がありますが、購入する唯一の利点は、信頼できるアクセシビリティアプリケーションである必要がないことです(これは、ほとんどの場合)。欠点は、このAPIはいつでも変更される可能性があり(過去に既に変更されている)、すべて文書化されておらず、機能はリバースエンジニアリングによってのみ知られていることです。ただし、アクセシビリティは公開されており、文書化されており、最初のOS Xバージョンが導入されて以来ほとんど変更されていません(一部の新しい機能は10.4および10.5で追加されましたが、他の多くは変更されていません)。
これはコード例です。 5秒待機するので、別のウィンドウに切り替える前に別のウィンドウに切り替えることができます(それ以外の場合は、テストのために退屈ではなく、常にターミナルウィンドウで動作します)。次に、最前面のプロセス、このプロセスの最前面のウィンドウを取得し、その位置とサイズを印刷して、最終的に25ピクセル右に移動します。そのようなコマンドラインでコンパイルします(test.cという名前の場合)
gcc -framework Carbon -o test test.c
簡単にするためにコードでエラーチェックを行っていないことに注意してください(何かがうまくいかず、特定のことがうまくいかない可能性がある場合、プログラムがクラッシュする可能性があるさまざまな場所があります)。コードは次のとおりです。
/* Carbon includes everything necessary for Accessibilty API */
#include <Carbon/Carbon.h>
static bool amIAuthorized ()
{
if (AXAPIEnabled() != 0) {
/* Yehaa, all apps are authorized */
return true;
}
/* Bummer, it's not activated, maybe we are trusted */
if (AXIsProcessTrusted() != 0) {
/* Good news, we are already trusted */
return true;
}
/* Crap, we are not trusted...
* correct behavior would now be to become a root process using
* authorization services and then call AXMakeProcessTrusted() to make
* ourselves trusted, then restart... I'll skip this here for
* simplicity.
*/
return false;
}
static AXUIElementRef getFrontMostApp ()
{
pid_t pid;
ProcessSerialNumber psn;
GetFrontProcess(&psn);
GetProcessPID(&psn, &pid);
return AXUIElementCreateApplication(pid);
}
int main (
int argc,
char ** argv
) {
int i;
AXValueRef temp;
CGSize windowSize;
CGPoint windowPosition;
CFStringRef windowTitle;
AXUIElementRef frontMostApp;
AXUIElementRef frontMostWindow;
if (!amIAuthorized()) {
printf("Can't use accessibility API!\n");
return 1;
}
/* Give the user 5 seconds to switch to another window, otherwise
* only the terminal window will be used
*/
for (i = 0; i < 5; i++) {
sleep(1);
printf("%d", i + 1);
if (i < 4) {
printf("...");
fflush(stdout);
} else {
printf("\n");
}
}
/* Here we go. Find out which process is front-most */
frontMostApp = getFrontMostApp();
/* Get the front most window. We could also get an array of all windows
* of this process and ask each window if it is front most, but that is
* quite inefficient if we only need the front most window.
*/
AXUIElementCopyAttributeValue(
frontMostApp, kAXFocusedWindowAttribute, (CFTypeRef *)&frontMostWindow
);
/* Get the title of the window */
AXUIElementCopyAttributeValue(
frontMostWindow, kAXTitleAttribute, (CFTypeRef *)&windowTitle
);
/* Get the window size and position */
AXUIElementCopyAttributeValue(
frontMostWindow, kAXSizeAttribute, (CFTypeRef *)&temp
);
AXValueGetValue(temp, kAXValueCGSizeType, &windowSize);
CFRelease(temp);
AXUIElementCopyAttributeValue(
frontMostWindow, kAXPositionAttribute, (CFTypeRef *)&temp
);
AXValueGetValue(temp, kAXValueCGPointType, &windowPosition);
CFRelease(temp);
/* Print everything */
printf("\n");
CFShow(windowTitle);
printf(
"Window is at (%f, %f) and has dimension of (%f, %f)\n",
windowPosition.x,
windowPosition.y,
windowSize.width,
windowSize.height
);
/* Move the window to the right by 25 pixels */
windowPosition.x += 25;
temp = AXValueCreate(kAXValueCGPointType, &windowPosition);
AXUIElementSetAttributeValue(frontMostWindow, kAXPositionAttribute, temp);
CFRelease(temp);
/* Clean up */
CFRelease(frontMostWindow);
CFRelease(frontMostApp);
return 0;
}
Sine Benは、コメント内のすべてのウィンドウのリストを取得する方法を尋ねました。以下にその方法を示します。
「kAXFocusedWindowAttribute」の代わりに&quot; kAXWindowsAttribute&quot;を使用します。 AXUIElementCopyAttributeValue関数用。結果はAXUIElementRefではなく、AXUIElementRef要素のCFArrayであり、このアプリケーションの各ウィンドウに1つです。
他のヒント
アクセシビリティが最善の方法であることに同意します。しかし、手っ取り早くしたい場合は、AppleScriptも同様に機能します。