JavaScript 與 Desktop - DBus
我先前在 ICOS 2010 記事 提及目前有多項軟體專案,正試圖將 Web 軟體開發經驗延伸到 Linux 桌面軟體開發領域。 在那之中,以 gjs 和 seed 這兩項專案的成果最接近實用階段。這兩套都是基於 C 與 GNOME Library 的 JavaScript 解譯器實作品。透過 GNOME Library 的 GObject introspection framework ,它們可以呼叫系統中所安裝的其他函數庫。故而它們可以用於開發一般的 Linux 桌面軟體。
我這兩天試用 Ubuntu 10.10 與 gnome-shell 時,同時嘗試著用 gjs 和 seed 撰寫一些小程式。首先嘗試的項目是透過 D-Bus 調用其他桌面軟體的服務。
請安裝 gjs 或 seed 其中一套,以便執行本文的範例。Ubuntu 的使用者,在 Ubuntu 10.04/10.10 中,皆有提供 gjs 與 seed 的套件;Ubuntu 10.04 的 libgjs0 有版本衝突的bug,請勾選非正式版本套件庫(lucid-proposed repository)安裝非正式版本。其他散佈版本的使用者,可能要自行編譯。
gjs/seed 調用 D-Bus 之基本步驟
如果你曾經在其他程式語言中,使用過 D-Bus 功能,那麼你看到 JavaScript 目前提供的 D-Bus 調用能力時,應該會覺得它相當低階,仍有許多細節要由程序員自己處理。這使得 gjs/seed 目前在使用 D-Bus 時,顯得不是那麼簡便。
Message taking loop
在 gjs/seed 中調用 D-Bus 服務時,你要自己建立一個提供 D-Bus 使用的訊息處理迴圈。
seed 提供的範例中,進入訊息處理迴圈的程式碼如下:
至於 gjs ,則本身提供了一個 mainloop 模組可用。如下列:
本文範例在 Ubuntu 10.04 與 10.10 上,分別以 gjs 和 seed 測試過。在撰寫過程中,我發現 Ubuntu 10.10 的 seed 相依的環境有問題。seed 的範例程式碼最後皆使用 GLib 的 main_loop_new() 建立訊息處理迴圈。但是它所使用的 glib binding ,並沒有定義 main_loop_new() 和 main_loop_run() 函數,所以在調用遠端服務的 D-Bus 方法後,無法取得回傳訊息。我目前還無法確定這個問題是否僅存在於 Ubuntu 10.10 提供的 seed 套件身上。Ubuntu 10.04 倒是很正常。
Proxy object
在 gjs/seed 中,首先你必須為你嘗試調用的遠端個體,建立一個本地端的代理(proxy object)。所以我們要先定義一個代理個體的類別。至於 JavaScript 定義類別的方式,請自行學習。例如本部落格的其他相關文章,此處不複述。
下列為 gjs/seed 使用的 D-Bus 代理個體類別的固定範本:
Interface spec
定義了代理個體類別後,你還要描述你的調用對象的介面規格(在 Python 和 Ruby 中,可以內觀得之(introspect),不必程序員描述)。
gjs/seed 使用下列的表格形式描述介面規格:
當你填完上面那張表後,請將它交給 gjs/seed 實作的 DBus.proxifyPrototype() 方法。DBus.proxifyPrototype() 會把那張表描述的介面內容,注入我們指定的代理類別。
至於如何得知你的服務對象的 D-Bus 規格呢?途徑有二。一、查看它的文件有沒有描述。二、透過 DBus Introspectable 介面的 Introspect 方法查詢。我個人偏好第二種方式,因為它的查詢結果會很明確地告訴我型態簽名(D-Bus 規格的型態簽名之意義,請自行查詢),而不需我自己翻譯。下列示範如何利用 dbus-send 工具內觀 Notifications 的介面規格。
$ dbus-send --session --print-reply --dest=org.freedesktop.Notifications \
/org/freedesktop/Notifications \
org.freedesktop.DBus.Introspectable.Introspect
使用 callback 模式接受回傳值
不論你調用遠端個體的方法,或傾聽它的訊號,在 gjs/seed 中,都是使用 callback 模式接受回傳值。
調用遠端方法時,將你的 callback 函數放在最後一個參數;若你不需要回傳值,就不必提供 callback 函數。callback 函數將接收一個參數。如果遠端方法的回傳結果只有一個,那麼 callback 收到的參數就是一個變數;若有多個回傳結果,則你會得到一張回傳值表。請參考 dbus-notify.js 。
傾聽遠端訊號時,你的 callback 函數會接收至少一個參數。第一個參數是發訊個體,其他的參數值依序排列。請參考 dbus-notify.js 。
Notifications Notify
第一個撰寫的程式,將透過桌面管理程式提供的 Notifications 介面(See also: Desktop Notifications Specification ),在桌面上彈出提示訊息。這是衡量你的程式是否與桌面環境親密結合的一項功能指標。
下列為 dbus-notify.js 的程式碼。
執行結果如下列所示,同時你會看到桌面上彈出一個訊息視窗:
$ dbus-send --session --print-reply --dest=org.freedesktop.Notifications \
/org/freedesktop/Notifications \
org.freedesktop.DBus.Introspectable.Introspect
$ gjs dbus-notify.js
object
0: notify-osd
1: Canonical Ltd
2: 1.0
3: 1.1
result: 17
Closed. ID: 17, reason: 1
NetworkManager and Introspectable
第二個範例將調用 NetworkManager 的 org.freedesktop.NetworkManager.GetDevices 方法以及 org.freedesktop.DBus.Introspectable.Introspect 。
GetDevices 方法可得知目前有幾個網路設備可用。
大部份的 D-Bus 服務都會實作 org.freedesktop.DBus.Introspectable 介面,提供 Introspect 方法。讓其他人可以藉由這個方法查看介面規格。我們查詢的結果會是一份 XML 文件,若我們進一步分析該文件,就可以直接將分析結果交給 DBus.proxifyPrototype() 注入指定的代理類別,而不必自己描述。在此範例中,只是展示我們可以在執行時內觀介面規格,並未進一步實現分析與注入動作。
執行結果如下列所示:
$ seed nm-get-devices.js
.
.
.
Devices:
/org/freedesktop/NetworkManager/Devices/0
結語
儘管 gjs/seed 目前在使用 D-Bus 時,仍然不太方便。但整體功能上倒沒有發生什麼錯誤。我們距離用 JavaScript 撰寫桌面應用軟體的目標,又更進一步了。
相關文章
- JavaScript 與 Desktop - WebKit
- JavaScript 與 Desktop - Desktop and WebKit
- GJS - D-Bbus 自動內觀(Introspect)與配置代理個體
樂多舊回應