[Wayland] (三) Wayland中的跨進程過程調用淺析 [FW]

原文地址:http://blog.csdn.net/jinzhuojun/article/details/40264449
 
Wayland協議主要提供了Client端應用與Server端Compositor的通訊機制,Weston是Server端Compositor的一個參考實現。Wayland協議中最基礎的是提供了一種面向對象的跨進程過程調用的功能,在做用上相似於Android中的Binder。與Binder不一樣的是,在Wayland中Client和Server底層經過domain socket進行鏈接。和Binder同樣,domain socket支持在進程間傳遞fd,這爲傳遞graphic buffer和shared memory等提供了基礎。Client和Server端一方面各自在消息循環中等待socket上的數據,拿到數據後通過反序列化處理生成本地的函數調用,而後進行調用;還有一方面將本地的遠端調用請求封裝序列化後經過socket發出。另外,由於是基於消息循環的處理模型,意味着這樣的調用不是同步,但經過等待Server端處理完畢後的事件再返回可以達到同步調用的效果。
 
如下從幾個基本方面介紹Wayland協議的跨進程過程調用是怎樣實現的。
 

1、基本工做流程

 
以Weston自帶的例程simple-shm爲例,先感覺一下Client怎樣經過Wayland協議和Compositor通訊。
1. 鏈接Server,綁定服務
display->display = wl_display_connect()// 經過socket創建與Server端的鏈接,獲得wl_display。它即表明了Server端的display資源,同一時候也是代理對象wl_proxy。Client可以經過它來向Server端提交調用請求和接收事件。
display->registry = wl_display_get_registry(display->display) // 申請建立registry,獲得代理對象wl_registry。這個對象至關於Client在Server端放的一個用於嗅探資源的Observer。Client經過它獲得Server端有哪些Global對象的信息。Server端有一系列的Global對象,如wl_compositor, wl_shm等,串在display->global_list鏈表裏。Global對象在概念上相似於Service服務,所以Server端至關於充當了ServiceManager的角色。
wl_registry_add_listener(display->registry, &registry_listener,...) // 讓Client監聽剛纔建立的wl_registry代理對象。這樣,當Client調用wl_display_get_registry()函數或者有新的Global對象增長到Server端時,Client就會收到event通知。
wl_display_roundtrip() // 等待前面的請求全被Server端處理完,它同步了Client和Server端。這意味着到這個函數返回時,Server端有幾個Global對象,回調處理函數registry_handle_global()應該就已經被調用過幾回了。registry_handle_global()中會推斷是當前此次event表明何種Global對象,而後調用wl_registry_bind()進行綁定,獲得遠程服務對象的本地代理對象。這些代理對象類型可以是wl_shm, wl_compositor等,但本質上都是wl_proxy類型。這步的做用相似於Android中的bindService(),它會獲得一個遠端Service的本地代理。
 
2. 建立窗體
window->surface = wl_compositor_create_surface() // 經過剛纔綁定的wl_compositor服務建立Server端的weston_surface,返回代理對象wl_surface。
xdg_shell_get_xdg_surface(..., window->surface, ...) // 經過剛纔綁定的xdg_shell服務建立Server端的shell_surface,返回代理對象xdg_surface。有些樣例中用的是wl_shell_surface,它和xdg_surface的做用是同樣的。xdg_surface是做爲wl_shell_surface未來的替代品,但還沒進Wayland核心協議。
爲何一個窗體要建立兩個surface呢?因爲Wayland協議若是Server端對Surface的管理分兩個層次。以Weston爲例,Compositor僅僅負責合成(代碼主要在compositor.c),它至關於Android中的SurfaceFligner,它所示主要是weston_surface。而Weston在啓動時會載入shell模塊(如desktop-shell.so,代碼主要在desktop-shell/shell.c),它至關於Android中的WindowManagerService,它所示主要是shell_surface。shell_surface在結構上是weston_surface的進一步封裝,爲了作窗體管理。這樣,合成渲染和窗體管理的模塊既可以方便地相互訪問又保證了較低的耦合度。
 
