zoukankan      html  css  js  c++  java
  • 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端的事件的。

  • 相关阅读:
    多项式学习笔记(二) NTT
    矩阵树定理学习笔记
    拓展BSGS 学习笔记
    P2257 YY的GCD
    P1891 疯狂的lcm
    友链
    关于我
    焚燃指间の回忆
    洛谷P4180
    洛谷P2292
  • 原文地址:https://www.cnblogs.com/Arnold-Zhang/p/15312088.html
Copyright © 2011-2022 走看看