モディファイヤキーつきでキー入力をエミュレートする

May 15th, 2010  |  Published in CS

Mac アプリをつくっててだいぶハマってしまったのでその小話。

キーボード入力をエミュレートするには、 Foundation の CGEventCreateKeyboardEvent() を使うのが基本です。ドキュメントにもそう書いてある。

Quartz Event Services Reference:

CGEventRef event1, event2, event3, event4;
event1 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)56, true);
event2 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)6, true);
event3 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)6, false);
event4 = CGEventCreateKeyboardEvent (NULL, (CGKeyCode)56, false);
56 はシフトキーを表し、 6 は z キー。つまり、大文字の z (‘Z’) をタイプするよというサンプルコード、のはずなのですが、 Mac OS X 10.6.3 でやってみてもまるでうまくいかない。たんに z が入力されるにとどまります。

で、ぐぐっていると スタックがオーバーフローして:

CGEventRef event1, event2;
event1 = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)6, true);//'z' keydown event
CGEventSetFlags(event1, kCGEventFlagMaskShift);//set shift key down for above event
CGEventPost(kCGSessionEventTap, event1);//post event
のように、 CGEventSetFlags() をかまして、モディファイヤキーが押されているのをイベントに修飾せよ、とある。なるほど。

それでやってみると、たしかに Z が入力されます。I’m feeling lucky! と思うもつかのま、こんどは z に戻らない。むむむ、これは…

そこから一週間ほどもがいていたのですが、状態をリセットすればいいんじゃなかろうかと気づいてやってみたらうまくいった。バッドノウハウすぎるだろこれ..

けっきょくうまくいったコードは以下のようなものになりました。

- (void) postKey:(uint16_t) key withModifiers:(uint16_t) modifiers down:(BOOL)down
{
   CGEventRef event = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)key, down);


if (modifiers & MODE_SHIFT) { CGEventSetFlags(event, NSShiftKeyMask); } else if (modifiers & MODE_CONTROL) { CGEventSetFlags(event, kCGEventFlagMaskControl); } else if (modifiers & MODE_OPTION) { CGEventSetFlags(event, kCGEventFlagMaskAlternate); } else if (modifiers & MODE_COMMAND) { CGEventSetFlags(event, kCGEventFlagMaskCommand); } else CGEventSetFlags(event, 0); // <<<<<<< これ
CGEventPost(kCGSessionEventTap, event); CFRelease(event); }

CGEventSetFlags に指定するフラグは OR でとればよいので、もっとかんたんに書けるはず。しかし今夜はここで力尽きた..


教訓: 仕様、コードに一致していないドキュメントはむしろ害になり得るので気をつけよう。自戒自戒。

Tags:

Grand Central Dispatch をためす (1)

August 29th, 2009  |  Published in CS

Snow Leopard がでましたね!

OpenCL と Grand Central Dispatch がおもしろそうなので、そのへんのことを書いていくシリーズです。まずは Grand Central Dispatch について、手がかりのない状態からかるく使ってみるまで。

0. Grand Central Dispatch について

前に少し書いたように、Snow Leopard から入る並列実行基盤ですね。

1. Xcode でドキュメント探し

Xcode のドキュメントビューア (えらくインターフェイスがかわった) から、 Grand Central とか Dispatch とかをキーワードにして検索すると、それらしいタイトルがひっかかります。しかし、内容をみることはまだできないみたい。 ADC の上級メンバーならよいのでしょうが、ヒラ会員の我々は無理なの?

2. カンをつかう

mdfind dispatch とかしてみるわけです。すると

/usr/share/man/man3/dispatch.3

とかひっかかる。これはあやしい。 というわけで man 3 dispatchman 3 dispatch_async としていくと、概念を紹介するためのサンプルコードが書かれている man ページにあたります。ビンゴですね。

3. 書いてみる

6-9 行がミソですね。 global queue に、ブロックないの処理を非同期で実行するようつっこんでるわけです。

4. 実行してみる

% ./a.out
hoge
... (寝てる)
%

ちゃんと非同期で実行されてます。

5. 確かめてみる

gdb であそんでみましょう。

