Wayland入门9:第一个窗口

前面已经可以成功的连接服务器了。本文介绍如何显示一个窗口。

Wayland窗口绘制有两种方法:1) 共享内存方式(SHM)、2)EGL。

要想使用EGL,你得会EGL,还得先会OpenGL,这两个哪一个工程量都不小。本文使用方式1绘制窗口(第二种方法等OpenGL开发系列和EGL开发系列更新完再说)。

窗口

先将上一篇的代码拿过来,当然不需要输出所有的registry信息。

所以将

printf("Got a registry event for %s id %d\n", interface, id);

删除。

改动之后最好编译一下,以确保每一步都正确。

在添加注册函数中添加一个shm部分的处理

static void
global_registry_handler(void *data, struct wl_registry *registry,
		uint32_t id, const char *interface, uint32_t version) {
	if (strcmp(interface, "wl_compositor") == 0) {
		BIND_WL_REG(registry, compositor, id, &wl_compositor_interface, 1);
	} else if (strcmp(interface, "wl_shell") == 0) {
		BIND_WL_REG(registry, shell, id, &wl_shell_interface, 1);
	} else if (strcmp(interface, "wl_shm")==0){
		BIND_WL_REG(registry, shm, id, &wl_shm_interface, 1);
		wl_shm_add_listener(shm, &shm_listener, NULL);
	}
}

可以看到共享内存中有回调函数shm_listener,我们要实现它:

struct wl_shm_listener shm_listener={
	.format=shm_format
};

和之前一样

struct wl_shm_listener shm_listener={
	shm_format
};

这样也可以,具体声明在wayland-client-protocol.h

此函数的作用是

struct wl_shm_listener {
	/**
	 * pixel format description
	 *
	 * Informs the client about a valid pixel format that can be used
	 * for buffers. Known formats include argb8888 and xrgb8888.
	 * @param format buffer pixel format
	 */
	void (*format)(void *data,
		       struct wl_shm *wl_shm,
		       uint32_t format);
};

就是说,此函数返回的参数可以知道buffer支持的像素类型。

那么我们就输出支持的所有像素类型名称。

static void 
shm_format(void *data, struct wl_shm *wl_shm, uint32_t format)
{
	//struct display *d = data;
    //	d->formats |= (1 << format);
	fprintf(stderr,"Format %d\n",format);
}

编译运行执行输出为:

hyper@ubuntu:~/Nutstore Files/Nutstore/Wayland_Freshman/9.window$ ./window
connected to display
Format 0
Format 1
Format 909199186
Format 808669761
Format 808669784
Format 808665665
Format 808665688
Format 1211388481
Format 1211388504
Format 1211384385
Format 1211384408
Found compositor
Created surface
Created shell surface
disconnected from display

输出一大堆数字,使用的时候我们要将其强制转换为enum wl_shm_format类型。

此类型可以在wayland-client-protocol.h中找到详细信息。

名称数值16进制数值
WL_SHM_FORMAT_ARGB888800
WL_SHM_FORMAT_XRGB888811
WL_SHM_FORMAT_RGB5659091991860x36314752
WL_SHM_FORMAT_ARGB21010108086697610x30335241
WL_SHM_FORMAT_XRGB21010108086697840x30335258
WL_SHM_FORMAT_ARGB16161616F12113884810x48345241

从几个典型值可以看出,主要支持16位、32位、64位像素值。

创建窗口

Wayland窗口:wl_surface

Wayland窗口绘制完全由程序控制,包括标题栏绘制,边框绘制,窗口移动,改变大小等。 其中与窗口绘制有关的函数有:

  • wl_surface_attach() 将缓存绑定到窗口上,窗口大小会根据缓存重新计算。
  • wl_surface_damage() 标记窗口失效的区域
  • wl_surface_commit() 缓存提交请求,合成器会锁定提交的缓存,直到下一次wl_surface_attach或合成器主动释放。
  • wl_surface_frame() 申请帧绘制回调,每当绘制完一帧就发送wl_callback::done消息。
static void 
create_window(){
	buffer = create_buffer();

	wl_surface_attach(surface,buffer,0,0);
	//wl_surface_damage(surface, 0, 0, WIDTH, HEIGHT);
    wl_surface_commit(surface);
}

然后创建缓存

Walyand缓存:wl_buffer

Wayland窗口显示的内容由wl_buffer负责。Wayland与X Server不同,Wayland只支持客户端直接绘制,合成器不提供对wl_buffer的绘制操作。与wl_buffer有关的函数有:

  • wl_shm_create_pool() 创建一个缓存池,缓存池可以mmap到程序的内存空间中。
  • wl_shm_pool_create() 创建一个wl_buffer。
