2 * Copyright © 2019 Samsung Electronics co., Ltd. All Rights Reserved.
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:
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.
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
33 #include <tbm_bufmgr.h>
35 #include "headless_server.h"
39 static void server_handle_new_shell_surface(struct wl_listener *listener,
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);
48 handle_sigint(int signal_number, void *data)
50 struct wl_display *display = (struct wl_display *)data;
52 wl_display_terminate(display);
58 init_signal(struct wl_display *display)
60 struct wl_event_loop *loop;
61 struct wl_event_source *sigint;
63 loop = wl_display_get_event_loop(display);
64 sigint = wl_event_loop_add_signal(loop, SIGINT, handle_sigint, display);
72 dlog_level_from_ds_log_level(enum ds_log_level level)
86 handle_ds_log(enum ds_log_level level, const char *fmt, va_list args)
88 dlog_vprint(dlog_level_from_ds_log_level(level), "HEADLESS_SERVER",
93 main(int argc, char *argv[])
95 headless_server_t server = { 0, };
96 const char *socket_name = NULL;
98 tbm_bufmgr bufmgr = NULL;
100 /* set STDOUT/STDERR bufferless */
101 setvbuf(stdout, NULL, _IONBF, 0);
102 setvbuf(stderr, NULL, _IONBF, 0);
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);
109 socket_name = getenv("WAYLAND_DISPLAY");
112 socket_name = "wayland-0";
114 if (!getenv("XDG_RUNTIME_DIR"))
115 setenv("XDG_RUNTIME_DIR", "/run", 1);
117 if(!getenv("RUN_WITH_SPEAKER"))
120 server.display = wl_display_create();
121 if (!server.display) {
122 ds_err("Could not create wl_display");
126 wl_list_init(&server.views);
128 wl_signal_init(&server.events.focus_view_change);
129 wl_signal_init(&server.events.top_visible_view_change);
131 server.compositor = ds_compositor_create(server.display);
132 HS_CHECK(server.compositor, goto end, "Failed to create ds_compositor");
134 server.tbm_server = ds_tbm_server_create(server.display);
135 HS_CHECK(server.tbm_server, goto end, "Failed to create ds_tbm_server");
137 /* Init event trace */
138 server.debug = headless_debug_create(&server);
139 HS_CHECK(server.debug, goto end, "headless_debug_create() failed\n");
141 /* Init input for headless */
142 server.input = headless_input_create(&server);
143 HS_CHECK(server.input, goto end, "headless_input_create() failed\n");
148 server.output = headless_output_create(server.display);
149 HS_CHECK(server.output, goto end, "headless_output_create() failed.");
151 headless_output_start_boot_ani(server.output);
152 server.boot_animating = true;
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");
163 server.shell = headless_shell_create(&server);
164 HS_CHECK(server.shell, goto end, "headless_shell_create() failed.\n");
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);
170 /* Init Signal for SIGINT */
171 init_signal(server.display);
173 server.name = wl_display_add_socket_auto(server.display);
174 HS_CHECK(server.name, goto end, "Could not add socket for wayland");
176 setenv("WAYLAND_DISPLAY", server.name, true);
178 ds_inf("Running headless server on WAYLAND_DISPLAY=%s", server.name);
181 wl_display_run(server.display);
186 headless_shell_destroy(server.shell);
188 if(use_output && server.output)
189 headless_output_destroy(server.output);
192 tbm_bufmgr_deinit(bufmgr);
197 headless_debug_destroy(server.debug);
200 headless_input_destroy(server.input);
202 wl_display_destroy_clients(server.display);
203 wl_display_destroy(server.display);
209 destroy_view(headless_view_t *view)
211 headless_server_t *server = view->server;
213 ds_inf("view(%p) destroyed", view);
215 if (server->top_visible_view == view)
216 server_set_top_visible_view(server, NULL);
218 server_schedule_idle_task(server);
220 wl_signal_emit(&view->events.destroy, view);
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);
234 view_handle_shell_surface_destroy(struct wl_listener *listener, void *data)
236 headless_view_t *view;
238 view = wl_container_of(listener, view, shell_surface_destroy);
244 view_handle_surface_commit(struct wl_listener *listener, void *data)
246 headless_view_t *view;
248 view = wl_container_of(listener, view, commit);
249 view->buffer = ds_surface_get_buffer(view->surface);
251 // FIXME use the size of surface, not size of buffer
253 ds_buffer_get_size(view->buffer, &view->width, &view->height);
255 server_schedule_idle_task(view->server);
259 view_handle_shell_surface_map(struct wl_listener *listener, void *data)
261 headless_view_t *view;
263 view = wl_container_of(listener, view, map);
266 server_schedule_idle_task(view->server);
270 view_handle_shell_surface_unmap(struct wl_listener *listener, void *data)
272 headless_view_t *view;
274 view = wl_container_of(listener, view, unmap);
275 view->mapped = false;
277 server_schedule_idle_task(view->server);
281 view_handle_request_activate(struct wl_listener *listener, void *data)
283 headless_view_t *view;
285 view = wl_container_of(listener, view, request_activate);
287 wl_list_remove(&view->link);
288 wl_list_insert(&view->server->views, &view->link);
290 server_schedule_idle_task(view->server);
294 view_handle_shell_surface_set_skip_focus(struct wl_listener *listener,
297 headless_view_t *view;
299 view = wl_container_of(listener, view, set_skip_focus);
300 view->skip_focus = true;
302 server_schedule_idle_task(view->server);
306 view_handle_shell_surface_unset_skip_focus(struct wl_listener *listener,
309 headless_view_t *view;
311 view = wl_container_of(listener, view, unset_skip_focus);
312 view->skip_focus = false;
314 server_schedule_idle_task(view->server);
318 create_view(headless_server_t *server, headless_shell_surface_t *shell_surface)
320 headless_view_t *view;
322 view = calloc(1, sizeof *view);
326 view->server = server;
327 view->shell_surface = shell_surface;
328 view->surface = headless_shell_surface_get_surface(shell_surface);
330 wl_signal_init(&view->events.destroy);
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);
336 view->commit.notify = view_handle_surface_commit;
337 ds_surface_add_commit_listener(view->surface, &view->commit);
339 view->map.notify = view_handle_shell_surface_map;
340 headless_shell_surface_add_map_listener(shell_surface, &view->map);
342 view->unmap.notify = view_handle_shell_surface_unmap;
343 headless_shell_surface_add_unmap_listener(shell_surface, &view->unmap);
345 view->request_activate.notify = view_handle_request_activate;
346 headless_shell_surface_add_request_activate_listener(shell_surface,
347 &view->request_activate);
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);
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);
357 wl_list_insert(&server->views, &view->link);
359 ds_inf("Create view(%p)", view);
365 server_handle_new_shell_surface(struct wl_listener *listener, void *data)
367 headless_server_t *server;
368 headless_shell_surface_t *shell_surface = data;
370 server = wl_container_of(listener, server, new_shell_surface);
372 ds_inf("shell surface added, server(%p)", server);
374 if (server->boot_animating) {
375 server->boot_animating = false;
376 headless_output_stop_boot_ani(server->output);
379 HS_CHECK(create_view(server, shell_surface), return,
380 "Could not create headless_view");
384 server_set_focus_view(headless_server_t *server, headless_view_t *view)
386 if (server->focus_view == view)
389 server->focus_view = view;
391 wl_signal_emit(&server->events.focus_view_change, view);
395 server_set_top_visible_view(headless_server_t *server, headless_view_t *view)
397 if (server->top_visible_view == view)
400 // TODO handle input and debug
402 server->top_visible_view = view;
404 wl_signal_emit(&server->events.top_visible_view_change, view);
408 server_repaint(headless_server_t *server)
410 struct ds_surface *surface = NULL;
411 struct ds_buffer *buffer = NULL;
418 if (server->top_visible_view) {
419 surface = server->top_visible_view->surface;
420 buffer = server->top_visible_view->buffer;
424 access = ds_buffer_begin_data_ptr_access(buffer,
425 DS_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride);
427 ds_err("Could not access ds_buffer(%p)", buffer);
430 headless_output_update_led(server->output, data);
433 ds_buffer_end_data_ptr_access(buffer);
436 clock_gettime(CLOCK_MONOTONIC, &now);
437 ds_surface_send_frame_done(surface, &now);
442 idle_task(void *data)
444 headless_server_t *server = data;
445 headless_view_t *view, *focus_view = NULL, *top_visible_view = NULL;
447 server->idle_source = NULL;
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) {
456 if (!top_visible_view)
457 top_visible_view = view;
459 if (!focus_view && !view->skip_focus)
462 if (focus_view && top_visible_view)
466 server_set_focus_view(server, focus_view);
467 server_set_top_visible_view(server, top_visible_view);
468 server_repaint(server);
472 server_schedule_idle_task(headless_server_t *server)
474 if (server->idle_source)
477 server->idle_source =
478 wl_event_loop_add_idle(wl_display_get_event_loop(server->display),