• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************
2  *
3  * Copyright (c) 2015 Advanced Driver Information Technology.
4  * This code is developed by Advanced Driver Information Technology.
5  * Copyright of Advanced Driver Information Technology, Bosch, and DENSO.
6  * All rights reserved.
7  *
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *        http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  *
21  ****************************************************************************/
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <fcntl.h>
28 #include <unistd.h>
29 #include <sys/epoll.h>
30 #include <wayland-client-protocol.h>
31 #include "window.h"
32 #include "util.h"
33 
34 #ifndef ARRAY_LENGTH
35 #  define ARRAY_LENGTH(a) (sizeof(a) / sizeof(a)[0])
36 #endif
37 
38 struct Global
39 {
40     uint32_t       name;
41     char          *p_interface;
42     uint32_t       version;
43     struct wl_list link;
44 };
45 
46 enum {
47     TYPE_NONE,
48     TYPE_TOPLEVEL
49 #if 0 /* not use */
50     TYPE_FULLSCREEN,
51     TYPE_MAXIMIZED,
52     TYPE_TRANSIENT
53 #endif
54 };
55 
56 /*** Event handlers ***********************************************************/
57 
58 /**
59  * wl_registry event handlers
60  */
61 static void
registry_handle_global(void * p_data,struct wl_registry * p_registry,uint32_t id,const char * p_interface,uint32_t version)62 registry_handle_global(void *p_data, struct wl_registry *p_registry,
63     uint32_t id, const char *p_interface, uint32_t version)
64 {
65     struct WaylandDisplay *p_display = (struct WaylandDisplay*)p_data;
66     struct Global *p_global;
67 
68     p_global = (struct Global*)allocate(sizeof(struct Global), 0);
69     if (NULL != p_global)
70     {
71         p_global->name        = id;
72         p_global->p_interface = strdup(p_interface);
73         p_global->version     = version;
74         wl_list_insert(p_display->global_list.prev, &p_global->link);
75     }
76 
77     if (0 == strcmp(p_interface, "wl_compositor"))
78     {
79         p_display->p_compositor = wl_registry_bind(p_registry, id,
80             &wl_compositor_interface, 1);
81     }
82     else if (0 == strcmp(p_interface, "wl_shell"))
83     {
84         p_display->p_shell = wl_registry_bind(p_registry, id,
85             &wl_shell_interface, 1);
86     }
87     else if (0 == strcmp(p_interface, "ivi_application"))
88     {
89         p_display->p_ivi_application = wl_registry_bind(p_registry, id,
90             &ivi_application_interface, 1);
91     }
92 
93     if (p_display->global_handler)
94     {
95         p_display->global_handler(p_display, id, p_interface, version,
96             p_display->p_user_data);
97     }
98 }
99 
100 static void
registry_handle_remove(void * p_data,struct wl_registry * p_registry,uint32_t id)101 registry_handle_remove(void *p_data, struct wl_registry *p_registry,
102     uint32_t id)
103 {
104     _UNUSED_(p_data);
105     _UNUSED_(p_registry);
106     _UNUSED_(id);
107 }
108 
109 static const struct wl_registry_listener registry_listener = {
110     registry_handle_global,
111     registry_handle_remove
112 };
113 
114 /**
115  * wl_surface event handlers
116  */
117 static void
surface_enter(void * p_data,struct wl_surface * p_surface,struct wl_output * p_output)118 surface_enter(void *p_data, struct wl_surface *p_surface,
119     struct wl_output *p_output)
120 {
121     _UNUSED_(p_data);
122     _UNUSED_(p_surface);
123     _UNUSED_(p_output);
124 }
125 
126 static void
surface_leave(void * p_data,struct wl_surface * p_surface,struct wl_output * p_output)127 surface_leave(void *p_data, struct wl_surface *p_surface,
128     struct wl_output *p_output)
129 {
130     _UNUSED_(p_data);
131     _UNUSED_(p_surface);
132     _UNUSED_(p_output);
133 }
134 
135 static const struct wl_surface_listener surface_listener = {
136     surface_enter,
137     surface_leave
138 };
139 
140 /**
141  * wl_shell_surface event handlers
142  */
143 static void
shell_surface_handle_ping(void * p_data,struct wl_shell_surface * p_shell_surface,uint32_t serial)144 shell_surface_handle_ping(void *p_data,
145     struct wl_shell_surface *p_shell_surface, uint32_t serial)
146 {
147     _UNUSED_(p_data);
148 
149     wl_shell_surface_pong(p_shell_surface, serial);
150 }
151 
152 static void
shell_surface_handle_configure(void * p_data,struct wl_shell_surface * p_shell_surface,uint32_t edges,int32_t width,int32_t height)153 shell_surface_handle_configure(void *p_data,
154     struct wl_shell_surface *p_shell_surface, uint32_t edges,
155     int32_t width, int32_t height)
156 {
157     _UNUSED_(p_shell_surface);
158     _UNUSED_(edges);
159 
160     WindowScheduleResize((struct WaylandEglWindow*)p_data, width, height);
161 }
162 
163 static void
shell_surface_handle_popup_done(void * p_data,struct wl_shell_surface * p_shell_surface)164 shell_surface_handle_popup_done(void *p_data,
165     struct wl_shell_surface *p_shell_surface)
166 {
167     _UNUSED_(p_data);
168     _UNUSED_(p_shell_surface);
169 }
170 
171 static const struct wl_shell_surface_listener shell_surface_listener = {
172     shell_surface_handle_ping,
173     shell_surface_handle_configure,
174     shell_surface_handle_popup_done
175 };
176 
177 /**
178  * wl_callback event handler
179  */
180 static void
frame_callback(void * p_data,struct wl_callback * p_cb,uint32_t time)181 frame_callback(void *p_data, struct wl_callback *p_cb, uint32_t time)
182 {
183     struct WaylandEglWindow *p_window = (struct WaylandEglWindow*)p_data;
184 
185     assert(p_cb == p_window->p_frame_cb);
186 
187     wl_callback_destroy(p_cb);
188 
189     p_window->p_frame_cb = NULL;
190     p_window->redraw_scheduled = 0;
191     p_window->time = time;
192 
193     if (0 != p_window->redraw_needed)
194     {
195         WindowScheduleRedraw(p_window);
196     }
197 }
198 
199 static const struct wl_callback_listener frame_listener = {
200     frame_callback
201 };
202 
203 static void
ivi_surface_configure(void * p_data,struct ivi_surface * p_ivi_surface,int32_t width,int32_t height)204 ivi_surface_configure(void *p_data, struct ivi_surface *p_ivi_surface,
205                       int32_t width, int32_t height)
206 {
207     _UNUSED_(p_data);
208     _UNUSED_(p_ivi_surface);
209     _UNUSED_(width);
210     _UNUSED_(height);
211 }
212 
213 static const struct ivi_surface_listener ivi_surface_event_listener = {
214     ivi_surface_configure
215 };
216 
217 /*** Static functions *********************************************************/
218 static int
set_cloexec_or_close(int fd)219 set_cloexec_or_close(int fd)
220 {
221     long flags;
222 
223     if (fd == -1)
224         return -1;
225 
226     flags = fcntl(fd, F_GETFD);
227     if (flags == -1)
228         goto err;
229 
230     if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
231         goto err;
232 
233     return fd;
234 
235 err:
236     close(fd);
237     return -1;
238 }
239 
240 int
os_epoll_create_cloexec()241 os_epoll_create_cloexec()
242 {
243     int fd = epoll_create1(EPOLL_CLOEXEC);
244 
245     if (fd >= 0)
246     {
247         return fd;
248     }
249 
250     if (errno != EINVAL)
251     {
252         return -1;
253     }
254 
255     fd = epoll_create(1);
256     return set_cloexec_or_close(fd);
257 }
258 
259 static void
handle_display_data(struct Task * p_task,uint32_t events)260 handle_display_data(struct Task *p_task, uint32_t events)
261 {
262     struct WaylandDisplay *p_display = (struct WaylandDisplay*)p_task;
263     struct epoll_event ep;
264     int ret;
265 
266     p_display->display_fd_events = events;
267 
268     if ((events & EPOLLERR) || (events & EPOLLHUP))
269     {
270         DisplayExit(p_display);
271         return;
272     }
273 
274     if (events & EPOLLIN)
275     {
276         ret = wl_display_dispatch(p_display->p_display);
277         if (ret == -1)
278         {
279             DisplayExit(p_display);
280             return;
281         }
282     }
283 
284     if (events & EPOLLOUT)
285     {
286         ret = wl_display_flush(p_display->p_display);
287         if (ret == 0)
288         {
289             ep.events = EPOLLIN | EPOLLERR | EPOLLHUP;
290             ep.data.ptr = &p_display->display_task;
291             epoll_ctl(p_display->epoll_fd,
292                 EPOLL_CTL_MOD, p_display->display_fd, &ep);
293         }
294         else if (ret == -1 && errno != EAGAIN)
295         {
296             DisplayExit(p_display);
297             return;
298         }
299     }
300 }
301 
302 void
display_watch_fd(struct WaylandDisplay * p_display,int fd,uint32_t events,struct Task * p_task)303 display_watch_fd(struct WaylandDisplay *p_display, int fd, uint32_t events,
304     struct Task *p_task)
305 {
306     struct epoll_event ep;
307 
308     ep.events = events;
309     ep.data.ptr = p_task;
310     epoll_ctl(p_display->epoll_fd, EPOLL_CTL_ADD, fd, &ep);
311 }
312 
313 static int
init_egl(struct WaylandDisplay * p_display)314 init_egl(struct WaylandDisplay *p_display)
315 {
316     EGLint iMajor, iMinor;
317     EGLint n;
318 
319     static const EGLint argb_config_attribs[] = {
320         EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
321         EGL_RED_SIZE,   1,
322         EGL_GREEN_SIZE, 1,
323         EGL_BLUE_SIZE,  1,
324         EGL_ALPHA_SIZE, 1,
325         EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
326         EGL_NONE
327     };
328 
329     static const EGLint context_attribs[] = {
330         EGL_CONTEXT_CLIENT_VERSION, 2,
331         EGL_NONE
332     };
333 
334     p_display->egldisplay = eglGetDisplay(p_display->p_display);
335 
336     if (!eglInitialize(p_display->egldisplay, &iMajor, &iMinor))
337     {
338         fprintf(stderr, "failed to initialize EGL\n");
339         return -1;
340     }
341 
342     if (!eglBindAPI(EGL_OPENGL_ES_API))
343     {
344         fprintf(stderr, "failed to bind EGL client API\n");
345         return -1;
346     }
347 
348     if (!eglChooseConfig(p_display->egldisplay, argb_config_attribs,
349         &p_display->eglconfig, 1, &n))
350     {
351         fprintf(stderr, "failed to choose argb EGL config\n");
352         return -1;
353     }
354 
355     p_display->eglcontext = eglCreateContext(p_display->egldisplay,
356         p_display->eglconfig, EGL_NO_CONTEXT, context_attribs);
357     if (NULL == p_display->eglcontext)
358     {
359         fprintf(stderr, "failed to create EGL context\n");
360         return -1;
361     }
362 #if 0
363     if (!eglMakeCurrent(p_display->egldisplay, NULL, NULL,
364         p_display->eglcontext))
365     {
366         fprintf(stderr, "failed to make EGL context current\n");
367         return -1;
368     }
369 #endif
370     return 0;
371 }
372 
373 static void
finish_egl(struct WaylandDisplay * p_display)374 finish_egl(struct WaylandDisplay *p_display)
375 {
376     eglMakeCurrent(p_display->egldisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
377         EGL_NO_CONTEXT);
378 
379     eglTerminate(p_display->egldisplay);
380     eglReleaseThread();
381 }
382 
383 static void
window_create_surface(struct WaylandEglWindow * p_window)384 window_create_surface(struct WaylandEglWindow *p_window)
385 {
386     if (NULL != p_window->p_egl_window)
387     {
388         return;
389     }
390 
391     p_window->p_egl_window = wl_egl_window_create(p_window->p_surface,
392         p_window->geometry.width, p_window->geometry.height);
393 
394     p_window->eglsurface = eglCreateWindowSurface(
395         p_window->p_display->egldisplay, p_window->p_display->eglconfig,
396         p_window->p_egl_window, NULL);
397 
398     eglMakeCurrent(p_window->p_display->egldisplay, p_window->eglsurface,
399         p_window->eglsurface, p_window->p_display->eglcontext);
400 }
401 
402 static void
idle_redraw(struct Task * p_task,uint32_t events)403 idle_redraw(struct Task *p_task, uint32_t events)
404 {
405     struct WaylandEglWindow *p_window = (struct WaylandEglWindow*)p_task;
406 
407     _UNUSED_(events);
408 
409     window_create_surface(p_window);
410 
411     if (NULL != p_window->redraw_handler)
412     {
413         p_window->redraw_handler(p_window, p_window->p_user_data);
414     }
415 
416     p_window->redraw_needed = 0;
417     wl_list_init(&p_window->redraw_task.link);
418 
419     if (p_window->type == TYPE_NONE)
420     {
421         p_window->type = TYPE_TOPLEVEL;
422         if (NULL != p_window->p_shell_surface)
423         {
424             wl_shell_surface_set_toplevel(p_window->p_shell_surface);
425         }
426     }
427 
428     p_window->p_frame_cb = wl_surface_frame(p_window->p_surface);
429     wl_callback_add_listener(p_window->p_frame_cb, &frame_listener,
430         p_window);
431 
432     eglSwapBuffers(p_window->p_display->egldisplay, p_window->eglsurface);
433 }
434 
435 /*** Public functions *********************************************************/
436 
437 void
WindowCreateSurface(struct WaylandEglWindow * p_window)438 WindowCreateSurface(struct WaylandEglWindow *p_window)
439 {
440     window_create_surface(p_window);
441 }
442 
443 void
DisplaySetGlobalHandler(struct WaylandDisplay * p_display,display_global_handler_t handler)444 DisplaySetGlobalHandler(struct WaylandDisplay *p_display,
445     display_global_handler_t handler)
446 {
447     struct Global *p_global;
448 
449     p_display->global_handler = handler;
450     if (NULL == handler)
451     {
452         return;
453     }
454 
455     wl_list_for_each(p_global, &p_display->global_list, link)
456     {
457         p_display->global_handler(p_display, p_global->name,
458             p_global->p_interface, p_global->version, p_display->p_user_data);
459     }
460 }
461 
462 void
DisplaySetUserData(struct WaylandDisplay * p_display,void * p_data)463 DisplaySetUserData(struct WaylandDisplay *p_display, void *p_data)
464 {
465     p_display->p_user_data = p_data;
466 }
467 
468 void
WindowScheduleRedraw(struct WaylandEglWindow * p_window)469 WindowScheduleRedraw(struct WaylandEglWindow *p_window)
470 {
471     p_window->redraw_needed = 1;
472     if (0 == p_window->redraw_scheduled)
473     {
474         p_window->redraw_task.run = idle_redraw;
475         wl_list_insert(&p_window->p_display->deferred_list, &p_window->redraw_task.link);
476         p_window->redraw_scheduled = 1;
477     }
478 }
479 
480 void
WindowScheduleResize(struct WaylandEglWindow * p_window,int width,int height)481 WindowScheduleResize(struct WaylandEglWindow *p_window, int width, int height)
482 {
483     p_window->geometry.width  = width;
484     p_window->geometry.height = height;
485 
486     WindowScheduleRedraw(p_window);
487 }
488 
489 int
DisplayAcquireWindowSurface(struct WaylandDisplay * p_display,struct WaylandEglWindow * p_window)490 DisplayAcquireWindowSurface(struct WaylandDisplay *p_display,
491     struct WaylandEglWindow *p_window)
492 {
493     if (!eglMakeCurrent(p_display->egldisplay, p_window->eglsurface,
494         p_window->eglsurface, p_display->eglcontext))
495     {
496         fprintf(stderr, "[ERR] failed to make surface current\n");
497     }
498 
499     return 0;
500 }
501 
502 struct WaylandDisplay *
CreateDisplay(int argc,char ** argv)503 CreateDisplay(int argc, char **argv)
504 {
505     struct WaylandDisplay *p_display;
506 
507     _UNUSED_(argc);
508     _UNUSED_(argv);
509 
510     p_display = (struct WaylandDisplay*)malloc(sizeof(struct WaylandDisplay));
511     if (NULL == p_display)
512     {
513         return NULL;
514     }
515 
516     p_display->p_display = wl_display_connect(NULL);
517     if (NULL == p_display->p_display)
518     {
519         fprintf(stderr, "[ERR] failed to connect to wayland: %m\n");
520         free(p_display);
521         return NULL;
522     }
523 
524     p_display->epoll_fd = os_epoll_create_cloexec();
525     p_display->display_fd = wl_display_get_fd(p_display->p_display);
526     p_display->display_task.run = handle_display_data;
527     display_watch_fd(p_display, p_display->display_fd,
528         EPOLLIN | EPOLLERR | EPOLLHUP, &p_display->display_task);
529 
530     wl_list_init(&p_display->global_list);
531     wl_list_init(&p_display->surface_list);
532     wl_list_init(&p_display->deferred_list);
533 
534     p_display->p_registry = wl_display_get_registry(p_display->p_display);
535     wl_registry_add_listener(p_display->p_registry, &registry_listener,
536         p_display);
537 
538     if (0 > wl_display_dispatch(p_display->p_display))
539     {
540         fprintf(stderr, "[ERR] failed to process wayland connection: %m\n");
541         free(p_display);
542         return NULL;
543     }
544 
545     if (0 > init_egl(p_display))
546     {
547         fprintf(stderr, "[ERR] EGL does not seem to work\n");
548         free(p_display);
549         return NULL;
550     }
551 
552     return p_display;
553 }
554 
555 void
DestroyDisplay(struct WaylandDisplay * p_display)556 DestroyDisplay(struct WaylandDisplay *p_display)
557 {
558     finish_egl(p_display);
559 
560     if (NULL != p_display->p_shell)
561         wl_shell_destroy(p_display->p_shell);
562 
563     if (NULL != p_display->p_ivi_application)
564         ivi_application_destroy(p_display->p_ivi_application);
565 
566     if (NULL != p_display->p_compositor)
567         wl_compositor_destroy(p_display->p_compositor);
568 
569     if (NULL != p_display->p_registry)
570         wl_registry_destroy(p_display->p_registry);
571 
572     if (NULL != p_display->p_display)
573         wl_display_disconnect(p_display->p_display);
574 
575     free(p_display);
576 }
577 
578 struct WaylandEglWindow *
CreateEglWindow(struct WaylandDisplay * p_display,const char * p_window_title,uint32_t surface_id)579 CreateEglWindow(struct WaylandDisplay *p_display, const char *p_window_title,
580                 uint32_t surface_id)
581 {
582     struct WaylandEglWindow *p_window;
583 
584     p_window =
585         (struct WaylandEglWindow*)malloc(sizeof(struct WaylandEglWindow));
586     if (NULL == p_window)
587     {
588         return NULL;
589     }
590     memset(p_window, 0x00, sizeof(struct WaylandEglWindow));
591 
592     p_window->p_display = p_display;
593     p_window->p_surface = wl_compositor_create_surface(p_display->p_compositor);
594 
595     wl_surface_add_listener(p_window->p_surface, &surface_listener, p_window);
596 
597     if (NULL != p_display->p_shell)
598     {
599         p_window->p_shell_surface =
600             wl_shell_get_shell_surface(p_display->p_shell, p_window->p_surface);
601 
602         wl_shell_surface_set_user_data(p_window->p_shell_surface, p_window);
603 
604         wl_shell_surface_add_listener(p_window->p_shell_surface,
605             &shell_surface_listener, p_window);
606 
607         wl_shell_surface_set_title(p_window->p_shell_surface, p_window_title);
608     }
609 
610     if (NULL != p_display->p_ivi_application)
611     {
612         /* force sync to compositor */
613         wl_display_roundtrip(p_display->p_display);
614         p_window->p_ivi_surface =
615             ivi_application_surface_create(p_display->p_ivi_application,
616                                            surface_id,
617                                            p_window->p_surface);
618         ivi_surface_add_listener(p_window->p_ivi_surface,
619                                  &ivi_surface_event_listener, p_window);
620     }
621 
622     wl_list_insert(p_display->surface_list.prev, &p_window->link);
623 
624     return p_window;
625 }
626 
627 void
DisplayRun(struct WaylandDisplay * p_display)628 DisplayRun(struct WaylandDisplay *p_display)
629 {
630     struct Task *p_task;
631     struct epoll_event ep[16];
632     int i, count, ret;
633 
634     p_display->running = 1;
635     while (1)
636     {
637         while (0 == wl_list_empty(&p_display->deferred_list))
638         {
639             /* avoid lint warning */
640             const __typeof__(((struct Task*)0)->link) *p_ptr = p_display->deferred_list.prev;
641             p_task = (struct Task*)((unsigned long)p_ptr - offsetof(struct Task, link));
642 
643             wl_list_remove(&p_task->link);
644             p_task->run(p_task, 0);
645         }
646 
647         wl_display_dispatch_pending(p_display->p_display);
648 
649         if (0 == p_display->running)
650         {
651             break;
652         }
653 
654         ret = wl_display_flush(p_display->p_display);
655         if (0 > ret && EAGAIN == errno)
656         {
657             ep[0].events = EPOLLIN | EPOLLOUT | EPOLLERR | EPOLLHUP;
658             ep[0].data.ptr = &p_display->display_task;
659             epoll_ctl(p_display->epoll_fd, EPOLL_CTL_MOD,
660                 p_display->display_fd, &ep[0]);
661         }
662         else if (0 > ret)
663         {
664             break;
665         }
666 
667         count = epoll_wait(p_display->epoll_fd, ep, ARRAY_LENGTH(ep), 1);
668         for (i = 0; i < count; ++i)
669         {
670             p_task = ep[i].data.ptr;
671             p_task->run(p_task, ep[i].events);
672         }
673     }
674 }
675 
676 void
DisplayExit(struct WaylandDisplay * p_display)677 DisplayExit(struct WaylandDisplay *p_display)
678 {
679     if (NULL != p_display)
680     {
681         p_display->running = 0;
682     }
683 }
684