% gdb a.out
(gdb) b 7           # ブロック内で break
(gdb run
Starting program: /Users/mootoh/gcd_sandbox
[Switching to process 10433]


Breakpoint 1, __main_block_invoke_1 (.block_descriptor=0x100001080) at gcd_sandbox.c:7 7 printf("hoge\n");

とまりました。 switching to process とかでてますね。スレッド切り替えがおこったのでしょう。

(gdb) bt

0 __main_block_invoke_1 (.block_descriptor=0x100001080) at gcd_sandbox.c:7


1 0x00007fff85bc2dc7 in _dispatch_call_block_and_release ()


2 0x00007fff85ba1341 in _dispatch_worker_thread2 ()


3 0x00007fff85ba0c80 in _pthread_wqthread ()


4 0x00007fff85ba0b1d in start_wqthread ()



wqthread とな。 Worker Queue thread とかかな? スレッド全体をみてみましょう。

(gdb) info threads
* 2 port# 0x417 __main_block_invoke_1 (.block_descriptor=0x100001080) at gcd_sandbox.c:7
  1 port# 0x903 0x00007fff85bc1ace in __semwait_signal ()
(gdb) thread 1
(gdb) bt

0 0x00007fff85bc1ace in __semwait_signal ()


1 0x00007fff85bc195d in nanosleep ()


2 0x00007fff85c0f320 in sleep ()


3 0x0000000100000e94 in main (argc=1, argv=0x7fff5fbfe9b0) at gcd_sandbox.c:10



ちゃんと元スレッドがいますね。

6. その他

そういえば、 gcc に何のオプションも指定することなく、ふつうに Grand Central Dispatch のブロック構文が使えていました。また、特になにかライブラリをリンクすることもなく、ただ gcc -g gcd_sandbox.c これだけでビルドできて並列実行できるバイナリが生成されますね。うむむ。

7. まとめ

ADC ヒラ会員なので、まとまったドキュメントとして man 3 dispatch 周りだけをたよりに Grand Central Dispatch を覗いてみました。ブロック構文がちゃんと使えることをまず確認し、 gdb からもちゃんとマルチスレッドの動作が見える、というところまでを追いました。

あと気になるのは、ハイレベル API がどういうものなのか、というところです。 Cocoa 的なのがあるとすれば、 .framework にまとまってるだろうと思うのですけれど、それらしきものはまだ見つけられていません。

次回はもう少し掘り下げてみたいとおもいます。それまでにドキュメント手に入るようにならないかなぁ…

20009-09-02 21:13 JST 追記

その2 を書きました。

Tags: , , 技術紹介

Bonjour

June 2nd, 2009  |  Published in Uncategorized

iPhone アプリをつくってて、 Bonjour をつかえばいろいろと楽しいことができるんじゃない? と調べていました。ここまでのところをシェア。

Bonjour というのは、 DNS サーバがなくても、同じネットワーク内にいるコンピュータを見つけあうことができる技術。 Zero Configuration とか Rendezvous とか言われてたりもした。

準備

遊びでつかうなら、 service type = _wwdcpic あたりでローカルで遊ぶといいんじゃないかな。

本気でつかうなら、dns-sd にサービスの登録をする。 サービス名、説明、責任者といった情報をつけてメールすれば、そのうち受理されたりする。

Cocoa API

いいところ:

  • 使いやすく抽象化されてる
  • Mac, iPhone どちらでも同じように使える!

ちゃんと RubyCocoa でも使えたよ!

NSNetService

サーバ側。サービスを提供していることをアピールし、クライアントから接続されてくるのを待つ。

ながれ:

  • initWithDomain_type_name_port
  • setDelegate
  • publish

ここの type で指定するのを、 dns-sd に登録しておくということ。 クライアントとのやりとりは delegate のコールバックに通知される。 接続されたあとの処理は、ふつうのネットワークプログラムとおなじ。

NSNetServiceBrowser

クライアント側。サーバをてきとうに探し出して、接続する。

ながれ:

  • init
  • setDelegate
  • searchForServicesOfType

みつかったあとのサーバとのやりとりは、 delegate のコールバックに通知される。

Sample code

/Developer/Examples//Foundation/PictureSharing

このコードを読むと、だいたい把握できる。

あと、自分で書いたコードが github:remoteshortcuts にあります。 Mac with RubyCocoa, iPhone とごちゃまぜなかんじがたのしいかと。

つまづきポイント

  • NSNetService は、 stop を送られたあとにもう一度 publish で起動しようとしてもダメ (しぬ)
  • delegate を設定する前にメッセージを送っても意味なし

まとめ

想像以上にかんたんに使えました。 Cocoa フレームワークはすばらしい。 iPhone アプリどうしで小さなネットワークをつくって遊ぶようなときにさくっと使うと楽しそうです。勉強会とか。

Tags: ,

QSTwitter 1.4

March 1st, 2008  |  Published in Uncategorized

QuicksilverからTwitterに投稿するプラグイン、QSTwitterを1.4にアップデートしました。 ダウンロードはこちら。 まだ若干バギーです :) が、 つかってみてくださいね。