static struct wl_buffer *
create_buffer() {
    struct wl_shm_pool *pool;
    int stride = WIDTH * 4; // 4 bytes per pixel
    int size = stride * HEIGHT;
    int fd;
    struct wl_buffer *buff;

    fd = os_create_anonymous_file(size);
    if (fd < 0) {
	    fprintf(stderr, "creating a buffer file for %d B failed: %m\n",size);
	    exit(1);
    }
    
    shm_data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shm_data == MAP_FAILED) {
	    fprintf(stderr, "mmap failed: %m\n");
	    close(fd);
	    exit(1);
    }

    pool = wl_shm_create_pool(shm, fd, size);
    buff = wl_shm_pool_create_buffer(pool, 0,
					  WIDTH, HEIGHT,
					  stride, 	
					  WL_SHM_FORMAT_XRGB8888);
    //wl_buffer_add_listener(buffer, &buffer_listener, buffer);
    wl_shm_pool_destroy(pool);
    return buff;
}

根据需要创建一个给定大小的新的、唯一的、匿名的文件,并为其返回文件描述符。文件描述符被设置为cloexec。该文件立即适合于mmap()的给定大小的偏移量为零。

该文件不应该像磁盘那样有永久备份存储,但如果XDG_RUNTIME_DIR在操作系统中没有正确实现,可能有。

该文件名将从文件系统中删除。

该文件适用于通过使用*SCM_RIGHTS方法通过Unix套接字传输文件描述符来实现进程之间的缓冲区共享。

static int
set_cloexec_or_close(int fd)
{
        long flags;

        if (fd == -1)
                return -1;

        flags = fcntl(fd, F_GETFD);
        if (flags == -1)
                goto err;

        if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
                goto err;

        return fd;

err:
        close(fd);
        return -1;
}

static int
create_tmpfile_cloexec(char *tmpname)
{
        int fd;

#ifdef HAVE_MKOSTEMP
        fd = mkostemp(tmpname, O_CLOEXEC);
        if (fd >= 0)
                unlink(tmpname);
#else
        fd = mkstemp(tmpname);
        if (fd >= 0) {
                fd = set_cloexec_or_close(fd);
                unlink(tmpname);
        }
#endif
        return fd;
}

int os_create_anonymous_file(off_t size)
{
	static const char template[] = "/weston-shared-XXXXXX";
	const char *path;
	char *name;
	int fd;

	path = getenv("XDG_RUNTIME_DIR");
	if (!path)
	{
		errno = ENOENT;
		return -1;
	}

	name = malloc(strlen(path) + sizeof(template));
	if (!name)
		return -1;

	strcpy(name, path);
	strcat(name, template);
	printf("%s\n", name);

	fd = create_tmpfile_cloexec(name);

	free(name);

	if (fd < 0)
		return -1;

	if (ftruncate(fd, size) < 0)
	{
		close(fd);
		return -1;
	}

	return fd;
}

显示窗口

while(wl_display_dispatch(display)!=-1){
	;
}

效果为:

display

  • 因为是死循环,那么这个窗口将一直存在直到手动杀死。
  • 除了显示窗口没有其他操作,如果点击窗口就会出现程序未响应的情况。
  • 这是一个最基本的窗口,没有状态栏、没有最大最小按钮、没有拖拽移动功能

窗口默认为黑色的,因为shm_data值没有修改过,默认为0。我们将其修改一下。

在create_buffer函数中,我们将shm_data和fd对应的文件进行了映射,那么修改shm_data的数据就会相应的修改fd文件的数据。反之亦然。

以像素的方式修改每个值

static void
paint_pixels() {
    int n;
    uint32_t *pixel = shm_data;

    fprintf(stderr, "Painting pixels\n");
    for (n =0; n < WIDTH*HEIGHT; n++) {
	*pixel++ = 0xff0000;//红色
    }
}

效果为:

red_bg

消息队列

主消息队列

调用wl_display_dispath()函数的线程会自动成为主线程,并且拥有主消息队列。

wl_proxy消息队列

Wayland允许创建多个消息队列,使用wl_display_create_queue()创建,新建的消息队列可以绑定到一个wl_proxy对象上。

消息循环

与Win32消息循环不同,Wayland消息循环只需要调用一个函数:

int ret = 0;
while(ret!=-1)
    ret = wl_display_dispatch(dpy);

wl_proxy消息队列的消息循环使用:

int ret = 0;
while(ret!=-1)
    ret = wl_display_dispatch_queue(dpy, queue);

Wayland消息处理

Wayland没有窗口函数,而是使用wl_proxy listener处理具体的消息。wl_proxy代表服务器端的对象,服务器发送的消息会放入消息队列,如果使用wl_proxy_set_queue()设置过其他消息队列,则消息会放入指定的消息队列,wl_display_dispatch负责读取消息并调用proxy的listener。proxy的listener是由 wl_proxy_add_listener()指定的。

wl_proxy listener

  • wl_pointer_add_listener() 鼠标消息处理
  • wl_keyboard_add_listener() 键盘消息处理
  • wl_callback_add_listener() wl_callback由wl_surface_frame() 创建,每当服务器显示下一帧使会给wl_callback发送一条消息。

总结

我们来看一下调用流程图

flow

这个操作流程是以后的基础。

有了窗口,我们就可以在窗口上进行更复杂的操作。

完整代码在Wayland_Frashman中的9.window下。

本文首发于:Wayland入门9:第一个窗口

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

幽迷狂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值