3. 分配buffer與繪製
wl_surface_damage() // 告訴Compositor該surface哪塊區域是髒的,需要重繪。一開始是整個窗體區域。
redraw() // 接下來調用redraw()開始繪製的循環,這裏是雙buffer的軟件渲染。
     window_next_buffer() // 取一個buffer,用做繪製。
         create_shm_buffer() // 假設該buffer還沒有分配則用以前綁定的wl_shm服務分配一塊共享內存。
             fd = os_create_anonymous_file() // 爲了建立共享內存,先建立一個暫時文件做爲內存映射的backing file。
             mmap(..., fd,...) // 將該文件先映射到Client端的內存空間。
             pool = wl_shm_create_pool(..., fd,...) // 經過wl_shm服務建立共享內存池。將剛纔的fd做爲參數傳過去,這樣Server端就可以和Client經過這個fd映射到同一塊物理內存。
             buffer->buffer = wl_shm_pool_create_buffer(pool, ...) // 經過這個共享內存池在Server端分配buffer,返回wl_buffer爲其本地代理對象。
             wl_buffer_add_listener(buffer->buffer, &buffer_listener,...) // 監聽這個buffer代理對象,當Server端再也不用這個buffer時,會發送release事件。這樣,Client就可以重用這個buffer做下一幀的繪製。
     paint_pixels() // Client在buffer上繪製本身的內容。
     wl_surface_attach()// 將繪製好的buffer attach到surface上。做用上相似於Android中的updateTexImage(),即把某一個buffer與surface綁定。
     wl_surface_damage()// 告訴Server端的Compositor這個surface哪塊區域是髒區域,需要又一次繪製。 
     window->callback = wl_surface_frame() // 在Server端建立Frame callback,它會被放在該surface下的frame_callback_list列表中。返回它的代理對象wl_callback。
     wl_callback_add_listener(window->callback, &frame_listener, ...) // 監聽前面獲得的callback代理對象。在Server端Compositor在完畢一幀的合成渲染後,會往這些callback對象發done的事件(參考weston_output_repaint())。Client收到後會調用參數中wl_callback_listener中的done事件相應的方法,這裏是redraw()函數。這樣,就造成了一個循環。
     wl_surface_commit() // 在0.99版本號後,爲了保證原子性及使surface屬性的修改順序無關,Server端對於surface的屬性(damage region, input region, opaque region, etc.)都是雙buffer的(weston_surface_state)。因此commit前的修改都存在backing buffer中。僅僅有當Client調用wl_surface_commit()時,這些修改才生效。
 
與Android做個類比,這裏的wl_surface相應SF中的Layer,wl_buffer相應GraphicBuffer。Weston相應SF+WMS。一個surface相應用戶視角看到的一個窗體。爲了使Client和Server並行做業,一般會用多個buffer。和Android比較不一樣的是,Android是Server端來維護buffer的生命週期,而Wayland中是Client端來作的。
 

2、鏈接的創建

 
首先Server端的Compositor在啓動時會在$XDG_RUNTIME_DIR文件夾下建立用於監聽的socket,默以爲wayland-0。而後把該socket fd增長event loop等待的fd列表。參考實現位於weston_create_listening_socket() -> wl_display_add_socket_auto() -> _wl_display_add_socket()。當有Client鏈接時,回調處理函數爲socket_data(),當中會調用wl_os_accept_cloexec()獲得與該Client相連的socket fd。而後調用wl_client_create(),建立wl_client。Server端會爲每一個鏈接的Client建立wl_client,這些對象被串在display->client_list這個列表裏。wl_client中的wl_connection表明這個與Client的鏈接,當中包括了in buffer和out buffer,分別做爲輸入和輸出的緩衝區。注意這個in buffer和out buffer都是雙份的,一份for普通數據,一份for fd,因爲fd需要以out-of-band形式傳輸,要特殊處理。wl_event_loop_add_fd()會把這個與Client鏈接的socket fd增長到event loop等待的列表中,回調處理函數爲wl_client_connection_data()。
weston_create_listening_socket()
     wl_display_add_socket_auto()
         wl_socket_init_for_display_name() // $XDG_RUNTIME_DIR/wayland-0,
         _wl_display_add_socket() 
             wl_os_socket_cloexec() // create socket
             bind() 
             listen() 
             wl_event_loop_add_fd(.., socket_data,...) // 建立wl_event_source_fd,它表明一個基於socket fd的事件源。處理函數是wl_event_source_fd_dispatch(),當中會調用這裏參數裏的回調函數socket_data()。
                 add_source() // 把剛建立的監聽socket fd,經過epoll_ctl()附加到loop->epoll_fd上。這樣消息循環就可以在上面等待Client的鏈接了。
 
