系列索引地址:Wayland入门教程索引 
上一篇:Wayland入门教程04.08:Protocol说明 
本文介绍如何显示一个窗口。
Wayland窗口绘制有两种方法:
  共享内存在添加注册函数中添加一个shm部分的处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static  void global_registry_handler (void  *data, struct  wl_registry *registry, 						uint32_t  id, const  char  *interface, uint32_t  version)  { 	printf ("Got a registry event for %s id %d\n" , interface, id); 	if  (strcmp (interface, "wl_compositor" ) == 0 ) 	{ 		compositor = wl_registry_bind (registry, id, &wl_compositor_interface, version); 	} 	else  if  (strcmp (interface, xdg_wm_base_interface.name) == 0 ) 	{ 		xdg_shell = wl_registry_bind (registry, id, &xdg_wm_base_interface, version); 	} 	else  if  (strcmp (interface, "wl_shm" ) == 0 ) 	{ 		shm = wl_registry_bind (registry, id, &wl_shm_interface, version); 		wl_shm_add_listener (shm, &shm_listener, NULL ); 	} }
可以看到共享内存中有回调函数shm_listener,我们要实现它:
1 2 3 struct  wl_shm_listener  shm_listener={ 	.format=shm_format };
或者和之前一样
1 2 3 struct  wl_shm_listener  shm_listener={ 	shm_format };
这样也可以,具体声明在wayland-client-protocol.h。
此函数的作用是
1 2 3 4 5 6 7 8 9 10 11 12 struct  wl_shm_listener  { 	 	void  (*format)(void  *data, 		       struct  wl_shm *wl_shm, 		       uint32_t  format); };
就是说,此函数返回的参数可以知道buffer支持的像素类型。
那么我们就输出支持的所有像素类型名称。
1 2 3 4 5 static  void  shm_format (void  *data, struct  wl_shm *wl_shm, uint32_t  format)  { 	fprintf (stderr,"Format %d\n" ,format); }
编译运行执行输出为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 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_ARGB8888 0 0 WL_SHM_FORMAT_XRGB8888 1 1 WL_SHM_FORMAT_RGB565 909199186 0x36314752 WL_SHM_FORMAT_ARGB2101010 808669761 0x30335241 WL_SHM_FORMAT_XRGB2101010 808669784 0x30335258 WL_SHM_FORMAT_ARGB16161616F 1211388481 0x48345241 
从几个典型值可以看出,主要支持16位、32位、64位像素值。
  创建窗口  Wayland窗口:wl_surfaceWayland窗口绘制完全由程序控制,包括标题栏绘制,边框绘制,窗口移动,改变大小等。 其中与窗口绘制有关的函数有:
wl_surface_attach() 将缓存绑定到窗口上,窗口大小会根据缓存重新计算。 wl_surface_damage() 标记窗口失效的区域 wl_surface_commit() 缓存提交请求,合成器会锁定提交的缓存,直到下一次wl_surface_attach或合成器主动释放。 wl_surface_frame() 申请帧绘制回调,每当绘制完一帧就发送wl_callback::done消息。 创建函数和代码
1 2 3 4 5 6 7 static  void  create_window ()  { 	buffer = create_buffer (); 	wl_surface_attach (surface,buffer,0 ,0 );     wl_surface_commit (surface); }
然后创建缓存
  Walyand缓存:wl_bufferWayland窗口显示的内容由wl_buffer负责。Wayland与X Server不同,Wayland只支持客户端直接绘制,合成器不提供对wl_buffer的绘制操作。与wl_buffer有关的函数有:
wl_shm_create_pool() 创建一个缓存池,缓存池可以mmap到程序的内存空间中。 wl_shm_pool_create() 创建一个wl_buffer。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static  struct  wl_buffer  *create_buffer () {     struct  wl_shm_pool  *pool;     int  stride = WIDTH * 4 ;      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_shm_pool_destroy (pool);     return  buff; }
根据需要创建一个给定大小的新的、唯一的、匿名的文件,并为其返回文件描述符。文件描述符被设置为cloexec。该文件立即适合于mmap()的给定大小的偏移量为零。
该文件不应该像磁盘那样有永久备份存储,但如果XDG_RUNTIME_DIR在操作系统中没有正确实现,可能有。
该文件名将从文件系统中删除。
该文件适用于通过使用*SCM_RIGHTS方法通过Unix套接字传输文件描述符来实现进程之间的缓冲区共享。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 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; }
  显示窗口调用wl_display_dispath()函数的线程会自动成为主线程,并且拥有主消息队列。
