1. 程式人生 > >ARChon 分析之六:native-client 的載入、顯示與事件傳遞淺析

ARChon 分析之六:native-client 的載入、顯示與事件傳遞淺析

新手教程/基礎概念,請先移步看這篇文章

這篇文章聚焦兩個問題:

  • html/js程式碼如何載入nacl中的程式?
  • nacl與chrome.app.window是如何溝通的?(渲染與滑鼠事件傳遞)

如何載入

PNaCl

GoogleChrome 現在是支援直接用 PNaCl 的,要使用的話怎麼做呢?

增加一個類似這樣的dom節點:

<embed name="nacl_module" id="nacl_module" width="100%" height="100%" 
path src="//storage.googleapis.com/gonacl/demos/publish/236779/voronoi/voronoi.nmf"
type="application/x-pnacl">

可以看到,nacl做作為一種新的型別( application/x-pnacl )被Chrome瀏覽器協議接受.js 可以通過 PPB_Messaging 向dom節點發資訊。

NaCl

NaCl 和 PNaCl 不同的一個點是換了一個type ,新的type是 application/x-pnacl 原生部分的程式碼還是遵循同樣的原則,需要一個 .nmf 的配置檔案。配置在 embed 節點的 src 中。

那麼什麼是 nmf 呢?

參考:Native Client Manifest (nmf) Format

每個Native Client應用程式都有一個JSON格式的 NaCl Manifest File( nmf )。 nmf告訴瀏覽器下載和載入Native Client應用程式檔案和庫的位置。 該檔案還可以包含配置選項。

