2011年2月15日火曜日

Cocoa ObjC CGSSetUniversalOwner

CGSSetUniversalOwnerに行き着くまでの経過をメモ。

CGSPrivate.hの中にはウィンドウを操作するらしい関数がいくつかある。それが実際に動くか試してみた。

例えば

extern CGError CGSOrderWindow(const CGSConnection cid, const CGSWindow wid, CGSWindowOrderingMode place, CGSWindow relativeToWindowID /* can be NULL */);

これはきっとウィンドウを画面の一番上に持ってくる関数だろう。(いや、もしかしたら「常に一番上に表示」状態にするのかも・・・。ちゃんと試していないので実際は不明)

extern CGError CGSGetWindowAlpha(const CGSConnection cid, const CGSWindow wid, float* alpha);

これは明らかにウィンドウの透明度を設定する関数。どちらの関数も第1引数はCGSConnection cidで、これは次の関数で得ることのできる「現在のプロセスのコネクションID」。

/* Get the default connection for the current process. */
extern CGSConnection _CGSDefaultConnection(void);

自分のアプリケーションとCPUのプロセスを一意に結びつけている番号、みたいなものでしょうか。第2引数ではconst CGSWindow widを使うものが多い。これにはNSWindowクラスのプロパティwindowNumberを入れればいい。ただし、他のアプリのwinddowNumberを入れるとエラーになる。結局、const CGSConnection cidで操作できるのはほとんど自分のアプリのウィンドウだけ、らしい。

wid関連の操作でない時は自由にいろいろなウィンドウが操作できる。例えばこの関数。

extern CGError CGSMoveWorkspaceWindows(const CGSConnection connection, CGSWorkspace toWorkspace, CGSWorkspace fromWorkspace);

Workspace1にあるウィンドウを全部Workspace2へ、なんてことが簡単にできてしまう。

ここでこういう関数を発見。

extern CGError CGSGetConnectionIDForPSN(const CGSConnection cid, ProcessSerialNumber *psn, CGSConnection *targetConnection);

任意のアプリのConnectionIDを得ることができる?
それで得たIDを第1引数に指定すれば、任意のアプリの任意のウィンドウを操作できるのか?

というわけでProcessSerialNumberをデベロッパドキュメントで調べたら、

GetProcessForPID(pid ,&psn)
という関数があった。PIDがわかれば、そのProcessSerialNumberを得ることができる、と。そこでこんなふうに書いてみた。

 CGSConnection targetConnection;
 ProcessSerialNumber psn;
 
 GetProcessForPID([_app _pid] ,&psn);
 NSLog(@"%@:%d:%o",[_app _name],[_app _pid], psn.lowLongOfPSN);
 CGSGetConnectionIDForPSN(cid, &psn, &targetConnection);
 CGSOrderWindow(targetConnection, [sender tag], kCGSOrderAbove , 0);

第1引数を他のアプリのConnectionIDに替えてみたけれど・・やはりエラーになりました。orzだめか。

この他、ニセのNSEventをでっちあげてみる、とか、Notificationを投げてみる、とかやってみましたがことごとくだめ。

先日の3連休はこの試行錯誤でほとんどなくなりましたね。

cocoadev CGSPrivate.h

CGSPrivate.hはもともとこのフォーラムからコピペしてたわけだが、あとになって「Warp」のプロジェクトフォルダがコピーしたものを使っている。

本日、いろいろ調べているうちにまたまたこのフォーラムに行き着いて、ずーっとページをスクロールさせたらこんなことが書いてあるではないか。

So, I've tried writing a little utility that would change the alpha value of any window, but it only works for windows in my application... What can I do to make it work with any cocoa window?

おお、まさに私の知りたいことではないか!これに対するレスポンス。

You can't. A "special" connection to the WindowServer? is required to mess with other application's windows, and this connection is maintained exclusively by the Dock. If you quit the Dock you can take control of other windows... but good luck getting your users to ditch the dock :)

orz。そうか、そういうことだったのか。自分のアプリのウィンドウしか操作できないのか・・。
ま、納得しました。HyperDockが「ユニバーサルアクセス」を必要とする理由はこれか。Dockは不可触領域のようですな。
CGSSetUniversalOwnerのUniversalOwneが「maintained exclusively by the Dock」なんでしょう。

けっこうディープに調べた感を得ることができたここ数日だけど、結局やりたことができないとわかってフラストレーションが溜まってしまった。

次はGUI関連で実験していこう。まずは「Viewをリサイズする」かな。

0 件のコメント:

コメントを投稿