當有Client鏈接到Server的監聽socket上,會調用剛纔註冊的回調函數socket_data(),而後會調用wl_os_accept_cloexec()->accept()建立與Client鏈接的fd。接着調用wl_client_create()建立Client對象。
socket_data()
     wl_os_accept_cloexec()
     wl_client_create()
         client->connection = wl_connection_create() 
         wl_map_init(&client->objects) // 初始化wl_map對象,用於映射Server和Client端的對象。
         bind_display() // 綁定Client端的wl_display代理對象與Server端的display資源對象。
             client->display_resource = wl_resource_create(.., &wl_display_interface,...) // display資源對象的接口就是wl_display_interface,request的實現在display_interface中。這裏建立wl_resource結構,當中resource->object是一個可供Client調用的遠端對象的抽象,當中的成員interface和implementation分別表明其接口和實現。而後用wl_map_insert_at()插入到client->objects的wl_map結構中供之後查詢。
             wl_resource_set_implementation(..., &display_interface, ...)  // 對Client而言,Server端提供的接口實現是request部分。
 
Client端要鏈接Server端,是經過調用wl_display_connect()。當中會建立socket並且調用connect()鏈接Server端建立的監聽port。獲得與Server端鏈接的socket fd後調用wl_display_connect_to_fd()建立wl_display。wl_display是Server端的display資源在Client端的代理對象,它的第一個元素wl_proxy,所以它可以與wl_proxy互轉。和Server端同樣,也會建立一個wl_connection包括讀寫緩衝區。
wl_display_connect()
     fd =connect_to_socket() // 嘗試鏈接$XDG_RUNTIME_DIR/wayland-0,返回與Server端相連的socket fd。
         wl_os_socket_cloexec() 
         connect()
     wl_display_connect_to_fd()// 建立和返回wl_display。
         display->proxy.object_interface = &wl_display_interface; // 設置wl_display的接口。
         display->proxy.object.implementation = (void(**)(void)) &display_listener // 對Server而言,Client端提供的接口實現是event部分。
         display->connection = wl_connection_create() 
可以看到display在Client端的wl_proxy和Server端wl_resource都包括了它完整的接口描寫敘述wl_display_interface。但wl_proxy僅僅包括了event的實現display_listener,wl_resource僅僅包括了request的實現display_interface。
 

3、消息處理模型

 
在Server端,Compositor初始化完後會調用wl_display_run()進入大循環。這個函數主體是:
while (...) {
    wl_display_flush_clients() // 將相應的out buffer經過socket發出去。
    wl_event_loop_dispatch() // 處理消息循環。
}
wl_event_loop表明主消息循環,wl_event_loop_dispatch()的大多數時間會經過epoll_wait()等待在wl_event_loop的epoll_fd上。epoll是相似於select, poll的機制,可以讓調用者等待在一坨fd上,直到當中有fd可讀、可寫或錯誤。這個event loop和當中的epoll_fd是Compositor在wl_display_create() ->  wl_event_loop_create()時建立的。
 
wl_event_source表明wl_event_loop中等待的事件源。它有多種類型,比方wl_event_source_fd, wl_event_source_timer和wl_event_source_signal。它們分別表明由socket fd, timerfd和signalfd觸發的事件源。wl_event_source_idle比較特殊,當消息循環處理完那些epoll_fd上等到的事件後,在下一次堵塞在epoll_wait()上前,會先處理這個idle list裏的事件。比方有Client更新graphic buffer後會調用weston_output_schedule_repaint() -> wl_event_loop_add_idle(),就是往這個idle list里加一個消息。wl_event_source_fd的建立爲例,在Client鏈接到Server時,Server會調用wl_client_create() -> wl_event_loop_add_fd()->add_source()將之增長到display的loop上,其處理回調函數爲wl_client_connection_data(),意味着當主消息循環在這個Client相應的socket上讀到數據時,就會調用wl_client_connection_data()進行處理。
 
在Client端,當需要與Server端交換數據時,終於會調用wl_display_dispatch_queue()。當中最基本的是三件事:
1. wl_connection_flush()將當前out buffer中的數據經過socket發往Server端。這些數據是以前在wl_connection_write()中寫入的。
2. 經過poll()在socket上等待數據,並經過read_events()將這些數據處理生成函數閉包結構wl_closure,而後放到display的wl_event_queue.event_list事件列表中。wl_closure可以看做是一個函數調用實例,裏面包括了一個函數調用需要的所有信息。
3. dispatch_queue()->dispatch_event()用於處理前面加入到隊列的事件。這裏就是把隊列中的wl_closure拿出來生成trampoline後進行調用。
 

