讓我們用 Node.js 與 DBus 打交道
一個多工的作業系統,最不可或缺的機制就是 IPC,應用程式互相溝通的管道。但在比較複雜的桌面應用,系統核心本身的 IPC 就顯得太過簡單,不敷使用。而『DBus』是一個被廣泛使用的 IPC 機制,擁有權限控管、標準的 Interface 甚至是可跨網路溝通等特性,讓應用程式只要遵循標準的資料結構,就可以將資訊傳遞到另一支程式手上。DBus 尤其是在桌面環境下被使用最多,從網路管理員(Network Manager)、藍芽(Bluetooth)連線管理、亮度/音量變更等各式更新訊息通知(Notification)機制,無一不用到他。但由於 DBus 有眾多複雜功能,撰寫程式去使用他其實不是這麼容易,所以若是能用 JavaScript 來做這項工作,肯定能加快許多。
對 JavaScript 而言,打通了 DBus,意謂著可以控制網路管理員,去建立所有類型的網路連線(無論是有線、無線、需要撥號或 VPN),亦可以連接 iBus(當前主流的輸入法框架)以支援並控制輸入法,甚至是可以控制絕大多數的系統服務,說是打通任督二脈可是一點也不為過。
上網尋找,其實已經有一些人嘗試在為 Node.js/V8 Engine 寫 DBus 的支援,只是多半都不夠完整或 API 的使用上過於複雜,這讓筆者萌生了要自己開發一個新的 Dbus 模組的念頭。但重新開發實在是太累人,於是在網路上眾多的現成品中,挑選了一個 API 比較易於使用的專案『node-dbus』,然後為他加上更完整的功能,目前已經小有成果,除了提交回 upstream 外,也已經發佈到 NPM 上。
如果想要使用,你可以直接以 NPM 安裝(註:NPM上註冊的名稱和github上不太一樣):
其使用上相當簡單,建立一個自己的 DBus Service 範例如下:
寫一支 Client 程式,透過 DBus 去使用前一支程式所提供的 WhoAreYou() 函式:
相較於用 C 寫 DBus 程式,這樣應該更簡單吧。當然,還有更簡化的空間,會在後續版本中改進。:-)
後記
最近一直有人問我,花這麼多時間去開發這麼多模組,只為了銜接各種 Library,那為何不直接使用 gir?
我的回答是,JavaScript 有自己的特性和慣用邏輯,如果只是將各個原本用 C/C++ 語言習慣所設計出來的 API 引入到 JavaScript,那和直接寫 C/C++ 語言去使用那些 Library,在開發難度上有何不同?雖然可以讓 JavaScript 得到很多支援,但對實際應用上卻沒有太大幫助。別忘了,使用 JavaScript 就是要享受無痛開發的樂趣。:-D
對 JavaScript 而言,打通了 DBus,意謂著可以控制網路管理員,去建立所有類型的網路連線(無論是有線、無線、需要撥號或 VPN),亦可以連接 iBus(當前主流的輸入法框架)以支援並控制輸入法,甚至是可以控制絕大多數的系統服務,說是打通任督二脈可是一點也不為過。
上網尋找,其實已經有一些人嘗試在為 Node.js/V8 Engine 寫 DBus 的支援,只是多半都不夠完整或 API 的使用上過於複雜,這讓筆者萌生了要自己開發一個新的 Dbus 模組的念頭。但重新開發實在是太累人,於是在網路上眾多的現成品中,挑選了一個 API 比較易於使用的專案『node-dbus』,然後為他加上更完整的功能,目前已經小有成果,除了提交回 upstream 外,也已經發佈到 NPM 上。
如果想要使用,你可以直接以 NPM 安裝(註:NPM上註冊的名稱和github上不太一樣):
$ npm install dbus
其使用上相當簡單,建立一個自己的 DBus Service 範例如下:
var dbus = require("dbus"); dbus.start(function() { var service_path = 'org.freedesktop.DBus.TestSuitePythonService'; var object_path = '/org/freedesktop/DBus/TestSuitePythonObject'; var interface_path = 'org.freedesktop.DBus.TestSuiteInterface'; session = dbus.session_bus(); var dbusRegister = new dbus.DBusRegister(dbus, session); /* Export Methods */ var Methods = { WhoAreYou: function () { return 'I AM Spiderman!'; } }; /* Register a Bus Name */ dbus.requestName(session, service_path); /* Register Methods */ dbusRegister.addMethods(object_path, interface_path, Methods); dbusRegister.addMethods(object_path, 'org.freedesktop.DBus.Introspectable', { Introspect: function() { return '<?xml version=\"1.0\" encoding=\"UTF-8\" ?>' + '<node name=\"' + object_path + '\">' + '<interface name=\"' + interface_path + '\">' + '<method name=\"WhoAreYou\">' + '<arg name=\"data\" type=\"s\" direction=\"out\" />' + '</method>' + '</interface>' + '<interface name=\"org.freedesktop.DBus.Introspectable\">' + '<method name=\"Introspect\">' + '<arg name=\"data\" type=\"s\" direction=\"out\" />' + '</method>' + '</interface>' + '</node>'; } }); /* Loop to waiting for somebody to call */ dbus.runListener(); });
寫一支 Client 程式,透過 DBus 去使用前一支程式所提供的 WhoAreYou() 函式:
var dbus = require("dbus"); dbus.start(function() { session = dbus.session_bus(); interface = dbus.get_interface(session, "org.freedesktop.DBus.TestSuitePythonService", "/org/freedesktop/DBus/TestSuitePythonObject", "org.freedesktop.DBus.TestSuiteInterface"); console.log(interface.WhoAreYou()); });
相較於用 C 寫 DBus 程式,這樣應該更簡單吧。當然,還有更簡化的空間,會在後續版本中改進。:-)
後記
最近一直有人問我,花這麼多時間去開發這麼多模組,只為了銜接各種 Library,那為何不直接使用 gir?
我的回答是,JavaScript 有自己的特性和慣用邏輯,如果只是將各個原本用 C/C++ 語言習慣所設計出來的 API 引入到 JavaScript,那和直接寫 C/C++ 語言去使用那些 Library,在開發難度上有何不同?雖然可以讓 JavaScript 得到很多支援,但對實際應用上卻沒有太大幫助。別忘了,使用 JavaScript 就是要享受無痛開發的樂趣。:-D
作者已經移除這則留言。
回覆刪除不太瞭解您說寫死的意思,如果你要自己設計 Service,仿造範例中的寫法應該就可以了。
刪除作者已經移除這則留言。
回覆刪除Music Player 如果有支援 dbus,並接受其他程式來的指令,他應該會註冊成一個 Service。
刪除通常這 Service Name 是依程式各自喜好而定,所以你要做的是,先找出每個 Music Player 的 Service Name。
這邊可以利用 d-feet 這類工具去看一下 dbus 目前有哪些 Service 正在運行,並得到 Service Name。
然後,你就把這些 Service 寫死在你的程式中,然後去一一偵測是否存在。
BTW, 因為每個 Service 提供的 Method 可能都不一樣,你這部份要針對不同 Music 特別去設計。