変更点

Triggerで一発ポスト !

後述のTrigger設定をすることにより、ショートカット一発でTwitterにポストできるようにしました。

これまで:

  • QS起動
  • テキストモードに移行 (ピリオド入力)
  • テキスト入力
  • タブキーでActionに移動
  • postと入力 (ここを抜かして、’Large Type’になるミスが多発していました)
  • ENTER でポスト

1.4:

  • Triggerのショートカット入力
  • テキスト入力
  • ENTER でポスト !

ステップ数で2倍、体感速度およびストレスでさらに倍程度速くなりました。

Trigger のセットアップ

カタログをつくったあと、図のような手順を踏みます。ショートカットキーはお好きなものを。ターゲットのとこをブランクにするのがコツかと。

参考: わかばマークのMacの備忘録 : Quicksilver/ Proxy Objects について

スクリーンショット

注意事項

1.3と同様です。

中のつくりについて

QSTwitterというダミーfollowingユーザをつくり、ここにreplyするとpublic timelineに発言するようなフェイクをつくることでTriggerを実現しました。

コード

CodeRepos : TwitterPlugin

以前のバージョン

  • 1.3 : 2008.02.22
  • 1.2 : 2008.01.23
  • 1.1 : 2007.12.22

関連エントリ

  • Quicksilver Twitter Plugin (日本語)
  • QSTwitter 1.1 (Quicksilver Twitter Plugin)
  • QSTwitter 1.2
  • QSTwitter 1.3
  • RubyCocoaを使ってQuicksilverプラグインを書く
  • もっとRubyCocoaでQuicksilverプラグインを書く
Tags: , , , ,

QSTwitter 1.3

February 22nd, 2008  |  Published in Uncategorized

QuicksilverからTwitterに投稿するプラグイン、QSTwitterを1.3にアップデートしました。 ダウンロードはこちら。 若干バギーです :)

変更点

Friendの補完

自分がfollowしているひと (= Friend) をカタログに保持し、 Quicksilverから補完入力できるようにしました。 アイコンが表示されるのがキュート。

Reply Action

補完入力したFriendに対して、reply Action でメッセージを送れるようにしました。 自動的に、@だれそれがメッセージの先頭につきます。

HTTP プロキシ

環境変数に、http_proxyが設定されている場合に、そのプロキシサーバを使うようにしました。

Action名の変更

これまでは、TwitterというActionでメッセージを送っていましたが、 postというActionに名前を変えました。

スクリーンショット

スクリーンキャスト

百聞は一見にしかずということで。

注意事項

  • Mac OS X 10.5.2 でしか確認していません。Leopardが必須です。RubyCocoaがインストールされているTigerでも、ひょっとしたらビルドできるかも。
  • JSONのRubyライブラリが必要です。sudo gem install jsonなどとしてインストールしてください。
  • インストールした直後、Quicksilverが固まります。これは、Friendすべてをダウンロードしてカタログ化しているためです。
  • 初回のカタログが生成されたあと、Quicksilverがクラッシュしたり暴走したりします。Quicksilverを再起動すると、ちゃんと動くようです。 (調査中)

中のつくりについて

これまではObjective-Cで書いていたのですが、RubyCocoaで書き直しました。 メリットとしては、以下のようなものがあります。

  • JSONが簡単に扱える
  • HTTP POST via プロキシができる (NSURLConnectionではなかなかうまくいかない)
  • デバッグがラク ( /reload と postすると、Rubyスクリプトが再読み込みされるようになってる)

また、コードを見てもらえると分かるのですが、カタログ化のためにダウンロードしたFriendのJSONを、 Marshal.dumpでPluginがインストールされた場所にキャッシュしています。 なんという手抜き。

コード

CodeRepos : TwitterPlugin

以前のバージョン

  • 1.2 : 2008.01.23
  • 1.1 : 2007.12.22

関連エントリ

  • Quicksilver Twitter Plugin (日本語)
  • QSTwitter 1.1 (Quicksilver Twitter Plugin)
  • QSTwitter 1.2
  • RubyCocoaを使ってQuicksilverプラグインを書く
  • もっとRubyCocoaでQuicksilverプラグインを書く
Tags: , , , ,

LeopardでのQuartzComposerカスタムプラグインづくり

February 19th, 2008  |  Published in Uncategorized

