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端的事件的。