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端的事件的。
最新评论