2011年3月6日日曜日

Cocoa ObjC Spin デスクトップでマウスころころでSpacesを切り替える

意外と簡単にできてしまって自分でも驚いている。タイトル通り、「デスクトップでマウスホイールを回すとSpacesのWorkspaceが切り替わる」という機能。

デスクトップ上にでかい透明ウィンドウをはりつけて、という方法しか思いつかなかったけれど、以前一度触れた「NSEvent addGlobalMonitorForEventsMatchingMask:handler:」を使えばけっこう簡単に実現できる。

[NSEvent addGlobalMonitorForEventsMatchingMask:NSScrollWheelMask
              handler:^(NSEvent* event) {[self isDeskTop:event];}];
てな感じでイベントハンドラを指定しておけば、とりあえずどこで起ころうとscrollWheelイベントは拾うことができる。あとはeventの中から、scrollWheelが起きた場所なりウィンドウIDなりを取り出せば、それがデスクトップで起きたかどうか分かるはず。

こういう時はCGSPrivate.hの中の
extern CGError CGSGetWorkspaceWindowCount(const CGSConnection cid, CGSWorkspace workspaceNumber, int *outCount);
  extern CGError CGSGetWorkspaceWindowList(const CGSConnection cid, CGSWorkspace workspaceNumber, int count, int* list, int* outCount);

この2つを使えばなんとかなるはずなのでさっそくググって調べてみた。(もちろんxcatsan師匠ご推奨のCGWindowListCopyWindowInfoという手もあるわけだが、こっちは「全部のワークスペースのWindow」が拾えてしまうのでかえってloopに時間がかかりそう)

蛇の道は蛇、ちゃんとこの2つのプライベート関数を使って「現在のワークスペースにあるWindowを取得する」方法がまんまネット上に落ちていた。

Paste number 31828: leet

便利そうなコードをコピペしておいておく、というサイトなのかしらん。
ここに載っていたコードはWindowのID、領域のRect、タイトルを取得しているのだが、デスクトップで起きたかどうかはwindowIDさえあればいいようなので多少編集して、次のようにしてみた。

int err;
 CGSConnection connection;
 int workspace;
 int numberOfWindows;
 int *windowList;
 BOOL notDeskTop=NO;
 
 connection = _CGSDefaultConnection();
 CGSGetWorkspace(connection, &workspace);
 CGSGetWorkspaceWindowCount(connection, workspace, &numberOfWindows);
 if (numberOfWindows>0) {
  
  windowList = (int*)malloc(sizeof(int) * numberOfWindows);
 
  err = CGSGetWorkspaceWindowList(connection, workspace, numberOfWindows, 
         windowList, &numberOfWindows);
 
 //NSLog(@"There are %d windows", numberOfWindows);
 
  if( err != 0)
   NSLog(@"Couldn't get window list, err=%d", err);
  else
  {
   //NSLog(@"Got window list!");
 
   int windowId;
  
   for(int i = 0; i < numberOfWindows; ++i)
   {
    windowId = windowList[i];
    if (windowId==[event windowNumber] ) {
     notDeskTop=YES;
    }
   }
  }
 }
 if (notDeskTop==NO) {
  [self scrollWheel:event];
 }

if (numberOfWindows>0)を入れないとウィンドウなしのWorkspaceだと必ずエラーが出る、と。「notDesktop」などというダサいflagはもちろん私が入れた豚野郎コードでありますな。もう少しなんとか書き直そう。とりあえず動けばいい、というコードなんで。

CGSGetWorkspaceWindowListで得られるwindowIDはeventのwindowNumberと同じなので、どのwindow上のeventかの判断か簡単にできる。Workspace上のどのウィンドウ上でもなければデスクトップと判断して、マウスイベントハンドラを呼び出している。

これだけでデスクトップ上、どこでマウスホィールを回してもWorkspaceが切り替えられた。うむ、いいぞ。

む?書き終わって気づいたんだけど、デスクトップにウィンドウをもっていないアプリの上でホイール回しても切り替わってしまいますな。Dockの上とかMenuバーの上とか。ま、いいか。

0 件のコメント:

コメントを投稿