4、跨進程過程調用

 
術語上,Wayland中把Client發給Server的跨進程函數調用稱爲request,反方向的跨進程函數調用稱爲event。本質上,它們處理的方式是相似的。要讓兩個進程經過socket進行函數調用,首先需要將調用抽象成數據流的形式。函數的接口部分是同一時候連接到Client和Server端的庫中的,當中包括了對象所支持的request和event的函數簽名。所以這部分不用傳輸,僅僅要傳輸目標對象id,方法id和參數列表這些信息就可以了。這些信息會經過wl_closure_marshal()寫入wl_closure結構,再由serialize_closure()變成數據流。等到了目標進程後,會從數據流經過wl_connection_demarshal()轉回wl_closure。這個過程相似於Android中的Parcel機制。那麼問題來了,參數中的整形,字符串什麼的都好搞,拷貝便可。但假設參數中包括對象,咱們不能把整個對象拷貝過去,也不能傳引用過去。那麼需要一種機制來做同一對象在Server和Client端的映射,這是經過wl_map實現的。wl_map在Client和Server端各有一個,它們分別存了wl_proxy和wl_resource的數組,且是一一相應的。這些對象在這個數組中的索引做爲它們的id。這樣,參數中的對象僅僅要傳id,這個id被傳到目的地後會經過查找這個wl_map表來獲得本地相應的對象。在功能上相似於Android中的BpXXX和BnXXX。wl_proxy和wl_resource都包括wl_object對象。這個wl_object和麪向對象語言裏的對象概念相似,它有interface成員描寫敘述了這個對象所實現的接口,implementation是這些接口的實現函數的函數指針數組,id就是在wl_map結構裏數組中的索引。前面所說的Client綁定Server端資源的過程就是在Client端建立wl_proxy,在Server端建立wl_resource。而後Client就可以經過wl_proxy調用Server端相應wl_resource的request,Server端就可以經過wl_resource調用Client端相應wl_proxy的event。這個映射步驟例如如下圖所看到的(以wl_registry爲例):
與Android不一樣的是,Android中請求到達Server端,調用時需要在目標對象中有一段Stub來生成調用的上下文。而Wayland中,這是由libffi完畢的。
 
Wayland核心協議是經過protocol/wayland.xml這個文件定義的。它經過wayland_scanner這個程序掃描後會生成wayland-protocol.c, wayland-client-protocol.h和wayland-server-protocol.h三個文件。wayland-client-protocol.h是給Client用的;wayland-server-protocol.h是給Server用的; wayland-protocol.c描寫敘述了接口,Client和Server都會用。這裏以wl_display的get_registry()這個request爲例,分析下跨進程的過程調用是怎樣實現的。
 
首先在wayland.xml中申明wl_display有get_registry這個request:
 
   54     <request name="get_registry">
   55       <description summary="get global registry object">
   56     This request creates a registry object that allows the client
   57     to list and bind the global objects available from the
   58     compositor.
   59       </description>
   60       <arg name="registry" type="new_id" interface="wl_registry"/>
   61     </request>
 
這裏的參數類型是new_id,說明需要新建一個代理對象。其餘的如object表明一個對象,fd表明表明文件描寫敘述符等。
 
wayland-protocol.c中描寫敘述了wl_display這個對象的request和event信息,當中包括了它們的函數簽名。get_registry是request中的一項。
 
 147 static const struct wl_message wl_display_requests[] = {
 148     { "sync", "n", types + 8 },
 149     { "get_registry", "n", types + 9 },
 150 };
 151 
 152 static const struct wl_message wl_display_events[] = {
 153     { "error", "ous", types + 0 },
 154     { "delete_id", "u", types + 0 },
 155 };
 156 
 157 WL_EXPORT const struct wl_interface wl_display_interface = {
 158     "wl_display", 1,
 159     2, wl_display_requests,
 160     2, wl_display_events,
 161 };
 
wayland-server-protocol.h中:
  115 struct wl_display_interface {
...
 
  143     void (*get_registry)(struct wl_client *client,
  144                  struct wl_resource *resource,
  145                  uint32_t registry);
 
這個聲明是讓Server端定義implementation中的實現函數列表用的,如:
  761 static const struct wl_display_interface display_interface = {
  762     display_sync,
  763     display_get_registry
  764 };
 
wayland-client-protocol.h中:
 
  184 static inline struct wl_registry *
  185 wl_display_get_registry(struct wl_display *wl_display)
  186 {
  187     struct wl_proxy *registry; 
  188 
  189     registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
  190              WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);
  191 
  192     return (struct wl_registry *) registry;
  193 }   
 
