799729b165a8c25536b37f13baac8c3b8c06fd55
[src/libhomescreen.git] / sample / simple-egl / src / simple-egl.cpp
1 /*
2  * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
3  * Copyright © 2011 Benjamin Franzke
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24
25 #include <mutex>
26 #include <chrono>
27
28 #include <iostream>
29 #include <string>
30 #include <stdarg.h>
31 #include <sys/types.h>
32 #include <thread>
33 #include <exception>
34 #include <vector>
35 #include <sstream>
36
37 #include <assert.h>
38 #include <signal.h>
39
40 #include <wayland-client.h>
41 #include <wayland-egl.h>
42
43 #include <GLES2/gl2.h>
44 #include <EGL/egl.h>
45 #include <EGL/eglext.h>
46
47 #include <unistd.h>
48 #include <time.h>
49
50 #include <libwindowmanager.h>
51 #include <libhomescreen.hpp>
52
53 #include <ilm/ivi-application-client-protocol.h>
54 #include "hmi-debug.h"
55
56 using namespace std;
57
58 const char* log_prefix = "simple-egl";
59 uint32_t g_id_ivisurf = 9009;
60 long port = 1700;
61 string token = string("wm");
62
63 string app_name = string("Navigation");
64 const char* main_role = "navigation";
65
66 LibHomeScreen* hs;
67 LibWindowmanager *wm;
68
69 static const struct wl_interface *types[] = {
70         NULL,
71         NULL,
72         NULL,
73         &wl_surface_interface,
74         &ivi_surface_interface,
75 };
76
77 static const struct wl_message ivi_surface_requests[] = {
78         { "destroy", "", types + 0 },
79 };
80
81 static const struct wl_message ivi_surface_events[] = {
82         { "configure", "ii", types + 0 },
83 };
84
85 const struct wl_interface ivi_surface_interface = {
86         "ivi_surface", 1,
87         1, ivi_surface_requests,
88         1, ivi_surface_events,
89 };
90
91 static const struct wl_message ivi_application_requests[] = {
92         { "surface_create", "uon", types + 2 },
93 };
94
95 const struct wl_interface ivi_application_interface = {
96         "ivi_application", 1,
97         1, ivi_application_requests,
98         0, NULL,
99 };
100
101 #include "platform.h"
102
103 #ifndef EGL_EXT_swap_buffers_with_damage
104 #define EGL_EXT_swap_buffers_with_damage 1
105 typedef EGLBoolean (EGLAPIENTRYP PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)(EGLDisplay dpy, EGLSurface surface, EGLint *rects, EGLint n_rects);
106 #endif
107
108 #ifndef EGL_EXT_buffer_age
109 #define EGL_EXT_buffer_age 1
110 #define EGL_BUFFER_AGE_EXT                      0x313D
111 #endif
112
113 struct window;
114 struct seat;
115
116 struct display {
117         struct wl_display *display;
118         struct wl_registry *registry;
119         struct wl_compositor *compositor;
120         struct wl_seat *seat;
121         struct {
122                 EGLDisplay dpy;
123                 EGLContext ctx;
124                 EGLConfig conf;
125         } egl;
126         struct window *window;
127         struct ivi_application *ivi_application;
128
129         PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC swap_buffers_with_damage;
130 };
131
132 struct geometry {
133         int width, height;
134 };
135
136 struct window {
137         struct display *display;
138         struct geometry geometry, window_size;
139         struct {
140                 GLuint rotation_uniform;
141                 GLuint pos;
142                 GLuint col;
143         } gl;
144
145         uint32_t benchmark_time, frames;
146         struct wl_egl_window *native;
147         struct wl_surface *surface;
148         struct ivi_surface *ivi_surface;
149         EGLSurface egl_surface;
150         struct wl_callback *callback;
151         int fullscreen, opaque, buffer_size, frame_sync;
152 };
153
154 static const char *vert_shader_text =
155         "uniform mat4 rotation;\n"
156         "attribute vec4 pos;\n"
157         "attribute vec4 color;\n"
158         "varying vec4 v_color;\n"
159         "void main() {\n"
160         "  gl_Position = rotation * pos;\n"
161         "  v_color = color;\n"
162         "}\n";
163
164 static const char *frag_shader_text =
165         "precision mediump float;\n"
166         "varying vec4 v_color;\n"
167         "void main() {\n"
168         "  gl_FragColor = v_color;\n"
169         "}\n";
170
171 static int running = 1;
172
173 static void
174 init_egl(struct display *display, struct window *window)
175 {
176         static const EGLint context_attribs[] = {
177                 EGL_CONTEXT_CLIENT_VERSION, 2,
178                 EGL_NONE
179         };
180         const char *extensions;
181
182         EGLint config_attribs[] = {
183                 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
184                 EGL_RED_SIZE, 1,
185                 EGL_GREEN_SIZE, 1,
186                 EGL_BLUE_SIZE, 1,
187                 EGL_ALPHA_SIZE, 1,
188                 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
189                 EGL_NONE
190         };
191
192         EGLint major, minor, n, count, i, size;
193         EGLConfig *configs;
194         EGLBoolean ret;
195
196         if (window->opaque || window->buffer_size == 16)
197                 config_attribs[9] = 0;
198
199         display->egl.dpy = weston_platform_get_egl_display(EGL_PLATFORM_WAYLAND_KHR, display->display, NULL);
200         assert(display->egl.dpy);
201
202         ret = eglInitialize(display->egl.dpy, &major, &minor);
203         assert(ret == EGL_TRUE);
204         ret = eglBindAPI(EGL_OPENGL_ES_API);
205         assert(ret == EGL_TRUE);
206
207         if (!eglGetConfigs(display->egl.dpy, NULL, 0, &count) || count < 1)
208                 assert(0);
209
210         configs = calloc(count, sizeof *configs);
211         assert(configs);
212
213         ret = eglChooseConfig(display->egl.dpy, config_attribs,
214                               configs, count, &n);
215         assert(ret && n >= 1);
216
217         for (i = 0; i < n; i++) {
218                 eglGetConfigAttrib(display->egl.dpy,
219                                    configs[i], EGL_BUFFER_SIZE, &size);
220                 if (window->buffer_size == size) {
221                         display->egl.conf = configs[i];
222                         break;
223                 }
224         }
225         free(configs);
226         if (display->egl.conf == NULL) {
227                 HMI_ERROR(log_prefix,"did not find config with buffer size %d",
228                         window->buffer_size);
229                 exit(EXIT_FAILURE);
230         }
231
232         display->egl.ctx = eglCreateContext(display->egl.dpy,
233                                             display->egl.conf,
234                                             EGL_NO_CONTEXT, context_attribs);
235         assert(display->egl.ctx);
236
237         display->swap_buffers_with_damage = NULL;
238         extensions = eglQueryString(display->egl.dpy, EGL_EXTENSIONS);
239         if (extensions &&
240             strstr(extensions, "EGL_EXT_swap_buffers_with_damage") &&
241             strstr(extensions, "EGL_EXT_buffer_age"))
242                 display->swap_buffers_with_damage =
243                         (PFNEGLSWAPBUFFERSWITHDAMAGEEXTPROC)
244                         eglGetProcAddress("eglSwapBuffersWithDamageEXT");
245
246         if (display->swap_buffers_with_damage)
247                 HMI_DEBUG(log_prefix,"has EGL_EXT_buffer_age and EGL_EXT_swap_buffers_with_damage");
248
249 }
250
251 static void
252 fini_egl(struct display *display)
253 {
254         eglTerminate(display->egl.dpy);
255         eglReleaseThread();
256 }
257
258 static GLuint
259 create_shader(struct window *window, const char *source, GLenum shader_type)
260 {
261         GLuint shader;
262         GLint status;
263
264         shader = glCreateShader(shader_type);
265         assert(shader != 0);
266
267         glShaderSource(shader, 1, (const char **) &source, NULL);
268         glCompileShader(shader);
269
270         glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
271         if (!status) {
272                 char log[1000];
273                 GLsizei len;
274                 glGetShaderInfoLog(shader, 1000, &len, log);
275                 HMI_ERROR(log_prefix,"Error: compiling %s: %*s",
276                         shader_type == GL_VERTEX_SHADER ? "vertex" : "fragment",
277                         len, log);
278                 exit(1);
279         }
280
281         return shader;
282 }
283
284 static void
285 init_gl(struct window *window)
286 {
287         GLuint frag, vert;
288         GLuint program;
289         GLint status;
290
291         frag = create_shader(window, frag_shader_text, GL_FRAGMENT_SHADER);
292         vert = create_shader(window, vert_shader_text, GL_VERTEX_SHADER);
293
294         program = glCreateProgram();
295         glAttachShader(program, frag);
296         glAttachShader(program, vert);
297         glLinkProgram(program);
298
299         glGetProgramiv(program, GL_LINK_STATUS, &status);
300         if (!status) {
301                 char log[1000];
302                 GLsizei len;
303                 glGetProgramInfoLog(program, 1000, &len, log);
304                 HMI_ERROR(log_prefix,"Error: linking:%*s", len, log);
305                 exit(1);
306         }
307
308         glUseProgram(program);
309
310         window->gl.pos = 0;
311         window->gl.col = 1;
312
313         glBindAttribLocation(program, window->gl.pos, "pos");
314         glBindAttribLocation(program, window->gl.col, "color");
315         glLinkProgram(program);
316
317         window->gl.rotation_uniform =
318                 glGetUniformLocation(program, "rotation");
319 }
320
321 static void
322 create_ivi_surface(struct window *window, struct display *display)
323 {
324         uint32_t id_ivisurf = g_id_ivisurf;
325         window->ivi_surface =
326                 ivi_application_surface_create(display->ivi_application,
327                                                id_ivisurf, window->surface);
328
329         if (window->ivi_surface == NULL) {
330                 HMI_ERROR(log_prefix,"Failed to create ivi_client_surface");
331                 abort();
332         }
333
334 }
335
336 static void
337 create_surface(struct window *window)
338 {
339         struct display *display = window->display;
340         EGLBoolean ret;
341
342         window->surface = wl_compositor_create_surface(display->compositor);
343
344         window->native =
345                 wl_egl_window_create(window->surface,
346                                      window->geometry.width,
347                                      window->geometry.height);
348         window->egl_surface =
349                 weston_platform_create_egl_surface(display->egl.dpy,
350                                                    display->egl.conf,
351                                                    window->native, NULL);
352
353
354         if (display->ivi_application ) {
355                 create_ivi_surface(window, display);
356         } else {
357                 assert(0);
358         }
359
360         ret = eglMakeCurrent(window->display->egl.dpy, window->egl_surface,
361                              window->egl_surface, window->display->egl.ctx);
362         assert(ret == EGL_TRUE);
363
364         if (!window->frame_sync)
365                 eglSwapInterval(display->egl.dpy, 0);
366
367 }
368
369 static void
370 destroy_surface(struct window *window)
371 {
372         /* Required, otherwise segfault in egl_dri2.c: dri2_make_current()
373          * on eglReleaseThread(). */
374         eglMakeCurrent(window->display->egl.dpy, EGL_NO_SURFACE, EGL_NO_SURFACE,
375                        EGL_NO_CONTEXT);
376
377         eglDestroySurface(window->display->egl.dpy, window->egl_surface);
378         wl_egl_window_destroy(window->native);
379
380         if (window->display->ivi_application)
381                 ivi_surface_destroy(window->ivi_surface);
382         wl_surface_destroy(window->surface);
383
384         if (window->callback)
385                 wl_callback_destroy(window->callback);
386 }
387
388 static void
389 redraw(void *data, struct wl_callback *callback, uint32_t time)
390 {
391         struct window *window = data;
392         struct display *display = window->display;
393         static const GLfloat verts[3][2] = {
394                 { -0.5, -0.5 },
395                 {  0.5, -0.5 },
396                 {  0,    0.5 }
397         };
398
399         static const GLfloat colors[3][3] = {
400                 { 1, 0, 0 },
401                 { 0, 1, 0 },
402                 { 0, 0, 1 }
403         };
404
405         GLfloat angle;
406         GLfloat rotation[4][4] = {
407                 { 1, 0, 0, 0 },
408                 { 0, 1, 0, 0 },
409                 { 0, 0, 1, 0 },
410                 { 0, 0, 0, 1 }
411         };
412         static const uint32_t speed_div = 5, benchmark_interval = 5;
413         struct wl_region *region;
414         EGLint rect[4];
415         EGLint buffer_age = 0;
416         struct timeval tv;
417
418         assert(window->callback == callback);
419         window->callback = NULL;
420
421         if (callback)
422                 wl_callback_destroy(callback);
423
424         gettimeofday(&tv, NULL);
425         time = tv.tv_sec * 1000 + tv.tv_usec / 1000;
426         if (window->frames == 0)
427                 window->benchmark_time = time;
428
429         if (time - window->benchmark_time > (benchmark_interval * 1000)) {
430                 HMI_DEBUG(log_prefix,"%d frames in %d seconds: %f fps",
431                        window->frames,
432                        benchmark_interval,
433                        (float) window->frames / benchmark_interval);
434                 window->benchmark_time = time;
435                 window->frames = 0;
436         }
437
438         angle = (time / speed_div) % 360 * M_PI / 180.0;
439         rotation[0][0] =  cos(angle);
440         rotation[0][2] =  sin(angle);
441         rotation[2][0] = -sin(angle);
442         rotation[2][2] =  cos(angle);
443
444         if (display->swap_buffers_with_damage)
445                 eglQuerySurface(display->egl.dpy, window->egl_surface,
446                                 EGL_BUFFER_AGE_EXT, &buffer_age);
447
448         glViewport(0, 0, window->geometry.width, window->geometry.height);
449
450         glUniformMatrix4fv(window->gl.rotation_uniform, 1, GL_FALSE,
451                            (GLfloat *) rotation);
452
453         glClearColor(0.0, 0.0, 0.0, 0.5);
454         glClear(GL_COLOR_BUFFER_BIT);
455
456         glVertexAttribPointer(window->gl.pos, 2, GL_FLOAT, GL_FALSE, 0, verts);
457         glVertexAttribPointer(window->gl.col, 3, GL_FLOAT, GL_FALSE, 0, colors);
458         glEnableVertexAttribArray(window->gl.pos);
459         glEnableVertexAttribArray(window->gl.col);
460
461         glDrawArrays(GL_TRIANGLES, 0, 3);
462
463         glDisableVertexAttribArray(window->gl.pos);
464         glDisableVertexAttribArray(window->gl.col);
465
466         if (window->opaque || window->fullscreen) {
467                 region = wl_compositor_create_region(window->display->compositor);
468                 wl_region_add(region, 0, 0,
469                               window->geometry.width,
470                               window->geometry.height);
471                 wl_surface_set_opaque_region(window->surface, region);
472                 wl_region_destroy(region);
473         } else {
474                 wl_surface_set_opaque_region(window->surface, NULL);
475         }
476
477         if (display->swap_buffers_with_damage && buffer_age > 0) {
478                 rect[0] = window->geometry.width / 4 - 1;
479                 rect[1] = window->geometry.height / 4 - 1;
480                 rect[2] = window->geometry.width / 2 + 2;
481                 rect[3] = window->geometry.height / 2 + 2;
482                 display->swap_buffers_with_damage(display->egl.dpy,
483                                                   window->egl_surface,
484                                                   rect, 1);
485         } else {
486                 eglSwapBuffers(display->egl.dpy, window->egl_surface);
487         }
488
489         window->frames++;
490 }
491
492 static void
493 registry_handle_global(void *data, struct wl_registry *registry,
494                        uint32_t name, const char *interface, uint32_t version)
495 {
496         struct display *d = data;
497
498         if (strcmp(interface, "wl_compositor") == 0) {
499                 d->compositor =
500                         wl_registry_bind(registry, name,
501                                          &wl_compositor_interface, 1);
502         } else if (strcmp(interface, "ivi_application") == 0) {
503                 d->ivi_application =
504                         wl_registry_bind(registry, name,
505                                          &ivi_application_interface, 1);
506         }
507 }
508
509 static void
510 registry_handle_global_remove(void *data, struct wl_registry *registry,
511                               uint32_t name)
512 {
513 }
514
515 static const struct wl_registry_listener registry_listener = {
516         registry_handle_global,
517         registry_handle_global_remove
518 };
519
520 static void
521 signal_int(int signum)
522 {
523         running = 0;
524 }
525
526 int
527 init_wm(LibWindowmanager *wm, struct window *window)
528 {
529         HMI_DEBUG(log_prefix,"called");
530
531         if (wm->init(port, token) != 0) {
532                 HMI_ERROR(log_prefix,"wm init failed. ");
533                 return -1;
534         }
535
536         g_id_ivisurf = wm->requestSurface(main_role);
537         if (g_id_ivisurf < 0) {
538                 HMI_ERROR(log_prefix,"wm request surface failed ");
539                 return -1;
540         }
541         HMI_DEBUG(log_prefix,"IVI_SURFACE_ID: %d ", g_id_ivisurf);
542
543         WMHandler wmh;
544         wmh.on_visible = [](const char* role, bool visible){
545                 // Sample code if user uses visible event
546                 HMI_DEBUG(log_prefix, "role: %s, visible: %s", role, visible ? "true" : "false");
547         };
548         wmh.on_sync_draw = [wm, window](const char* role, const char* area, Rect rect) {
549
550                 HMI_DEBUG(log_prefix,"Surface %s got syncDraw! Area: %s. w:%d, h:%d", role, area, rect.width(), rect.height());
551
552                 wl_egl_window_resize(window->native, rect.width(), rect.height(), 0, 0);
553                 window->geometry.width  = rect.width();
554                 window->geometry.height = rect.height();
555
556                 wm->endDraw(role);
557         };
558
559         wm->setEventHandler(wmh);
560
561         return 0;
562 }
563
564 int
565 init_hs(LibHomeScreen* hs){
566         if(hs->init(port, token)!=0)
567         {
568                 HMI_ERROR(log_prefix,"homescreen init failed. ");
569                 return -1;
570         }
571
572         hs->set_event_handler(LibHomeScreen::Event_TapShortcut, [](json_object *object){
573                 const char *application_name = json_object_get_string(
574                         json_object_object_get(object, "application_name"));
575                 HMI_DEBUG("simple-egl","Event_TapShortcut application_name = %s ", application_name);
576                 if(strcmp(application_name, app_name.c_str()) == 0)
577                 {
578                         HMI_DEBUG("simple-egl","try to activesurface %s ", app_name.c_str());
579                         wm->activateWindow(main_role);
580                 }
581         });
582
583         return 0;
584 }
585
586 int
587 main(int argc, char **argv)
588 {
589         struct sigaction sigint;
590         struct window  window  = { 0 };
591         struct display display = { 0 };
592
593         window.display = &display;
594         display.window = &window;
595         window.geometry.width  = 1080;
596         window.geometry.height = 1488;
597         window.window_size = window.geometry;
598         window.buffer_size = 32;
599         window.frame_sync = 1;
600
601         if(argc > 2){
602                 port = strtol(argv[1], NULL, 10);
603                 token = argv[2];
604         }
605
606         HMI_DEBUG(log_prefix,"main_role: %s, port: %d, token: %s. ", main_role, port, token.c_str());
607
608         display.display = wl_display_connect(NULL);
609         assert(display.display);
610
611         display.registry = wl_display_get_registry(display.display);
612         wl_registry_add_listener(display.registry,
613                                  &registry_listener, &display);
614
615         wl_display_roundtrip(display.display);
616
617         init_egl(&display, &window);
618
619         wm = new LibWindowmanager();
620         if(init_wm(wm, &window)!=0){
621                 fini_egl(&display);
622                 if (display.ivi_application)
623                         ivi_application_destroy(display.ivi_application);
624                 if (display.compositor)
625                         wl_compositor_destroy(display.compositor);
626                 wl_registry_destroy(display.registry);
627                 wl_display_flush(display.display);
628                 return -1;
629         }
630
631         hs = new LibHomeScreen();
632         if(init_hs(hs)!=0){
633                 fini_egl(&display);
634                 if (display.ivi_application)
635                         ivi_application_destroy(display.ivi_application);
636                 if (display.compositor)
637                         wl_compositor_destroy(display.compositor);
638                 wl_registry_destroy(display.registry);
639                 wl_display_flush(display.display);
640                 return -1;
641         }
642
643         create_surface(&window);
644         init_gl(&window);
645
646         //Ctrl+C
647         sigint.sa_handler = signal_int;
648         sigemptyset(&sigint.sa_mask);
649         sigint.sa_flags = SA_RESETHAND;
650         sigaction(SIGINT, &sigint, NULL);
651
652         eglSwapBuffers(window.display->egl.dpy, window.egl_surface);
653
654         wm->activateWindow(main_role);
655
656         /* The mainloop here is a little subtle.  Redrawing will cause
657          * EGL to read events so we can just call
658          * wl_display_dispatch_pending() to handle any events that got
659          * queued up as a side effect. */
660         while (running) {
661                 wl_display_dispatch_pending(display.display);
662                 redraw(&window, NULL, 0);
663         }
664
665         HMI_DEBUG(log_prefix,"simple-egl exiting! ");
666
667         destroy_surface(&window);
668         fini_egl(&display);
669
670         if (display.ivi_application)
671                 ivi_application_destroy(display.ivi_application);
672
673         if (display.compositor)
674                 wl_compositor_destroy(display.compositor);
675
676         wl_registry_destroy(display.registry);
677         wl_display_flush(display.display);
678         wl_display_disconnect(display.display);
679
680         return 0;
681 }