2013年5月10日

js-ctypes で Objective-C を使う (3)

Function Proxy 使ったらいい感じにできそうなのでしてみた。

"use strict"

let Cu = Components.utils;

Cu.import ("resource://gre/modules/ctypes.jsm");

/* なんかイイカンジにアレしてくれるアレ */
let objc = {
  /* はじめる */
  init : function () {
    this.lib = ctypes.open (ctypes.libraryName ("objc"));

    this.objc_getClass = this.lib.declare ("objc_getClass",
                                           ctypes.default_abi,
                                           ctypes.voidptr_t,
                                           ctypes.char.ptr);
    this.sel_registerName = this.lib.declare ("sel_registerName",
                                              ctypes.default_abi,
                                              ctypes.voidptr_t,
                                              ctypes.char.ptr);
    this.objc_msgSend = this.lib.declare ("objc_msgSend",
                                          ctypes.default_abi,
                                          ctypes.voidptr_t,
                                          ctypes.voidptr_t,
                                          ctypes.voidptr_t,
                                          "...");
  },
  
  /* おわる */
  release : function () {
    this.lib.close ();
  },
  
  /* メッセージを送る */
  send : function (listener, sel, args) {
    return this.objc_msgSend.apply (undefined,
                                    [listener, this.sel_registerName (sel)]
                                    .concat (args));
  },
  
  /* NSString を作る */
  str : function (str) {
    return this.classes.NSString.alloc ()
    .initWithUTF8String (ctypes.char.array ()(str))
    .autorelease ()();
  },
  
  /* メソッド呼び出しみたいにするプロキシ */
  proxy : function (obj, sels) {
    return Proxy.createFunction ({
        get: function (a, sel) {
          /* セレクタを記録する */
          return objc.proxy (obj, sels.concat (sel));
        }
      },
      function () {
        if (sels.length == 0) {
          /* セレクタが無い場合は中身の取得 */
          if (arguments.length == 0) {
            /* 引数が無い場合はそのまま */
            return obj;
          }
          else {
            /* 引数がある場合はキャスト */
            return ctypes.cast (obj, arguments [0]).value;
          }
        }
        
        /* メッセージを送る */
        let args = Array.prototype.slice.call (arguments, 0);
        let sel = sels.join (":");
        if (args.length > 0) {
          sel += ":";
        }
        let ret = objc.send (obj, sel, args);
        return objc.proxy (ret, []);
      });
  },
  
  /* 名前に対応するクラスを返すオプジェクト的なアレ */
  classes : new Proxy ({}, {
      get : function (_, name) {
        return objc.proxy (objc.objc_getClass (name), []);
      }
    })
};

/* ---- ここから本体 ---- */

/* はじめる */
objc.init ();

/* プールを作る */
let pool = objc.classes.NSAutoreleasePool.alloc ().init ();

/* スピーチシンセサイザを作る */
let voice = "com.apple.speech.synthesis.voice.kyoko.premium";
let synth = objc.classes.NSSpeechSynthesizer.alloc ()
  .initWithVoice (objc.str (voice)).autorelease ();

/* 喋っていただく */
let NSUTF8StringEncoding = ctypes.int32_t (4);
let text = objc.classes.NSString.alloc ()
  .initWithCString.encoding (ctypes.char.array ()("あ"),
                             NSUTF8StringEncoding)
  .autorelease ()();
synth.startSpeakingString (text);

/* 喋り終わるのを待つ */
while (synth.isSpeaking ()(ctypes.int32_t)) {
}

/* プールを開放 */
pool.release ();

/* おわる */
objc.release ();

引数 2 個以上のサンプルとして initWithCString を引っ張り出す。

なんか微妙に気持ち悪い感が無いでもない。
でも VALUE と VALUE_OF が別の形で解決できたのでちょっとマシ。

x.initWithCString.encoding (ctypes.char.array ()("あ"), NSUTF8StringEncoding)

これは、

[x initWithCString: "あ" encoding: NSUTF8StringEncoding]

これの事。
プロパティを並べる事で複数の引数を記述する。


あと、データの扱いを間違えると toString とか valueOf のメッセージがオブジェクトに飛ぶという危険な仕様。

0 件のコメント:

コメントを投稿