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, ®istry_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