這是給Client端用來發起request的。當client調用wl_display_get_registry(),由於要返回代理對象,因此調用wl_proxy_mashal_constructor()。返回的wl_registry是一個代理對象。
wl_display_get_registry()
     wl_proxy_marshal_constructor()
         wl_argument_from_va_list() // 將上面傳來的參數按wl_display_interface->methods[WL_DISPLAY_GET_REGISTRY]中籤名描寫敘述的類型放到wl_argument數組中。
         wl_proxy_marshal_array_constructor() 
             new_proxy = create_outgoing_proxy()  // 因爲get_registry()的request參數中有new_id類型,因此要建立一個代理對象。
                 proxy_create() //建立wl_proxy。設置interface等信息,而後將該wl_proxy插入到display->objects的wl_map中,返回值爲id,事實上就是在wl_map中數組中的索引值。這個值是會被髮到Server端的,這樣Server端就可以在Server端的wl_map數組的一樣索引值的位置插入對應的wl_resource。這樣邏輯上,就建立了wl_proxy和wl_resource的映射關係。之後,Client和Server間要相互引用對象僅僅要傳這個id就可以了。
             closure = wl_closure_marshal() //建立wl_closure並依據前面的參數列表初始化。先將前面生成的wl_argument數組複製到wl_closure的args成員中。而後依據類型作調整,如將wl_proxy的對象指針改成id,因爲傳個本地指針到還有一個進程是沒意義的。
             wl_closure_send() // 發送前面生成的wl_closure。
                 copy_fds_to_connection() // 將參數中的fd放到專門的fd out buffer中。因爲它們在發送時是要特殊處理的。
                 serialize_closure() //將wl_closure轉化爲byte stream。像類型爲object的參數會轉化爲id。
                 wl_connection_write() // 放到connection的out buffer,準備經過socket發送。
到這裏本地的wl_registry代理對象建立完畢,並且準備向Server發出request。當下次運行到wl_display_dispatch_queue()時,會調用wl_connection_flush()把connection中out buffer的request經過socket發出去。固然,在往out buffer寫時發現滿了也會調用wl_connection_flush()往socket發數據。
 
到了Server端,前面提到會調用處理函數wl_client_connection_data()進行處理:
wl_client_connection_data()
     wl_connection_flush() //向Client發送數據。
     wl_connection_read() //從Client接收處理。
     while (...) // 循環處理從socket中讀到的數據。
         wl_connection_copy() // 每次從緩衝中讀入64字節。它至關於一個request的header,後面會跟參數數據。當中前4個字節表明是向哪一個對象發出request的。後面4個字節包括opcode(表明是這個object的哪一個request),及後面參數的長度。
         wl_map_lookup() // 在wl_map中查找目標資源對象wl_resource。其成員object中有該對象的接口和實現列表。結合前面的opcode就可以獲得對應的request的描寫敘述,用wl_message表示。如 { "get_registry", "n", types + 9 }。
         wl_connection_demarshal()   // 依據interface中的函數簽名信息生成函數閉包wl_closure。主要是經過wl_message中對參數的描寫敘述從緩衝區中把參數讀到wl_closure的args成員中。wl_closure的args成員是wl_argument的數組。因爲這裏沒法預先知道參數類型,因此wl_argument是個union。
         wl_closure_lookup_objects() // wl_closure中的參數中假設有object的話,現在還僅僅有id號。這步會經過wl_map把id號轉爲wl_object。
         wl_closure_invoke() //使用libffi庫生成trampoline code,跳過去運行。
 
在這個場景下,由於以前在bind_display()中把client->display_resource的implementation設爲:
 
  761 static const struct wl_display_interface display_interface = {
  762     display_sync,
  763     display_get_registry
  764 };
 
因此接下來會調用到display_get_registry()。這個函數裏會建立wl_registry相應的wl_resource對象。建立好後會放到display->registry_resource_list中。前面提到過,這個registry資源邏輯上的做用是Client放在Server端的Observer,它用於監聽Server端有哪些Global對象(Service服務)。display_get_registry()函數接下去會對於每一個Global對象向該Client新建的registry發送事件。另外在有Global對象建立和銷燬時(wl_global_create()和wl_global_destroy()),Server會向所有的registry發送事件進行通知。所以,Global對象可以理解爲可動態載入的Service。
 