Ruby会議2008CFPとして、RubyCocoaでインタラクティブにMacのプラグインをつくるよ、みたいなものを書いて出してみました。 出してから、そういえばLeopardになってからQuartzComposerで遊んでないなあと気づき、ちょっと触ってみたらえらく変わっていたので、今日調べたことをカスタムプラグインをつくるという観点でメモっときます。

カスタムプラグインがオフィシャルに

最大の変更点はこれです。 Appleの丁寧なドキュメント Introduction to Quartz Composer Custom Patch Programming Guide を読めば、たちまちつくれるようになるのではないかと。サンプルも充実してて、/Developer/Examples/Quartz\ Composer/Plugins/にごろごろ転がってます。 これまで、kineme.net:Xcode Template for Custom Quartz Composer Patches を使い、非公開のAPIでプログラミングしていたのに対して大きな進歩です。

Port指定にはpropertyをつかう

Objective-C 2.0で導入された、propertyという機能をつかって入出力Portの指定をコードでやるようになっています。 これまでは、インスタンス変数名の先頭にinput/outputがあれば、それがPortになっていました。

Portの増減が動的にできる

RubyCocoaでpropertyを使う方法を知らないのでどうしようかなあと思ってたのですが、 addinputPortWithTypeとかaddOutputPortWithTypeというメソッドを使えば動的にPortが加えられます。

注意すべきことは、これらのメソッドはプラグインのexecuteメソッドに入れてはいけないことです (例外があがってQuartzComposerが止まってしまいます)。んじゃどうするかということで、ExampleのCommandLineToolFreeFrameHostを調べてみると、どうやらQuartzComposerのインスペクタから受け取るイベントのハンドラで、これらのメソッドを呼ぶようにしています。なるほど。

インストール場所が変わっている

これまでは、/Library/Graphics/Patch にプラグインを置くのが流儀だったのですが、Leopardからは/Library/Graphics/Quartz\ Composer\ Plug-Ins/ になりました(~/Library以下でもOK)。


カスタムプラグインをつくるという点から、Leopardになって気づいたことをまとめました。次はいよいよ、RubyCocoaでカスタムプラグインづくりです。

関連エントリ

  • RubyCocoaでQuartzComposerパッチ
  • SocketReaderPatch
  • IRCのメッセージをQuartzComposerに表示
  • QuartzComposer の CustomPatch をつくるための Xcode template
  • QuartzComposer の CustomPatch で Signal を生成する方法
  • ApacheLogPatch 0.1
  • RubyCocoa で QuartzComposer CustomPatch をつくりたい
  • RubyCocoa で QuartzComposer CustomPatch (2)
Tags: , ,

もっとRubyCocoaでQuicksilverプラグインを書く

February 6th, 2008  |  Published in Uncategorized

RubyCocoaを使ってQuicksilverプラグインを書く の続編です。 前回書いたあと、hisaさんからアドバイスをもらい、RubyCocoaプラグインを書くことができるようになりました。

ポイントは以下の2つ。

  • NSPrincipalClass
  • RubyActionClass < OSX::QSActionProvider

コードは、CodeReposのものをupdateしておきました。 hisaさんコードがたくさん入っていますが、煮るなり焼くなり好きにせよとのことなので、前回と同じ修正BSDライセンスとします。

NSPrincipalClass

Quicksilver プラグインに限らず、Cocoaで何らかのBundleを書く際には、Info.plistの NSPrincipalClass にクラス名を指定しておくと、そのクラスの +load() メソッドが呼ばれてプラグインが組み込まれるようです。 なので、ここでRBBundleInit()を呼んでRubyCocoaを初期化すると。とてもスマート。 前回はこのことを知らずに、無理矢理なハックをしていたのでした。

RCLoader.m:

ここでは、NSPrincipalClassにRCLoaderというクラスを指定しています。

 RCLoader

  • (void)load { static bool installed = 0;

    if (! installed) { if (! RBBundleInit("load_ruby.rb", [self class], self)) { installed = true; } } }


load_ruby.rb:



そんで、RubyCocoaの初期化の中で、必要な.rbを読み込んでおく、と。
def load_ruby_programs(bundle, logger)
  path = bundle.resourcePath.fileSystemRepresentation
  rbfiles = Dir.entries(path).select {|x| /.rb\z/ =~ x}
  rbfiles -= [ File.basename(FILE) ]
  rbfiles.each do |path|
    require( File.basename(path) )
  end
end


OSX.init_for_bundle do |bundle, param, logger| load_ruby_programs(bundle, logger) end

