天天看點

wayland程序間調用

一、基本工作流程

以Weston自帶的例程simple-shm為例,先感受一下Client如何通過Wayland協定和Compositor通信。

1. 連接配接Server,綁定服務

1)   display->display = wl_display_connect()// 通過socket建立與Server端的連接配接,得到wl_display。它即代表了Server端的display資源,同時也是代理對象wl_proxy。Client可以通過它來向Server端送出調用請求和接收事件。

2)   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通知。

Add_listener 會向registry中注冊回調,例如:

wl_registry_add_listener(display.registry,&registry_listener, &display);

當有新的對象(eg:wl_drm)加入到Waylandserver時便會出發回調,__cb_client_registry_handle_global

3)   wl_display_roundtrip() //Block until allpending request are processed by the server

 等待前面的請求全被Server端處理完,它完成了Client和Server端的同步。也就是說,到這個函數傳回時,Server端有幾個Global對象,回調處理函數registry_handle_global()應該就已經被調用過幾次了。registry_handle_global()中會判斷是目前這次event代表何種Global對象,然後調用wl_registry_bind()進行綁定,得到遠端服務對象的本地代理對象。這些代理對象類型可以是wl_shm,wl_compositor等,但本質上都是wl_proxy類型。

d->compositor =wl_registry_bind(registry, name, &wl_compositor_interface, 1);                                                                             

2. 建立視窗

1)   window->surface = wl_compositor_create_surface() // 通過剛才綁定的wl_compositor服務建立Server端的weston_surface,傳回代理對象wl_surface。

2)   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核心協定。

3)    window->egl_surface=eglCreateWindowSurface(display->egl.dpy,

                                           display->egl.conf,window->native, NULL);

為什麼一個視窗要建立兩個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的進一步封裝,為了做視窗管理。這樣,合成渲染和視窗管理的子產品既可以友善地互相通路又保證了較低的耦合度。

(wl-surface--- window->surface,負責合成

  shell_surface--- window->egl_surface,負責視窗管理)

3. 配置設定buffer與繪制

1)   wl_surface_damage() // 告訴Compositor該surface哪塊區域是需要繪制的。一開始是整個視窗區域。

2)   redraw() // 接下來調用redraw()開始繪制的循環

3)    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為其本地代理對象。

4)    wl_buffer_add_listener(buffer->buffer,&buffer_listener,...) // 監聽這個buffer代理對象,當Server端不再用這個buffer時,會發送release事件。這樣,Client就可以重用這個buffer作下一幀的繪制。

             paint_pixels() //Client在buffer上繪制自己的内容。

5)    wl_surface_attach()// 将繪制好的buffer attach到surface上,即把某一個buffer與surface綁定。

wl_resource = (struct wl_resource*)wl_drm_create_buffer()

wl_surface_attach(wl_egl_window->surface,

                                    (void*)wayland_buffer->proc.app.wl_resource,

                                    wl_egl_window->dx,

                                    wl_egl_window->dy);

6)   wl_surface_damage()// 告訴Server端的Compositor這個surface哪塊區域是髒區域,需要重新繪制。 

7)   window->callback = wl_surface_frame() // 在Server端建立Framecallback,它會被放在該surface下的frame_callback_list清單中。傳回它的代理對象wl_callback。

8)   wl_callback_add_listener(window->callback, &frame_listener, ...) // 監聽前面得到的callback代理對象。在Server端Compositor在完成一幀的合成渲染後,會往這些callback對象發done的事件(參考weston_output_repaint())。Client收到後會調用參數中wl_callback_listener中的done事件對應的方法,這裡是redraw()函數。這樣,就形成了一個循環。

9)    wl_surface_commit() // 在0.99版本後,為了保證原子性及使surface屬性的改動順序無關,Server端對于surface的屬性(damage region, inputregion, 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端來做的。

二、連接配接的建立

首先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,分别作為輸入和輸出的緩沖區。注意這個inbuffer和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() // createsocket

           bind() 

           listen() 

           wl_event_loop_add_fd(.., socket_data,...) //建立wl_event_source_fd,它代表一個基于socketfd的事件源。處理函數是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端建立的監聽端口。得到與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。

三、消息處理模型

在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()上前,會先處理這個idlelist裡的事件。比如有Client更新graphicbuffer後會調用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後進行調用。

四、跨程序過程調用

術語上,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 globalregistry object">

   56     This request creates a registry object thatallows the client

   57     to list and bind the global objects availablefrom the

   58     compositor.

   59       </description>

   60       <arg name="registry"type="new_id" inter/>

   61     </request>

這裡的參數類型是new_id,說明需要建立一個代理對象。其它的如object代表一個對象,fd代表代表檔案描述符等。

wayland-protocol.c中描述了wl_display這個對象的request和event資訊,其中包含了它們的函數簽名。get_registry是request中的一項。

 147 staticconst 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 structwl_display_interface {

...

  143    void (*get_registry)(struct wl_client *client,

  144                  structwl_resource *resource,

  145                 uint32_t registry);

這個聲明是讓Server端定義implementation中的實作函數清單用的,如:

  761 staticconst struct wl_display_interface display_interface = {

  762     display_sync,

  763     display_get_registry

  764 };

wayland-client-protocol.h中:

  184 staticinline 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((structwl_proxy *) wl_display,

  190             WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);

  191 

  192     return (struct wl_registry *) registry;

  193 }   

這是給Client端用來發起request的。當用戶端調用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發出去。當然,在往outbuffer寫時發現滿了也會調用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 staticconst 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()。下圖簡單地描繪了整個流程。 

繼續閱讀