{
  "program": {
    // Required: at least one entry
    // Add one of these for each architecture supported by the application.
    "x86-32": { "url": "lib32/runnable-ld.so" },
    "x86-64"
: { "url": "lib64/runnable-ld.so" } }, // discussed in next section "files": { "main.nexe": { "x86-32": { "url": "url_to_x86_32_nexe" }, "x86-64": { "url": "url_to_x86_64_nexe" } }, // ... } }

files欄位指定Native Client應用程式要使用的檔案資源字典。files清單欄位對於動態連結的可執行files很重要,這些可執行檔案必須在PPAPI初始化之前載入檔案。 files字典應包括主動態程式及其動態庫。 應該有一個與每個動態庫對應的檔案條目。 每個檔案條目都是受支援體系結構的字典,以及可以找到該體系結構的相應Native Client共享物件(.so)的URL。

由於program用於引用glibc的NaCl埠附帶的動態連結器,因此主程式在files字典中指定。 主程式在files字典的"main.nexe"欄位下指定。

渲染顯示 與 滑鼠事件輸入

參考 example 中的程式碼,看到nacl中有幾個標頭檔案:ppapi/cpp/graphics_2d.hppapi/cpp/graphics_2d.hppapi/lib/gles2 可以看到它已經支援了GLES2.0了。

這部分的資訊,example/AI/Graphics_2d 做了展示。檢視它的程式碼.

它構建了一個 pp:Module 物件,這個物件整合建立了 pp:Instance 的例項。

class Graphics2DModule : public pp::Module {
 public:
  Graphics2DModule() : pp::Module() {}
  virtual ~Graphics2DModule() {}

  virtual pp::Instance* CreateInstance(PP_Instance instance) {
    return new Graphics2DInstance(instance);
  }
};

namespace pp {
Module* CreateModule() { return new Graphics2DModule(); }
}  // namespace pp

查了一下文件,發現pp::Instance 能力很強大,看介面,設計者應該是把它封裝為能夠 處理輸入輸出事件畫版

pp::Instance有幾個重要的鉤子:

BindGraphics //Graphics2D Graphics3D Compositor

此裝置的內容將顯示在網頁上的例項區域中。 裝置必須是2D或3D裝置。

您可以傳遞一個is_null() (預設構造的) Graphics2D作為裝置引數來取消繫結給定例項中的所有裝置。 然後該例項將顯示為透明。 重新繫結相同的裝置將返回true並且不執行任何操作。

任何以前繫結的裝置都將被釋放。 繫結裝置已繫結到另一個例項時繫結裝置是錯誤的。 如果要在例項之間移動裝置,首先將其與舊例項取消繫結,然後將其重新繫結到新例項。

繫結裝置將使網頁的該部分無效,以將新裝置的內容重新整理到螢幕。

DidChangeFocus

當例項獲得或失去焦點時,會呼叫DidChangeFocus() 。

擁有焦點意味著鍵盤事件將被髮送到例項。 例項的預設條件是它不具有焦點。

焦點標誌考慮了瀏覽器選項卡和視窗焦點以及頁面上外掛元素的焦點。 為了被視為具有焦點,瀏覽器視窗必須位於最頂層,必須在視窗中選擇選項卡,並且例項必須是頁面上的焦點元素。

注意:僅當您處理click事件時,例項上的單擊才會獲得焦點。 從HandleInputEvent中的PPP_InputEvent返回true (或使用未過濾的事件)以表示已處理click事件。 否則,瀏覽器將冒泡事件並將焦點放在頁面上實際最終消耗它的元素上。 如果您沒有獲得焦點,請檢查以確保您是通過RequestInputEvents() (which implicitly marks all input events as consumed) or via RequestFilteringInputEvents()請求它們,並從事件處理程式返回true。

HandleInputEvent (const pp::InputEvent &event)

預設實現不執行任何操作並返回false。

要接收輸入事件,必須通過呼叫RequestInputEvents()或RequestFilteringInputEvents()來註冊它們。 預設情況下,不會傳遞任何事件。

如果事件已處理,則不會將其轉發給任何預設處理程式。 如果未處理,則可以將其分派給預設處理程式。 因此,例項必須準確響應事件傳播是否應該繼續。

事件傳播也控制焦點。 如果您處理類似滑鼠事件的事件,通常會為例項提供焦點。 從過濾的事件處理程式返回false或不註冊事件型別意味著點選將被提供給頁面的下半部分,並且您的例項將不會獲得焦點。 這允許例項部分透明,其中透明區域的點選將表現為對基礎頁面的點選。

通常,您應該嘗試保持輸入事件處理的簡短。 特別是對於過濾的輸入事件,可能會阻止瀏覽器或頁面等待您的響應。

此函式的呼叫者將在此呼叫期間保持對輸入事件資源的引用。 除非您參考資源以便以後保留它,否則您不需要釋放它。

注意:如果您沒有收到輸入事件,請確保通過呼叫RequestInputEvents或RequestFilteringInputEvents註冊所需的事件類。 如果您仍然沒有收到鍵盤輸入事件,請確保您為滑鼠事件返回true(或使用非過濾事件處理程式)。 否則,例項將無法獲得焦點,並且不會發送鍵盤事件。

有關詳細資訊,請參閱RequestInputEvents和RequestFilteringInputEvents 。

HandleMessage

HandleMessage()是一個函式,當在JavaScript的例項的DOM元素上呼叫PostMessage()時,瀏覽器會呼叫該函式。

請注意,JavaScript介面中的PostMessage()是非同步的,這意味著當HandleMessage()處理訊息時,不會阻止JavaScript執行。

轉換JavaScript陣列時,將忽略名稱不是陣列索引的任何物件屬性。 傳遞陣列和物件時,將轉換和傳輸整個參考圖。 如果參考圖表有周期,則不會發送訊息,並且會將錯誤記錄到控制檯。

PostMessage

PostMessage()非同步呼叫給定例項的DOM元素上的訊息事件的任何偵聽器。
處理訊息時,對PostMessage()的呼叫不會阻止。

 {.html}

 <body>
   <object id="plugin"
           type="application/x-ppapi-postMessage-example"/>
   <script type="text/javascript">
     var plugin = document.getElementById('plugin');
     plugin.addEventListener("message",
                             function(message) { alert(message.data); },
                             false);
   </script>
</body>

然後該例項呼叫PostMessage() ,如下所示:

PostMessage(pp::Var("Hello world!"));

瀏覽器會彈出一個警告“Hello world!”
傳遞陣列或字典PP_Var ,將轉換並傳輸整個參考圖。 如果參考圖表有周期,則不會發送訊息,並且會將錯誤記錄到控制檯。

JavaScript程式碼中的訊息事件監聽器將接收符合HTML 5 MessageEvent介面的物件。 具體來說,message的值將作為收到的MessageEvent名為data的屬性包含MessageEvent 。
此訊息傳遞系統類似於用於偵聽來自Web Workers的訊息的系統。 有關詳細資訊,請參閱http://www.whatwg.org/specs/web-workers/current-work/ 。