RubyActionClass < OSX::QSActionProvider



前回説明したように、QuicksilverのActionを記述するには、QSActionProviderを継承したクラスでメソッドを用意します。 今回は、RubyのクラスでQSActionProviderを実装します。RubyCocoaのパワーがすごすぎる。
class RubyAction < OSX::QSActionProvider
  def act(arg)
    val = arg.stringValue
    OSX::QSObject.objectWithString('Hello world, ' + val)
  end
end


あとは、このRubyActionクラスを、Info.plistのactionClassに、actionSelectoract:に指定しておけば、ちゃんとactが呼ばれます。

疑問


  • OSX.require_framework とか使わないで、QSActionProviderとかのQS frameworkが使えてるのはなぜ?
  • hisaさんは、この方法だとCPU usageが高くなると懸念されているのですが、僕のところではいまひとつ高くなってるように見えません。why?

まとめ



RubyCocoaで、Quicsilverプラグインを書く方法をまとめました。 これで、あとはRubyのクラスとInfo.plistを書くだけでどんどんプラグインが書けますね。みんながんばれ!
あと、自分でもイマイチだなーと思うことでも、何かしら書いて晒すことが大事なんだなと。そんで改善していければOKなんだ。 hisaさん、ありがとうございました。
Tags: , , ,

Write a Quicksilver plugin with RubyCocoa

February 3rd, 2008  |  Published in Uncategorized

I wrote a Quicksilver plugin with RubyCocoa, that adds “hello world” to the passed string.

  • Xcode project codes : in CodeRepos
  • License : revised BSD

It should work on Tiger/Leopard if RubyCocoa is installed.

How does it work

As I mentioned in QuartzComposer CustomPatch with RubyCocoa, we just call RBBundleInit() function in plugin initializaiton phase to write some plugin with RubyCocoa. But wait, where should be the initializaiton code in Quicksilver plugin ?

We write actual plugin behavior in the class that inherits QSActionProvider, so I tried to call RBBundleInit() in the init() method in that class… however, that resulted in crashing Quicksilver :(

Then I called RBBundleInit() at only first time in performActionOnObject() of the actual Action class, and made references between Objective-C and Ruby class instances.

After that, I implemented an actual action behavior in Ruby class, and delegates from Objective-C to Ruby method, thats’ all. This is ugly, confusing, … I know, but it does work well :)

Code Snippets

ActionProvider in Objective-C:

 (QSObject *)performActionOnObject:(QSObject *)dObject{
  // initialize RubyCocoa
  static bool loaded = false;
  if (!loaded) {
    if (RBBundleInit("qs_action.rb", [self class], self)) {
      NSLog(@"[RubyCocoaPluginAction.performActionOnObject] RBBundleInit failed"
);
      abort();
    }
    loaded = true;
  }


// delegate actual action to Ruby class QSObject *ret = [QSObject objectWithString:[rb_ act:dObject]]; return ret; }

RubyCocoa side:

class Action
  def initialize(logger)
     = logger
  end


# write something great :) # - arg : QSObject def act(arg) val = arg.stringValue (val) 'Hello world, ' + val end end # Action
require 'osx/cocoa' OSX.init_for_bundle do |bdl, owner, log| # bdl - the bundle related with the 2nd argument of RBBundleInit # owner - the 3rd argument of RBBundleInit as optional data # log - logger for this block
act = Action.new(log) owner.setInstance act end

Future works

  • Better initializaiton. It should be in constractor of some class)
  • Inherits QSActionProvider by Ruby class (more pure Ruby)
  • Use ns_import ?

It should not be “With RubyCocoa”, but rather “By RubyCocoa”.

Conclusion

I made a start point to write a Quicksilver plugin by RubyCocoa. It is timing of initialization that is to be considered about writing some bundle in RubyCocoa. I made it in this article somehow. This article is for someone who wants to create Quicksilver plugin by Ruby, not learning unfamiliar Objective-C.

Reference

  • QuartzComposer CustomPatch by RubyCocoa
  • PyObjC Plug-ins : Article about writing Quicksilver plugin by PyObjC (a bit obsolete…)
Tags: , , ,

RubyCocoaを使ってQuicksilverプラグインを書く

February 3rd, 2008  |  Published in Uncategorized

RubyCocoaを使って、Quicksilverのプラグインを書いてみました。 受けとった文字列に、”hello world” をくっつけるだけのものです。

