de35f86412dc709b709d7c29d1e2959c16ab1654
[platform/core/uifw/headless-server.git] / src / headless_server.c
1 /*
2  * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the
13  * next paragraph) shall be included in all copies or substantial
14  * portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19  * NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20  * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21  * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23  * SOFTWARE.
24  */
25
26 #include <stdbool.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <signal.h>
30 #include <time.h>
31
32 #include <dlog.h>
33 #include <tbm_bufmgr.h>
34
35 #include "headless_server.h"
36
37 int use_output = 1;
38
39 static void server_handle_new_shell_surface(struct wl_listener *listener,
40                 void *data);
41 static void server_schedule_idle_task(headless_server_t *server);
42 static void server_set_focus_view(headless_server_t *server,
43                 headless_view_t *view);
44 static void server_set_top_visible_view(headless_server_t *server,
45                 headless_view_t *view);
46
47 static int
48 handle_sigint(int signal_number, void *data)
49 {
50         struct wl_display *display = (struct wl_display *)data;
51
52         wl_display_terminate(display);
53
54         return 0;
55 }
56
57 static bool
58 init_signal(struct wl_display *display)
59 {
60         struct wl_event_loop *loop;
61         struct wl_event_source *sigint;
62
63         loop = wl_display_get_event_loop(display);
64         sigint = wl_event_loop_add_signal(loop, SIGINT, handle_sigint, display);
65         if (!sigint)
66                 return false;
67
68         return true;
69 }
70
71 static int
72 dlog_level_from_ds_log_level(enum ds_log_level level)
73 {
74         switch (level) {
75                 case DS_INF:
76                         return DLOG_INFO;
77                 case DS_DBG:
78                         return DLOG_DEBUG;
79                 default:
80                 case DS_ERR:
81                         return DLOG_ERROR;
82         }
83 }
84
85 static void
86 handle_ds_log(enum ds_log_level level, const char *fmt, va_list args)
87 {
88         dlog_vprint(dlog_level_from_ds_log_level(level), "HEADLESS_SERVER",
89                         fmt, args);
90 }
91
92 int
93 main(int argc, char *argv[])
94 {
95         headless_server_t server = { 0, };
96         const char *socket_name = NULL;
97         bool ret;
98         tbm_bufmgr bufmgr = NULL;
99
100         /* set STDOUT/STDERR bufferless */
101         setvbuf(stdout, NULL, _IONBF, 0);
102         setvbuf(stderr, NULL, _IONBF, 0);
103
104         if (getenv("HEADLESS_SERVER_DLOG_ENABLE")) {
105                 HS_TRACE("ds log will be written to dlog !\n");
106                 ds_log_init(DS_INF, handle_ds_log);
107         }
108
109         socket_name = getenv("WAYLAND_DISPLAY");
110
111         if (!socket_name)
112                 socket_name = "wayland-0";
113
114         if (!getenv("XDG_RUNTIME_DIR"))
115                 setenv("XDG_RUNTIME_DIR", "/run", 1);
116
117         if(!getenv("RUN_WITH_SPEAKER"))
118         use_output = 0;
119
120         server.display = wl_display_create();
121         if (!server.display) {
122                 ds_err("Could not create wl_display");
123                 return EXIT_FAILURE;
124         }
125
126         wl_list_init(&server.views);
127
128         wl_signal_init(&server.events.focus_view_change);
129         wl_signal_init(&server.events.top_visible_view_change);
130
131         server.compositor = ds_compositor_create(server.display);
132         HS_CHECK(server.compositor, goto end, "Failed to create ds_compositor");
133
134         server.tbm_server = ds_tbm_server_create(server.display);
135         HS_CHECK(server.tbm_server, goto end, "Failed to create ds_tbm_server");
136
137         /* Init event trace */
138         server.debug = headless_debug_create(&server);
139         HS_CHECK(server.debug, goto end, "headless_debug_create() failed\n");
140
141         /* Init input for headless */
142         server.input = headless_input_create(&server);
143         HS_CHECK(server.input, goto end, "headless_input_create() failed\n");
144
145         /* Init Output */
146         if (use_output)
147         {
148                 server.output = headless_output_create(server.display);
149                 HS_CHECK(server.output, goto end, "headless_output_create() failed.");
150
151                 headless_output_start_boot_ani(server.output);
152                 server.boot_animating = true;
153         }
154         else {
155                 bufmgr = tbm_bufmgr_server_init();
156                 HS_CHECK(bufmgr, goto end, "Failed to init tbm buffer manager !\n");
157                 ret = tbm_bufmgr_bind_native_display(bufmgr, (void *)server.display);
158                 HS_CHECK(ret, goto end,
159                 "Failed to bind native display with tbm buffer manager !\n");
160         }
161
162         /* Init Shell */
163         server.shell = headless_shell_create(&server);
164         HS_CHECK(server.shell, goto end, "headless_shell_create() failed.\n");
165
166         server.new_shell_surface.notify = server_handle_new_shell_surface;
167         headless_shell_add_new_surface_listener(server.shell,
168             &server.new_shell_surface);
169
170         /* Init Signal for SIGINT */
171         init_signal(server.display);
172
173         server.name = wl_display_add_socket_auto(server.display);
174         HS_CHECK(server.name, goto end, "Could not add socket for wayland");
175
176         setenv("WAYLAND_DISPLAY", server.name, true);
177
178         ds_inf("Running headless server on WAYLAND_DISPLAY=%s", server.name);
179
180         /* run event loop */
181         wl_display_run(server.display);
182
183 end:
184         /* Deinit Process */
185         if (server.shell)
186                 headless_shell_destroy(server.shell);
187
188         if(use_output && server.output)
189                 headless_output_destroy(server.output);
190
191         if (bufmgr) {
192                 tbm_bufmgr_deinit(bufmgr);
193                 bufmgr = NULL;
194         }
195
196         if (server.debug)
197                 headless_debug_destroy(server.debug);
198
199         if (server.input)
200                 headless_input_destroy(server.input);
201
202         wl_display_destroy_clients(server.display);
203         wl_display_destroy(server.display);
204
205         return EXIT_SUCCESS;
206 }
207
208 static void
209 destroy_view(headless_view_t *view)
210 {
211         headless_server_t *server = view->server;
212
213         ds_inf("view(%p) destroyed", view);
214
215         if (server->top_visible_view == view)
216                 server_set_top_visible_view(server, NULL);
217
218         server_schedule_idle_task(server);
219
220         wl_signal_emit(&view->events.destroy, view);
221
222         wl_list_remove(&view->commit.link);
223         wl_list_remove(&view->map.link);
224         wl_list_remove(&view->unmap.link);
225         wl_list_remove(&view->request_activate.link);
226         wl_list_remove(&view->set_skip_focus.link);
227         wl_list_remove(&view->unset_skip_focus.link);
228         wl_list_remove(&view->link);
229
230         free(view);
231 }
232
233 static void
234 view_handle_shell_surface_destroy(struct wl_listener *listener, void *data)
235 {
236         headless_view_t *view;
237
238         view = wl_container_of(listener, view, shell_surface_destroy);
239
240         destroy_view(view);
241 }
242
243 static void
244 view_handle_surface_commit(struct wl_listener *listener, void *data)
245 {
246         headless_view_t *view;
247
248         view = wl_container_of(listener, view, commit);
249     view->buffer = ds_surface_get_buffer(view->surface);
250
251         // FIXME use the size of surface, not size of buffer
252         if (view->buffer)
253                 ds_buffer_get_size(view->buffer, &view->width, &view->height);
254
255         server_schedule_idle_task(view->server);
256 }
257
258 static void
259 view_handle_shell_surface_map(struct wl_listener *listener, void *data)
260 {
261         headless_view_t *view;
262
263         view = wl_container_of(listener, view, map);
264         view->mapped = true;
265
266         server_schedule_idle_task(view->server);
267 }
268
269 static void
270 view_handle_shell_surface_unmap(struct wl_listener *listener, void *data)
271 {
272         headless_view_t *view;
273
274         view = wl_container_of(listener, view, unmap);
275         view->mapped = false;
276
277         server_schedule_idle_task(view->server);
278 }
279
280 static void
281 view_handle_request_activate(struct wl_listener *listener, void *data)
282 {
283         headless_view_t *view;
284
285         view = wl_container_of(listener, view, request_activate);
286
287         wl_list_remove(&view->link);
288         wl_list_insert(&view->server->views, &view->link);
289
290         server_schedule_idle_task(view->server);
291 }
292
293 static void
294 view_handle_shell_surface_set_skip_focus(struct wl_listener *listener,
295                 void *data)
296 {
297         headless_view_t *view;
298
299         view = wl_container_of(listener, view, set_skip_focus);
300         view->skip_focus = true;
301
302         server_schedule_idle_task(view->server);
303 }
304
305 static void
306 view_handle_shell_surface_unset_skip_focus(struct wl_listener *listener,
307                 void *data)
308 {
309         headless_view_t *view;
310
311         view = wl_container_of(listener, view, unset_skip_focus);
312         view->skip_focus = false;
313
314         server_schedule_idle_task(view->server);
315 }
316
317 static bool
318 create_view(headless_server_t *server, headless_shell_surface_t *shell_surface)
319 {
320         headless_view_t *view;
321
322         view = calloc(1, sizeof *view);
323         if (!view)
324                 return false;
325
326         view->server = server;
327         view->shell_surface = shell_surface;
328         view->surface = headless_shell_surface_get_surface(shell_surface);
329
330         wl_signal_init(&view->events.destroy);
331
332         view->shell_surface_destroy.notify = view_handle_shell_surface_destroy;
333         headless_shell_surface_add_destroy_listener(shell_surface,
334                         &view->shell_surface_destroy);
335
336         view->commit.notify = view_handle_surface_commit;
337         ds_surface_add_commit_listener(view->surface, &view->commit);
338
339         view->map.notify = view_handle_shell_surface_map;
340         headless_shell_surface_add_map_listener(shell_surface, &view->map);
341
342         view->unmap.notify = view_handle_shell_surface_unmap;
343         headless_shell_surface_add_unmap_listener(shell_surface, &view->unmap);
344
345         view->request_activate.notify = view_handle_request_activate;
346         headless_shell_surface_add_request_activate_listener(shell_surface,
347                         &view->request_activate);
348
349         view->set_skip_focus.notify = view_handle_shell_surface_set_skip_focus;
350         headless_shell_surface_add_set_skip_focus_listener(shell_surface,
351                         &view->set_skip_focus);
352
353         view->unset_skip_focus.notify = view_handle_shell_surface_unset_skip_focus;
354         headless_shell_surface_add_unset_skip_focus_listener(shell_surface,
355                         &view->unset_skip_focus);
356
357         wl_list_insert(&server->views, &view->link);
358
359         ds_inf("Create view(%p)", view);
360
361         return true;
362 }
363
364 static void
365 server_handle_new_shell_surface(struct wl_listener *listener, void *data)
366 {
367         headless_server_t *server;
368         headless_shell_surface_t *shell_surface = data;
369
370         server = wl_container_of(listener, server, new_shell_surface);
371
372         ds_inf("shell surface added, server(%p)", server);
373
374         if (server->boot_animating) {
375                 server->boot_animating = false;
376                 headless_output_stop_boot_ani(server->output);
377         }
378
379         HS_CHECK(create_view(server, shell_surface), return,
380                         "Could not create headless_view");
381 }
382
383 static void
384 server_set_focus_view(headless_server_t *server, headless_view_t *view)
385 {
386         if (server->focus_view == view)
387                 return;
388
389         server->focus_view = view;
390
391         wl_signal_emit(&server->events.focus_view_change, view);
392 }
393
394 static void
395 server_set_top_visible_view(headless_server_t *server, headless_view_t *view)
396 {
397         if (server->top_visible_view == view)
398                 return;
399
400         // TODO handle input and debug
401
402         server->top_visible_view = view;
403
404         wl_signal_emit(&server->events.top_visible_view_change, view);
405 }
406
407 static void
408 server_repaint(headless_server_t *server)
409 {
410     struct ds_surface *surface = NULL;
411         struct ds_buffer *buffer = NULL;
412         struct timespec now;
413         void *data = NULL;
414         uint32_t format;
415         size_t stride;
416         bool access = false;
417
418     if (server->top_visible_view) {
419         surface = server->top_visible_view->surface;
420         buffer = server->top_visible_view->buffer;
421     }
422
423         if (buffer) {
424                 access = ds_buffer_begin_data_ptr_access(buffer,
425                                 DS_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride);
426                 if (!access)
427                         ds_err("Could not access ds_buffer(%p)", buffer);
428         }
429
430         headless_output_update_led(server->output, data);
431
432         if (access)
433                 ds_buffer_end_data_ptr_access(buffer);
434
435     if (surface) {
436                 clock_gettime(CLOCK_MONOTONIC, &now);
437                 ds_surface_send_frame_done(surface, &now);
438         }
439 }
440
441 static void
442 idle_task(void *data)
443 {
444         headless_server_t *server = data;
445         headless_view_t *view, *focus_view = NULL, *top_visible_view = NULL;
446
447         server->idle_source = NULL;
448
449         /* This iterates over all our views and attemps to find focusable and
450      * visible views on the top. This relies on server->views being ordered
451      * from top-to-bottom. */
452         wl_list_for_each(view, &server->views, link) {
453                 if (!view->mapped)
454                         continue;
455
456                 if (!top_visible_view)
457                         top_visible_view = view;
458
459                 if (!focus_view && !view->skip_focus)
460                         focus_view = view;
461
462                 if (focus_view && top_visible_view)
463                         break;
464         }
465
466         server_set_focus_view(server, focus_view);
467         server_set_top_visible_view(server, top_visible_view);
468         server_repaint(server);
469 }
470
471 static void
472 server_schedule_idle_task(headless_server_t *server)
473 {
474         if (server->idle_source)
475                 return;
476
477         server->idle_source =
478                 wl_event_loop_add_idle(wl_display_get_event_loop(server->display),
479                                 idle_task, server);
480 }