2011年1月17日月曜日

MacRuby・いきなりダークサイドに突入・CGSGetWorkspace

MacRubyから、cocoaのプライベート関数であるCGSGetWorkspaceを使ってみた。

午後からお休みをとって帰宅。家庭の用事をあれこれ済ませてもやはり時間の余裕があってMacRubyでできることを調べる。

(旧)cocoaの日々さんの「Spaces - アクティブなspaceの番号を得る(プライベート関数使用)」というアーティクルでCGSGetWorkspace という関数を使えばSpacesをいじれることを学習しました。これは昨晩すでに酔っ払ってから調べがついたため(それでもじゃあCGSGetWorkspaceのGetをSetにすればSpacesを動かせるんじゃないの?え?googleさんよ、とやっていたらやっぱりそのようだ、ということまではわかった)、本日改めて調べてみました。なんのことはない、「MacRuby CGSGetWorkspace」で検索しただけなんですが。

叩けよさらば開かれん、そうすると蛇の道は蛇でしてやっぱりこういうページに行き当たる。

Mouvement dans les spaces avec MacRuby (et Cocoa)

これ、何語?という状態ですが、ありがたいことにコードは読める。なるほど、プライベートな関数は一旦ObjCのclassにして、それをMacRubyから使うのか。

参考のために上記サイトのヘッダとインプリメントをコピーしておきます。
ヘッダ、Spaces.h。
//
//  Spaces.h
//  RConsole
//
//  Created by greg on 12/03/10.
//  Copyright 2010 Grégoire Lejeune. All rights reserved.
//

#import 
#import 
 
@interface Spaces : NSObject {

}
 
 - (NSInteger)spaceNumber;
 - (void)moveWindowToCurrentSpace:(NSWindow *)win;
 - (void)moveWindow:(NSWindow *)win toSpaceNumber:(NSInteger)space;
 - (NSInteger)getSpaceNumberOfWindow:(NSWindow *)win;
 @end

Spaces.m。
//
//  Spaces.m
//  RConsole
//
//  Created by greg on 12/03/10.
//  Copyright 2010 Grégoire Lejeune. All rights reserved.
//
 
 #import "Spaces.h"
 
 @implementation Spaces
 
 - (NSInteger)spaceNumber {
    NSInteger space = -1;
    CGSGetWorkspace(_CGSDefaultConnection(), &space);
    return space;
  }  

 - (NSInteger)getSpaceNumberOfWindow:(NSWindow *)win {
    NSInteger windowId = [win windowNumber];
    NSInteger space = -1;
    CGSGetWindowWorkspace(_CGSDefaultConnection(), windowId, &space);
    return space;
  }
 
 - (void)moveWindowToCurrentSpace:(NSWindow *)win {
    [self moveWindow:win toSpaceNumber:[self spaceNumber]];
  }
 
 - (void)moveWindow:(NSWindow *)win toSpaceNumber:(NSInteger)space {
    NSInteger windowId = [win windowNumber];
    if(windowId != -1) {
       CGSMoveWorkspaceWindowList(_CGSDefaultConnection(), &windowId, 1, space);
     }
  }
 @end

で、MacRubyでプロジェクトをつくり、WindowにラベルとボタンだけはりつけてControllerを作る。

class MyController < NSWindowController
attr_accessor :my_label
 def init
  super
  @my_space=Spaces.new
  self
 end
 
 def button_action(sender)
  number=@my_space.spaceNumber
  my_label.setStringValue number.to_s
 end
 
end

ボタンを押すとSpacesの番号が出るはず・・・なんですが-4976594とか表示されます。 はて・・・。intだから整数のはずなんだけど・・・。特にエラーが出るわけでもないので、とりあえずMacRubyからプライベート関数が使えることがわかっただけでも万々歳としよう。 ・・・と考えるわけですが、どう見てもintじゃない数値が返ってくるのは変です。いろいろなサイトで CGSGetWorkspaceについて読んでいくと、返ってくるのはどうやらポインタらしい。ということはnumber.to_sとかしてもだめだ。numberにはアドレスが入っている(という表現でいいのかな)なら、そのアドレスに入っているint型の数値を読まなければ。 で、MacRubyにはPointer型がある、という記事をどこかで読んだ記憶があったので検索。おお、MacRubyのコミッタさんの@watson1978さんのはてなだった。
MacRubyのPointerクラスについて
そこでPointer型を使ってコードを書き換えてみる。
def button_action(sender)
  number=Pointer.new('i')
  number[0]=@my_space.spaceNumber
  my_label.setStringValue number[0].to_s
end
わーい、これでちゃんと1とか2とか表示できるようになった。
ボタンを押すと現在のワークスペースを表示するという。 で、画像の中のボタン2は何かというと、
- (void)setSpaceNumber {
 NSInteger space = -1;
 CGSGetWorkspace(_CGSDefaultConnection(), &space);
 CGSSetWorkspace(_CGSDefaultConnection(),space-1);
}
こういう関数を書いておいて、
def button2_action(sender)
  @my_space.setSpaceNumber
end        
こうやってみる。見事、Spacesが瞬時に動くようになりました。ただし、1引いてるだけなのでSpacesの「1」で実行すると恐ろしいことになるに違いない(^^;)。また、設定しているSpaces変更のキーボードショートカット(ctrl-2とか)が一時的に効かなくなります。なんせど素人のやっていることなので危険。

ただ、全く不可能、ということではない、とわかったのが楽しかったです。これで次はマウスのキーイベントか。

0 件のコメント:

コメントを投稿