プロジェクト一式のコードは、CodeReposに。 ライセンスは修正BSDで。 RubyCocoaがインストールされていれば、TigerでもLeopardでも動くと思います。

仕組み

RubyCocoaでQuartzComposer CustomPatchのときに書いたように、 CocoaアプリじゃなくてプラグインをRubyCocoaで書くには、RBBundleInit() という関数をプラグインの初期化の際に呼んであげればよいわけです。 ところが、Quicksilverプラグインの初期化ってどこなんだろうという。

QSActionProviderを継承したクラスで実際のプラグインの動作 (Action) を記述するので、このクラスのinit()で RBBundleInit() を呼べばいいかなーと考えてやってみたところ、Quicksilverをクラッシュさせてしまいます。こまった。

しかたないので、Actionを記述する performActionOnObject() の中で、RBBundleInit()を最初の一度だけ呼ぶようにしました。その中で、Objective-CからRubyクラスのインスタンスを参照できるようにしておきます。

あとは、RubyのクラスでActionを実装し、Objective-CからRubyのメソッドを呼んで移譲します。これでできあがり。 かなり強引で美しくないのですが目的は果たせるかな、というダーティハックです。

コード片

Objective-CのActionProvider:

 (QSObject *)performActionOnObject:(QSObject *)dObject{
  // initialize RubyCocoa
  static bool loaded = false;
  if (!loaded) {
    if (RBBundleInit("qs_action.rb", [self class], self)) {
      NSLog(@"[RubyCocoaPluginAction.performActionOnObject] RBBundleInit failed"
);
      abort();
    }
    loaded = true;
  }


// delegate actual action to Ruby class QSObject *ret = [QSObject objectWithString:[rb_ act:dObject]]; return ret; }

RubyCocoa側:

class Action
  def initialize(logger)
     = logger
  end


# write something great :) # - arg : QSObject def act(arg) val = arg.stringValue (val) 'Hello world, ' + val end end # Action
require 'osx/cocoa' OSX.init_for_bundle do |bdl, owner, log| # bdl - the bundle related with the 2nd argument of RBBundleInit # owner - the 3rd argument of RBBundleInit as optional data # log - logger for this block
act = Action.new(log) owner.setInstance act end

あと、やるとしたら

  • もうちょっときれいに初期化する (何らかのクラスのコンストラクタが望ましい)
  • QSActionProviderをRubyのクラスで継承する (よりRubyだけで書けるように)
  • ns_importとかつかってみる?

「RubyCocoaを使って」 よりも、「RubyCocoa」、スマートにプラグインを書けるようにしたいものです。 まぁ、まずは第一歩ということでひとつ。

まとめ

RubyCocoaを使って、Quicksilverプラグインをつくるためのとっかかりについて書きました。 RubyCocoaでバンドルを書くときに悩むのは初期化のタイミングだと思います。 今回はここを無理矢理解決しました。 Objective-Cを学んでる時間があったら、慣れ親しんでるRubyでちゃちゃっと コード書きたいよ! という方の参考になればと思います。

参考

  • RubyCocoaでQuartzComposer CustomPatch : QuartzComposerのCustomPatchもCocoaのバンドルなので、このときの経験が活かせました。
  • PyObjC Plug-ins : 本家による、PyObjCでQuicksilver pluginをつくるための情報。ただし、リンク先があちこちロストしてる ><
  • ひ日誌 : RubyCocoaで Quicksilver pluginは書けるのか? : ここが発端。

2008.02.03 追記

RubyCocoaのhisaさんにコメントいただき (!)、もっとよい方法を教えてもらいました。 こちらについても、まとめて別エントリにします。

Tags: , , ,

QSTwitter 1.2

January 23rd, 2008  |  Published in Uncategorized

QuicksilverからTwitterに投稿するプラグイン、QSTwitterを1.2にアップデートしました。 ダウンロードはこちら

変更点は2つ。

‘+’ 問題 を解決

1.1までは、‘+’を含むメッセージを入力するとスペースに変換されてしまうという問題がありました。 これは、NSString.stringByAddingPercentEscapesUsingEncoding が ‘+’ をエスケープしてくれないことに起因していたのですが、今回はやっつけで /+/%2B/g するようなクイックハックで解決しています。

かわいいアイコン :)

のアイコンがかわいかったので、使っていい? とところ快諾されたので、使うことにしました。(Thanks Alex !) プラグイン選択画面、プラグインの設定画面、そしてメッセージを送るときに表示されます。 ドット絵風でとってもキュート。


ぜひぜひお使いくださいね。

Tags: , , ,