那麼,這些Global對象詳細都是些什麼呢?爲了故事的完整性,這裏就插播段題外話。Server端的Compositor在啓動時一般會註冊一些Global對象,邏輯上事實上就是一些服務。經過Wayland提供的wl_global_create()加入:
wl_global_create()
      global->name = display->id++; // Global對象的id號。
      global->interface = interface;
      wl_list_insert(display->global_list.prev, &global->link); // display->global_list保存了Global對象的列表。
 
      wl_list_for_each(resource, &display->registry_resource_list, link) // 向以前註冊過的registry對象發送這個新建立Global對象的event。
          wl_resource_post_event(resource,  WL_REGISTRY_GLOBAL, global->name, global->interface->name, global->version);
以wl_compositor這個Global對象爲例, Server端調用wl_global_create(display, &wl_compositor_interface, 3, ec, compositor_bind)。而後當Client端調用wl_display_get_registry()時,Server端的display_get_registry()會對每個Global對象向Client發送global事件,所以Server端有幾個Global對象就會發幾個event。Client收到event後調用先前註冊的回調registry_handle_global()。依據interface name推斷當前發來的是哪個,而後調用wl_reigistry_bind(..., &wl_compositor_interface,..)綁定資源,同一時候建立本地代理對象。接着Server端對應地調用registry_bind(),當中會調用先前在wl_global_create()中註冊的回調函數,即compositor_bind()。接着通過wl_resource_create(), wl_resource_set_implementation()等建立wl_resource對象。也就是說,對於同一個Global對象,每有Client綁定一次,就會建立一個wl_resource對象。換句話說,對於Server來講,每一個Client有一個命名空間,同一個Global對象在每一個Client命名空間的wl_resource是不同的。這樣,對於一個Global對象(Service服務),在Client端建立了wl_proxy,在Server端建立了wl_resource,它們就這樣綁定起來了。wl_proxy.object包括了event的處理函數,這是對Server端暴露的接口,而wl_resource.object包括了request的處理函數,這是對Client暴露的接口。
 
回到故事主線上,前面是從Client端調用Server端對象的request的流程,從Server端向Client端對象發送event並調用其回調函數的過程也是相似的。如下以display_get_registry()中向Client端發送global事件爲例分析下流程。Server端經過wl_resource_post_event()來向Client發送event。
wl_resource_post_event()
     wl_resource_post_event_array()
         wl_closure_marshal() // 封裝成wl_closure,當中會轉化object等對象。
         wl_closure_send()
             copy_fds_to_connection()
             serialize_closure() // 將closure序列化成數據流,因爲將要放到socket上傳輸。
             wl_connection_write()
這樣event就被放到connection的out buffer中,等待從socket上發送。那麼,Client是怎麼讀取和處理這些event呢?首先Client端需要監聽這個wl_proxy,這是經過調用wl_registry_add_listener()->wl_proxy_add_listener()設置的。該函數的參數中包括了這個event的處理函數列表registry_listener,它相應的接口在前面調用wl_display_get_registry()時已設置成wl_registry_interface。wl_registry_interface是在前面依據wayland.xml本身主動生成的一部分。這裏體現了event與request的一點細微區別,request是Server端都要處理的,而event在Client可以選擇不監聽。
  
而後在Client的主循環中會調用wl_display_dispatch_queue()來處理收到的event和發出out buffer中的request:
wl_display_dispatch_queue()
     dispatch_queue()
     wl_connection_flush()
     read_events() // 從connection的in buffer中讀出數據,轉爲wl_closure,插入到queue->event_list,等待興許處理。
         wl_connection_read()
         queue_event() //這塊處理有點像Server端的wl_client_connection_data(),差異在於這裏用的是wl_reigstry_interface的events列表而不是methods列表。
             wl_connection_copy()
             wl_map_lookup() // 查找目標代理對象wl_proxy。
             wl_connection_demarshal() // 從connection的緩衝區中讀入數據,結合函數簽名生成wl_closure。
             create_proxies() 
             wl_closure_lookup_objects() 
     dispatch_queue() // 將前面插入到queue其中的event(wl_closure)依次拿出來處理。
         dispatch_event(queue) // display->display_queue->event_list的每一個元素是一個wl_closure,表明一個函數調用實例,最後經過wl_closure_invoke()進行調用。
            wl_closure_invoke()
 
這樣該event的對應處理回調函數就被調用了,在這個場景中,即registry_handle_global()。下圖簡單地描繪了整個流程。
相關文章
相關標籤/搜索