smellman's Broken Diary

クソみたいなもんです

FirefoxをApple Remote Controllerで動かすプログラム

関西オープンソース2006にて、FirefoxApple Remote Controllerで動かすプログラムのデモを行いました。その時のプログラムを紹介します。
プログラム自体はPythonで作成をしています。元ネタはrubycocoaUse your Apple Remote from Rubyですが、rubyわからんのでpythonで書き直しました。で、当日もじら組ブースの真横がRuby関西のブースで...ちょっと焦りつつネタにしましたwww

必要なもの

今回のプログラムを動かすために必要なものは以下のものです。

Apple Remote Controller

Macminiなどに付属しているちっちゃなコントローラです。FrontRowというアプリケーションで動かす事を前提としているため、そのままでは動かせません。そこでmartin氏作成のApple Remote Controller Wrapper Classを使います。
Apple Remote Controller Wrapper ClassはObjective Cで書かれたクラスで、Frameworkとして動かすためには、rubycocoa1.2 Rebuild it as a Frameworkを参考にソースコードからビルドをします。Macユーザなら当然Xcodeはインストールしていますよね(ぉ
Frameworkができあがったら、 /Library/Frameworks/AppleRemote.framework として配置します。

MozRepl

Apple Remote Controllerの制御ができる事はわかりました。しかし、どうやってFirefoxを動かせばいいのでしょうか?その答えはMozLabの中に含まれているMozReplにあります。
詳しくは最速の人のFirefoxの拡張MozLabの中に含まれるMozReplがヤバすぎる件についてを参照していただくとして、ようするにtelnetでfirefoxに入ってJavascriptを投げまくるというだけです。telnetですよ、奥さん!(ナニ
例えば、コントローラの上を押したらページを上に移動させたいとします。そんな時は goDoCommand("cmd_scrollLineUp") を投げるという感じです。どんなものが対応しているかは、lxr で調べましょう(ぉぃ

プログラム紹介

というわけで、前置きが長かったですが、プログラムのソースを紹介します。ダウンロードはこちらから

import telnetlib
import objc
from Foundation import *
objc.loadBundle("AppleRemote", globals(), bundle_path=objc.pathForFramework(u'/Library/Frameworks/AppleRemote.framework'))

class AppleRemoteDelegate(NSObject):
    
    mode = False
    cmdstr = ""
    #up:0, down:1, right:4, left:5, play:3, menu:2
    commands = {
        "0": "goDoCommand(\"cmd_scrollLineUp\")\n",
        "1": "goDoCommand(\"cmd_scrollLineDown\")\n",
        "200": "goDoCommand(\"cmd_beginLine\")\n",
        "20": "goDoCommand(\"cmd_scrollPageUp\")\n",
        "21": "goDoCommand(\"cmd_scrollPageDown\")\n",
        "24": "gBrowser.mTabContainer.advanceSelectedTab(1)\n",
        "25": "gBrowser.mTabContainer.advanceSelectedTab(-1)\n",
        "2001154543": "goQuitApplication()\n"
    }           
    
    @objc.signature('v@:ii')
    def appleRemoteButton_pressedDown_(self, buttonIdentifer, pressedDown):
        print "bt: " + str(buttonIdentifer) + ", press: " + str(pressedDown)
        self.command(buttonIdentifer, pressedDown)

    def command(self, bt, pd):
        if bt == 2:
            if self.mode == False:
                self.mode = True
                self.addcmd(bt)
            else:
                self.mode = False
                self.runcommand()
            return
        if self.mode == False:
            self.addcmd(bt)
            self.runcommand()
        else:
            if bt == 0 and pd == 0:
                self.addcmd(bt)
            if bt == 1 and pd == 0:
                self.addcmd(bt)
            if bt == 3 or bt == 4 or bt == 5:
                self.addcmd(bt)

    def addcmd(self, bt):
        self.cmdstr = self.cmdstr + str(bt)

    def clear(self):
        self.cmdstr = ""

    def runcommand(self):
        if self.commands.has_key(self.cmdstr):
            c = self.commands[self.cmdstr]
            tn = telnetlib.Telnet("localhost", 4242)
            tn.write(c)
            tn.close()
        else:
            print "Not found"
        self.clear()
    

def main():
    global app
    app = NSApplication.sharedApplication()
    ap = AppleRemote.alloc().init()
    de = AppleRemoteDelegate.alloc().init()
    ap.setDelegate_(de)
    ap.startListening_(0)
    app.run()

if __name__ == '__main__':
    main()

Apple Remote Controllerに関係する部分はloadBundleしている部分とか、appleRemoteButton_pressedDown_関数の定義などです。さりげなくexposeを使っているのが憎いですね。まぁ、ソースを読めばだいたい何やってるかわかるでしょう(ぉぃ
main関数ではプログラムを通常のアプリケーションと同じように起動するようにします。じゃないとFrontRowを上書きできませんからねw
ほかはほとんどFirefoxの制御に関係する部分です。工夫をした点はコントローラ自体がボタンが少なすぎるので、MENUボタンをトリガーにしてコマンド入力を可能にした点です。例えば、上にスクロールする場合は MENU + 上 + MENU という感じでボタンを押します。一番上にスクロールする場合は MENU + 上 + 上 + MENU という感じです。
一番のポイントは Firefox を終了するコマンドが MENU + 上 + 上 + 下 + 下 + 左 + 右 + 左 + 右 + Play + Menu という所です。コナミコマンドで自爆は基本ですよね?
ちなみに、今回のプログラムはコナミコマンドで自爆したいが為にすべて作られています。欠点としては、MozReplがじゃまなのか、ちゃんと終了しません。だめじゃんwww

まとめ

Python(PyObjC) + Apple Remote Controller Wrapper Classで夢がひろがりんぐ(てけとー

追記:起動と終了のしかた

起動はまずReplを起動しておいて、ターミナル内で以下のコマンドを打ちます。

python2.4 remote_firefox.py

終了はターミナル上でCtrl+C を打って、その後コントローラのキーを適当に押します。プログラムが例外吐いて死んでくれます(ぉぃ