wayland学习笔记(四) 全局服务对象初探
wl_display是wayland協議的核心類, 一個wl_display 對象代表一個客戶端, 這個對象里面包含了client和server之間通信的socket, 所有和服務器之間的交互都是通過這個socket. wl_display也是客戶端必須第一個創建的wayland對象。
wl_display中比較特殊的地方在于,它的事件處理函數是在wayland源碼中實現的,可以認為這個是個基礎設施,不宜為用戶所改動。
客戶端調用的第一個wayland函數就是wl_display_connect(), 然后獲得一個wl_display對象
get_registry 請求基本上是客戶端調用的第二個wayland函數
static inline struct wl_registry * wl_display_get_registry(struct wl_display *wl_display)
{
struct wl_proxy *registry;
registry = wl_proxy_marshal_constructor((struct wl_proxy *) wl_display,
WL_DISPLAY_GET_REGISTRY, &wl_registry_interface, NULL);
return (struct wl_registry *) registry;
}
客戶端通過registry可以得知哪些服務可用,然后通過bind請求得到對應的服務對象,然后就可以使用對應的服務提供的接口了。
兩個事件函數(回調函數)是通過wl_registry_add_listener中的傳入參數struct wl_registry_listner 注冊進去的. 這兩個事件函數分別是
global 和 global_remove. global是啥意思捏,服務端有多少個服務,這個global就會被調用多少次。 global_remove則與之相反,當服務端某個服務停止了,就會調一次這個回調。
在調完wl_registry_add_listener之后,一般通過wl_display_dispatch 和 wl_display_roundtrip來確保異步操作已經完成。這里我們看一下client是如何bind到具體的服務對象的
struct wl_compositor* compositor = NULL;
static void global_registry_handler(void*data, struct wl_registry* registry, uint32_t id,
const char* interface, uint32_t version)
{
printf("Availalbe interface: %s, id: %d, version: %d
", interface, id , version);
if( strcmp("wl_compositor", interface) == 0) {
compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 1);
} else if (strcmp(interface, "wl_shell")) {
shell = wl_registry_bind(registry, id, &wl_shell_interface, 1);
}
}
wl_compositor 是wayland中創建窗口的靈魂
static inline struct wl_surface * wl_compositor_create_surface(struct wl_compositor *wl_compositor)
{
struct wl_proxy *id;
id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
WL_COMPOSITOR_CREATE_SURFACE, &wl_surface_interface, NULL);
return (struct wl_surface *) id;
}
//create_surface,這個請求函數用來創建窗口繪制的實體,但是它不是真正的窗口,只是窗口上面的內容,意思就是,可能存在很多的窗口,這些窗口上面的內容都是這個surface。后面我會專門拿一章來詳細說明這一點。這個接口返回wl_surface對象。
static inline struct wl_region * wl_compositor_create_region(struct wl_compositor *wl_compositor)
{
struct wl_proxy *id;
id = wl_proxy_marshal_constructor((struct wl_proxy *) wl_compositor,
WL_COMPOSITOR_CREATE_REGION, &wl_region_interface, NULL);
return (struct wl_region *) id;
}
這里看到wl_surface和wl_region都是client發起的request得到的,這里不禁讓我思考,記得之前說client的buffer都是自己這邊分配管理的?這里打上一個問號繼續往下看:
wl_shm這個服務,是wayland提供的客戶端和服務器端共享內存的服務,共享內存一般是作為窗口繪制內容的容器。
wl_shm_create_pool 原理是客戶端傳給服務器端內存的文件fd, 服務器再通過這個fd操作那塊內存
這個接口返回一個wl_shm_pool, 這個就是共享內存對象,結合上面的說法就是客戶端創建一個fd,然后mmap到一段內存,這段內存就是客戶端用來渲染的,最后把這個fd傳遞給服務器,讓服務器端去讀取它。但是對于窗口的繪制卻不是直接操作的這個wl_shm_pool 而是通過wl_shm_pool_create_buffer返回的wl_buffer作為保存繪制內容的真正實體。wl_shm_pool 和wl_buffer一般是1:1的關系
wl_buffer有一個請求函數wl_buffer_destroy和一個事件函數 void (*release) (void* data, struct wl_buffer* wl_buffer ).
再看看wl_surface的幾個接口:
destroy
attach , 綁定一個wl_buffer
dmage, invalidate surface的一塊區域
frame 申請幀繪制回調,每當繪制完一幀就發送wl_callback::done消息
==================================================
struct callback* frame_callback = wl_surface_frame(surface);
wl_callback_add_listener(frame_callback, &frame_listener, NULL);
===================================================
set_opaque_region
commit
...
好了,有了上述這些知識儲備,我們就可以分析wayland-wsegl這塊代碼了,我們通過分析來double check一下
先看下client端創建buffer的操作,簡單起見,我們先看下soft版本的
在client的初始化中creat_window(), 然后是create_buffer(), 然后是真正分配了一個ion buffer, 然后是通過
duss_hal_mem_share(fb.buffer, &duss_handle) 拿到這個用于共享的fd(duss_handle)
然后就是pool = eagle_shm_create_pool(shm)
然后就是 buffer = eagle_shm_pool_create_buffer(pool, duss_handle& 0xffffffff, duss_handle >> 32, w, h, pstride, fmt)
這個是定義在protocal中的接口。
完成wl_buffer的創建后,反手就是一波 wl_surface_attach(surface, buff, 0,0 , w, h)
然后是wl_suface_commit(surface)
差不多就是這個過程
我們繼續分析wl_shell和wl_shell_surface這倆貨
wl_shell是窗口的服務接口類
wl_shell_get_shell_surface(wl_shell*, wl_surface*) 創建指定wl_surface的顯示窗口對象wl_shell_surface
一般情況下,一個wl_surface對應一個wl_shell_surface
wl_shell_surface 就是真正的窗口,該服務提供了以下接口:
請求函數:
pong 用于響應ping事件函數
move
resize
set_toplevel
transient
set_fullscreen
set_popup
set_maxmized
set_title
..
事件函數
ping, configure, popup_done
再分析一下我們這邊的sample code:
shell_surface = wl_shell_get_shell_surface(shell, surface)
wl_shell_surface_set_toplevel(shell_surface)
wl_shell_surface_add_listener(shell_surface, &shell_surface_listener, NULL);
前面的get和設置為頂層沒啥好說的,著重說一下add_listener這個接口。
static const struct wl_shell_surface_listener shell_surface_listener = {
handle_ping, handle_configure, handle_popup_done};
就是通過add_listener來添加各個回調去響應server端的事件的。
總結
以上是生活随笔為你收集整理的wayland学习笔记(四) 全局服务对象初探的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [Ubuntu18]桌面美化-仿MAC主
- 下一篇: <数学的故事>之感想