Wayland允许创建多个消息队列,使用wl_display_create_queue()创建,新建的消息队列可以绑定到一个wl_proxy对象上。
与Win32消息循环不同,Wayland消息循环只需要调用一个函数:
1 2 3 int  ret = 0 ;while (ret!=-1 )     ret = wl_display_dispatch (dpy);
wl_proxy消息队列的消息循环使用:
1 2 3 int  ret = 0 ;while (ret!=-1 )     ret = wl_display_dispatch_queue (dpy, queue);
消息循环代码为
1 2 3 while (wl_display_dispatch (display)!=-1 ){ 	; }
效果为:
因为是死循环,那么这个窗口将一直存在直到手动杀死。 除了显示窗口没有其他操作,如果点击窗口就会出现程序未响应的情况。 这是一个最基本的窗口,没有状态栏、没有最大最小按钮、没有拖拽移动功能 窗口默认为黑色的,因为shm_data值没有修改过,默认为0。我们将其修改一下。
在create_buffer函数中,我们将shm_data和fd对应的文件进行了映射,那么修改shm_data的数据就会相应的修改fd文件的数据。反之亦然。
以像素的方式修改每个值
1 2 3 4 5 6 7 8 9 10 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 ;     } }
效果为:
  EGL版有了上面的基础,EGL版本就简单一些
  初始化1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 display = wl_display_connect (0 );if (!display){     printf ("Cannot connect to wayland compositor.\n" );     return  -1 ; } registry = wl_display_get_registry (display);wl_registry_add_listener (registry,®istry_listener,NULL );wl_display_dispatch (display);wl_display_roundtrip (display);if  (compositor == NULL  || shell == NULL ) {     fprintf (stderr, "Can't find compositor or shell\n" );     return  -1 ; }else {     fprintf (stderr, "Found compositor and shell\n" ); } surface = wl_compositor_create_surface (compositor);if  (surface == NULL ) {     fprintf (stderr, "Can't create surface\n" );     return  -1 ; }else {     fprintf (stderr, "Created surface\n" ); }struct  xdg_surface  *shell_surface = xdg_wm_base_get_xdg_surface (xdg_shell, surface);if  (shell_surface == NULL ) { 	fprintf (stderr, "Can't create shell surface\n" ); 	exit (1 ); }else  { 	fprintf (stderr, "Created shell surface\n" ); }struct  xdg_toplevel  *toplevel = xdg_surface_get_toplevel (shell_surface);xdg_surface_add_listener (shell_surface, &surface_listener, NULL );
连接到wayland服务器,创建窗口,还要添加注册捕获
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 static  void global_registry_handler (void  *data, struct  wl_registry *registry, 						uint32_t  id, const  char  *interface, uint32_t  version)  { 	printf ("Got a registry event for %s id %d\n" , interface, id); 	if  (strcmp (interface, "wl_compositor" ) == 0 ) 	{ 		compositor = wl_registry_bind (registry, id, &wl_compositor_interface, version); 	} 	else  if  (strcmp (interface, xdg_wm_base_interface.name) == 0 ) 	{ 		xdg_shell = wl_registry_bind (registry, id, &xdg_wm_base_interface, version); 	} }static  void  registry_remover (void  *data, struct  wl_registry *registry, uint32_t  id)  {}static  const  struct  wl_registry_listener  registry_listener= {     registry_handler,     registry_remover };
  窗口显示1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37  EGLint major, minor, config_count, tmp_n; EGLint config_attribs[] = {     EGL_SURFACE_TYPE, EGL_WINDOW_BIT,     EGL_RED_SIZE, 8 ,     EGL_GREEN_SIZE, 8 ,     EGL_BLUE_SIZE, 8 ,     EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,     EGL_NONE }; EGLint context_attribs[] = {     EGL_CONTEXT_CLIENT_VERSION, 2 ,     EGL_NONE }; egl_display = eglGetDisplay ((EGLNativeDisplayType)display);eglInitialize (egl_display, &major, &minor);printf ("EGL major: %d, minor %d\n" , major, minor);eglGetConfigs (egl_display, 0 , 0 , &config_count);printf ("EGL has %d configs\n" , config_count);eglChooseConfig (egl_display, config_attribs, &(egl_config), 1 , &tmp_n); egl_context = eglCreateContext (         egl_display,         egl_config,         EGL_NO_CONTEXT,         context_attribs); egl_window = wl_egl_window_create (surface, WIDTH, HEIGHT); egl_surface = eglCreateWindowSurface (         egl_display,         egl_config,         egl_window,         0 );eglMakeCurrent (egl_display, egl_surface, egl_surface, egl_context);eglSwapBuffers (egl_display, egl_surface);
获取窗口、初始化、获取配置、选择配置、创建上下文、创建EGL窗口、交换显示缓冲区
具体的到EGL开发系列中再说。
窗口绘制完成后,循环显示窗口
1 2 3 4 5 while  (wl_display_dispatch (display) != -1 ) { }wl_display_disconnect (display);
编译运行显示窗口
默认为黑色背景,我们调整一下。
可以看到代码中有swapbuffer字样,在标准的OpenGL开发中一般是调用绘图函数,然后使用双缓冲技术交换显示数据。绘制的背景颜色代码应该在此代码的上方。
1 2 glClearColor (0.5f , 0.5f , 0.0f , 1.0f );glClear (GL_COLOR_BUFFER_BIT);
将背景设置为棕色
效果为:
可以参考OpenGLut入门系列教程索引 
  总结这是共享内存的流程图
我们来看一下EGL调用流程图
可以看到,EGL方式相对简单,代码量少,函数调用少。
这个操作流程是以后的基础。
有了窗口,我们就可以在窗口上进行更复杂的操作。
完整代码在Wayland_Freshman 中的08.09.window下。
下一篇:Wayland入门教程04.10:输入设备管理器