Cocoa::EventLoop
そういえば Cocoa::EventLoop と言うモジュールを書いたんでした。
これはもともと AnyEvent::Impl::NSRunLoop として書いていたイベントループ処理部分だけを単体モジュールとして切り出した物で、 AnyEvent を使わなくても Cocoa のイベントループを Perl から使用できるというものです。
くわしくはドキュメントを参照してもらうとして、Cocoa::Growl を使ったサンプルを載せてみます:
use Cocoa::EventLoop;
use Cocoa::Growl;
my $done = 0;
growl_notify(
name => 'Notification Name',
title => 'Hello',
description => 'Cocoa World!',
on_click => sub {
$done++;
},
on_timeout => sub {
$done++;
},
);
Cocoa::EventLoop->run_while(0.1) while !$done;
これを AnyEvent で書くと:
use AnyEvent 5.30;
use Cocoa::EventLoop;
use Cocoa::Growl;
my $cv = AnyEvent->condvar;
growl_notify(
name => 'Notification Name',
title => 'Hello',
description => 'Cocoa World!',
on_click => sub {
$cv->send;
},
on_timeout => sub {
$cv->send;
},
);
$cv->recv;
ほぼ一緒ですね。 また、この例ではわからないですがtimerやioのインタフェースもAnyEventに合わせてあるのでほとんど同じ感じで使うことができます。
ただまぁ、AnyEventには豊富なライブラリがあるため、上記のGrowlのサンプル以上に複雑なことをする場合は素直にAnyEventを使うのがいいかと思います。
AnyEvent 5.3 Released
出てました。気がつきませんでした。
このバージョンから AnyEvent::Impl::Cocoa が入りました。これは Cocoa::EventLoop を AnyEvent から使うアダプターで、
use Cocoa::EventLoop;
していると自動的に使われます。したがって、
use AnyEvent;
use Cocoa::EventLoop;
# AnyEventを使用したコード...
と言うように書くと自動的に Cocoa のイベントループで AnyEvent が動作するというわけです。 こうしておけば Cocoa::Growl など、Cocoa::EventLoop を必要とするモジュールをシームレスに AnyEvent 内で使うことが出来て便利です。
なお、AnyEvent::Impl::NSRunLoop は DEPRECATED ってことでとりあえずドキュメントに注意書きを加え、さらに数週間後にはCPANから削除するつもりです。
ふるいPerlをいれる
perlbrew を導入したのでもりもりテスト環境を作れるようになったわけですが、perl-5.8.8 をいれようとしたら
Can't open makefile: No such file or directory.
Can't open x2p/makefile: No such file or directory.
make: *** No rule to make target `<command-line>', needed by `miniperlmain.o'. Stop.
make: *** No rule to make target `<command-line>', needed by `miniperlmain.o'. Stop.
のように make がこけてしまってインストールできなかった。 ググってみるとどうも新しいgccだと古いPerlはうまくビルドできないらしい。
解決法としては makefile (OSXではGNUmakefile) の command-line
を含む行は消してあげるというのでなんとかなるみたい。
以下のようにしてみた:
$ ./Configure -de -Dprefix=$HOME/perl5/perlbrew/perls/5.8.8
$ perl -i~ -nle 'print unless /command-line/' GNUmakefile x2p/GNUmakefile
$ make
$ make test
$ make install
これでちゃんとperlbrewからも認識できてて
$ perlbrew switch 5.8.8
とかでswitchできました。まる。
ユニバーサルバイナリ作成用シェルスクリプト
iPhone用にビルドしたライブラリは実機用(armv6/armv7)とシミュレータ用(i386)のユニバーサルバイナリとして作成しておくと使い勝手が良いです。 その分ビルドは面倒になるのですが。。
僕は通常以下のようにしてユニバーサルバイナリを作成してます。
- まずprefixをそれぞれ
~/dev/iphone/lib/curl-7.20.1-armv6
~/dev/iphone/lib/curl-7.20.1-armv7
~/dev/iphone/lib/curl-7.20.1-i386
などとしてそれぞれのアーキテクチャ用にビルドをする - 適当に書いた bundle.sh でユニバーサルバイナリ化
この bundle.sh
は上の例だと
./bundle.sh ~/dev/iphone/lib/curl-7.20.1
として実行すると ~/dev/iphone/lib/curl-7.20.1-armv6
~/dev/iphone/lib/curl-7.20.1-armv7
~/dev/iphone/lib/curl-7.20.1-i386
が全部がっちゃんこした ~/dev/iphone/lib/curl-7.20.1
ができるという寸法です。
この方法だとまだ3つ分手動でビルドするのがめんどくさいので、そこも自動化したいところですね。
ライブラリをユニバーサルバイナリでインストールする
以下のサイトが詳しい
http://macwiki.sourceforge.jp/wiki/index.php/UniversalBinary
Imager
に必要な libjpeg
などをユニバーサルバイナリにしてみた時のメモ
-M
などがついていて -arch
が複数指定できない場合以外は
CFLAGS='-arch x86_64 -arch i386 -isysroot /Developer/SDKs/MacOSX10.6.sdk' CXXFLAGS=$CFLAGS
とかすればいいということだが、libjpeg
は -M
られていて無理だったので、amd64 と i386 という二つのディレクトリにソースコードを展開、それぞれ以下のオプションで make まで終わらす:
$ cd amd64
$ CFLAGS='-arch x86_64 -isysroot /Developer/SDKs/MacOSX10.6.sdk' CXXFLAGS=$CFLAGS ./configure ...
$ make
$ cd ../i386
$ CFLAGS='-arch i386 -isysroot /Developer/SDKs/MacOSX10.6.sdk' CXXFLAGS=$CFLAGS ./configure ...
$ make
make されてできたバイナリを lipo コマンドでユニバーサルバイナリ化する。 上記サイトを参考にして、
#!/bin/sh
filelist=$(find ./amd64 -type f |grep -v \\.o$ | xargs file | sed -e 's,^\./amd64/,,g' | \
grep -E \(Mach-O\)\|\(ar\ archive\) |sed -e 's,:.*,,g' -e '/\for\ architecture/d')
for i in $filelist
do
echo $i
/usr/bin/lipo -create amd64/$i i386/$i -output `basename $i`
mv -f `basename $i` amd64/$i
done
このようなスクリプトを書いて、amd64 などのディレクトリの一つ上の階層で実行すると、amd64 ディレクトリのバイナリがユニバーサルバイナリ化されるという仕組みを作ってみた。
その後、amd64 のほうで make install
してやれば OK。
Snow Leopard の Perl とアーキテクチャ
Snow Leopard には二つの Perl がインストールされている
- /usr/bin/perl5.10.0
- /usr/bin/perl5.8.9
デフォルトの /usr/bin/perl
は 5.10.0 のほう。それぞれユニバーサルバイナリになっていて、
$ file /usr/bin/perl5.10.0
/usr/bin/perl5.10.0: Mach-O universal binary with 3 architectures
/usr/bin/perl5.10.0 (for architecture x86_64): Mach-O 64-bit executable x86_64
/usr/bin/perl5.10.0 (for architecture i386): Mach-O executable i386
/usr/bin/perl5.10.0 (for architecture ppc7400): Mach-O executable ppc
$ file /usr/bin/perl5.8.9
/usr/bin/perl5.8.9: Mach-O universal binary with 2 architectures
/usr/bin/perl5.8.9 (for architecture i386): Mach-O executable i386
/usr/bin/perl5.8.9 (for architecture ppc7400): Mach-O executable ppc
という感じで、5.10 は 64bit 版があるが、5.8 にはない。
で、普通に perl を実行すると perl5.10.0 は x86_64
で実行され、perl5.8.9 は i386
で実行される。
このアーキテクチャの差が結構くせ者で Snow Leopard 上で普通にライブラリなどを make すると x86_64
だけでビルドされてしまうため、そのようにして作ったライブラリはそのままでは perl5.8.9 からは使えないということになる。
デフォルトの 5.10.0 だけ使っている分にはなにも問題はないのだが、残念なことにこの 5.10.0 というのはいろいろな問題があり、開発に使用することはおすすめできない状況。(せめて 5.10.1 にしてくれればいいのに)
したがって無用なトラブルを避けるためにも 5.8.9 の方を使用するか、自前でビルドした perl を使用するのが良い。
またこのように x86_64
、i386
両方のアーキテクチャの実行ファイルがあり得る Snow Leopard に対して自分でライブラリをインストールする場合それらもユニバーサルバイナリにしておくと良い。
念のため追記@2010-01-08T15:19:23+09:00: もちろんこれは 64bit 対応の CPU の場合の話。そうでない場合はどちらも i386 で実行されるのでこの問題は起こらない。
CocoaEmacsその後
フルスクリーン化以外にもいじりたくなったのでそれぞれブランチを切った。
- feature/fullscreen - 今まで作業していたフルスクリーン化対応ブランチ
- fix/shift_modifier_with_ime - IME経由で入力するときShiftなんちゃらがIMEを素通りするのを直すブランチ
という感じになっています。 master はこれらの統合ブランチになってますので使ってる人いたら注意です。
使いながらちょいちょいいじっていて、昨日からの更新としては
- フルスクリーン時に裏にノーマルなウィンドウが残ってしまっていたのを修正
- 日本語入力時に Shift なんちゃらが IME をとおらずそのまま Emacs に渡されてしまっているのを修正
という感じになっております。
CocoaEmacsのフォント設定
現状こんな感じ。以前書いたCarbonEmacsの設定と大して変わってない。unicode コードセットで表示される文字が増えていたのでそれに対応した程度。
(create-fontset-from-fontset-spec
(concat
"-*-fixed-medium-r-normal--12-*-*-*-*-*-fontset-tobi"
",ascii:-apple-codingfonttobi-medium-r-normal--16-120-72-72-m-120-*-*"
",japanese-jisx0208:-apple-osaka-*"
",katakana-jisx0201:-apple-osaka-*"
",unicode:-apple-osaka-*"
",chinese-big5-1:-apple-apple ligothic medium-*"
))
(set-default-font "fontset-tobi")
ascii 用につかってる CodingFontTobi は proggyfonts.com から。
もちろんビットマップ表示にするため
$ defaults write org.gnu.Emacs AppleAntiAliasingThreshold 16
としている。
(setq mac-allow-anti-aliasing nil)
は Snow Leopard では効かないみたいだ。
フルスクリーン Cocoa Emacs
emacs23 が正式にリリースされ、ちまたではウィンドウシステムが Cocoa になったぞ、とか 64bit 対応だぞ、とか multitty だぞ、と盛り上がっていますが、 個人的にほとんど興味のないまま今日まできました。というのも CocoaEmacs はフルスクリーンにできないという僕にとっては致命的な問題があったからです。
というのも最近は Emacs をフルスクリーンにし縦二分割で使ういわゆる imakado スタイルを実践しているため、それができない CocoaEmacs は眼中になかったのでした。
そんなわけでずっと CarbonEmacs を使ってきたのですが、 Snow Leopard に移行したのを機に CocoaEmacs に移行し、さらについでにフルスクリーン化できるようにしてみています。 github でやってます。
現状でも若干不具合がありますが、使える段階にはなっていると思います。
ビルドは
$ git clone git://github.com/typester/emacs.git
$ cd emacs
$ ./configure --with-ns
$ make bootstrap
$ make install
こんな感じにすると、nextstep
ディレクトリ以下に Emacs.app
ができます。フルスクリーンにするには
M-x ns-toggle-fullscreen
でいけます。キー割り当てておくと便利です。
対応環境は定かではありませんが、OS X 10.4 と 10.6 で動作確認しております。
また 10.6 の場合のみ、フルスクリーン時に隠れているメニューバーや Dock 領域にマウスカーソルを移動させると自動的に表示されるようになっています。 CarbonEmacs と違い Dock も自動で表示されるので個人的に非常に便利です。10.6 からの新 API のため、10.5 以前ではフルスクリーン時にメニューバー、Dock は強制オフになります。
既知の不具合として外部ウィンドウでフルスクリーン化して戻そうとすると落ちるというのが報告されており調査中です。 それ以外は便利に使用できています。この文章もフルスクリーン CocoaEmacs で書いています ;)
またこのハックをしたことで elisp から C や Objective-C のコードを呼ぶ方法がわかったのでいろいろ夢がひろがりんぐというところです。
無線APによって自動でhostsを変える方法
OSX の LaunchAgents には特定のパスを監視して更新があったらプログラムを起動すると言うことが出来ます。例えば以下のような設定を書いてみると:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>org.unknownplace.wifi_switch</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/zsh</string>
<string>-c</string>
<string>exec perl /Users/typester/dev/scratch/wifi_switch/switch.pl</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>StandardErrorPath</key>
<string>/dev/null</string><!-- <string>/tmp/error</string> -->
<key>StandardOutPath</key>
<string>/dev/null</string>
<key>WatchPaths</key>
<array>
<string>/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist</string>
</array>
</dict>
</plist>
/Library/Preferences/SystemConfiguration/com.apple.airport.preferences.plist
を監視しそれが更新されたら /Users/typester/dev/scratch/wifi_switch/switch.pl
を実行する。と言うことが実現できます。
この監視先のファイルは OSX の無線ネットワーク設定なので、これが更新されると言うことは無線通信の状態が変わったときと言うことを意味します。
これを有効にするにはこのファイルを適当な名前 hoge.plist
で /System/Library/LaunchDaemons/
保存すれば次回起動から有効になります。すぐに有効にしたい場合は、
sudo launchctl load /System/Library/LaunchDaemons/hoge.plist
などとすればOKです。詳しくは launchctl や LaunchAgent/LaunchDaemon のドキュメントを参照のこと。システムレベルの LaunchDaemon を使用しているのは /etc/hosts
を書き換えるのに root 権限が必要だからです。
あとはここで起動されるスクリプト中でからAPの情報を取得しそれをもとに hosts を書き換えればいいわけです。APの情報は
/System/Library/PrivateFrameworks/Apple80211.framework/Versions/A/Resources/airport -I
と言うコマンドで取得可能なのでそれをパースすればオッケーです。
以下に僕が実際に使用しているスクリプトを貼っておきます。
このスクリプトと同じところに、default.mt
と 特定のAPのSSID名.mt
と言うファイルで hosts を書いておくと、通常は default.mt
が /etc/hosts
に書き出され、SSIDにマッチするファイルがある場合には SSID名.mt
が /etc/hosts
に書き出されるという感じです。
AP によって hosts を変えたいという状況がそんなにあるとは思いませんが、ネットワークの状態によっていろいろ設定を変えると言う目的では便利で使えますので覚えておいて損はないでしょう。