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, void *data);
42 handle_sigint(int signal_number, void *data)
44 struct wl_display *display = (struct wl_display *)data;
45 wl_display_terminate(display);
51 init_signal(struct wl_display *display)
53 struct wl_event_loop *loop;
54 struct wl_event_source *sigint;
56 loop = wl_display_get_event_loop(display);
57 sigint = wl_event_loop_add_signal(loop, SIGINT, handle_sigint, display);
65 dlog_level_from_ds_log_level(enum ds_log_level level)
79 handle_ds_log(enum ds_log_level level, const char *fmt, va_list args)
81 dlog_vprint(dlog_level_from_ds_log_level(level), "HEADLESS_SERVER",
85 int main(int argc, char *argv[])
87 headless_server_t server = { 0, };
88 const char *socket_name = NULL;
90 tbm_bufmgr bufmgr = NULL;
92 /* set STDOUT/STDERR bufferless */
93 setvbuf(stdout, NULL, _IONBF, 0);
94 setvbuf(stderr, NULL, _IONBF, 0);
96 if (getenv("HEADLESS_SERVER_DLOG_ENABLE")) {
97 HEADLESS_TRACE("ds log will be written to dlog !\n");
98 ds_log_init(DS_INF, handle_ds_log);
101 socket_name = getenv("WAYLAND_DISPLAY");
104 socket_name = "wayland-0";
106 if (!getenv("XDG_RUNTIME_DIR"))
107 setenv("XDG_RUNTIME_DIR", "/run", 1);
109 if(!getenv("RUN_WITH_SPEAKER"))
114 server.display = wl_display_create();
115 if (!server.display) {
116 ds_err("Could not create wl_display");
120 wl_list_init(&server.views);
122 wl_signal_init(&server.events.focus_change);
123 wl_signal_init(&server.events.top_change);
125 server.compositor = ds_compositor_create(server.display);
126 HEADLESS_CHECK(server.compositor, goto end, "Failed to create ds_compositor");
128 server.tbm_server = ds_tbm_server_create(server.display);
129 HEADLESS_CHECK(server.tbm_server, goto end, "Failed to create ds_tbm_server");
131 /* Init event trace */
132 server.debug = headless_debug_create(&server);
133 HEADLESS_CHECK(server.debug, goto end, "headless_debug_create() failed\n");
135 /* Init input for headless */
136 server.input = headless_input_create(&server);
137 HEADLESS_CHECK(server.input, goto end, "headless_input_create() failed\n");
142 server.output = headless_output_create(server.display);
143 HEADLESS_CHECK(server.output, goto end, "headless_output_create() failed.");
145 headless_output_start_boot_ani(server.output);
146 server.boot_animating = true;
149 bufmgr = tbm_bufmgr_server_init();
150 HEADLESS_CHECK(bufmgr, goto end, "Failed to init tbm buffer manager !\n");
151 ret = tbm_bufmgr_bind_native_display(bufmgr, (void *)server.display);
152 HEADLESS_CHECK(ret, goto end, "Failed to bind native display with tbm buffer manager !\n");
156 server.shell = headless_shell_create(server.display);
157 HEADLESS_CHECK(server.shell, goto end, "headless_shell_create() failed.\n");
159 server.new_shell_surface.notify = server_handle_new_shell_surface;
160 headless_shell_add_new_surface_listener(server.shell, &server.new_shell_surface);
162 /* Init Signal for SIGINT */
163 init_signal(server.display);
165 server.name = wl_display_add_socket_auto(server.display);
166 HEADLESS_CHECK(server.name, goto end, "Could not add socket for wayland");
168 setenv("WAYLAND_DISPLAY", server.name, true);
170 ds_inf("Running headless server on WAYLAND_DISPLAY=%s", server.name);
173 wl_display_run(server.display);
178 headless_shell_destroy(server.shell);
180 if(use_output && server.output)
181 headless_output_destroy(server.output);
184 tbm_bufmgr_deinit(bufmgr);
189 headless_debug_destroy(server.debug);
192 headless_input_destroy(server.input);
194 wl_display_destroy_clients(server.display);
195 wl_display_destroy(server.display);
201 server_schedule_idle_task(headless_server_t *server);
203 server_set_focus_view(headless_server_t *server, headless_view_t *view);
205 server_set_top_view(headless_server_t *server, headless_view_t *view);
208 destroy_view(headless_view_t *view)
210 ds_inf("view(%p) destroyed", view);
212 wl_signal_emit(&view->events.destroy, view);
214 wl_list_remove(&view->commit.link);
215 wl_list_remove(&view->map.link);
216 wl_list_remove(&view->unmap.link);
217 wl_list_remove(&view->request_activate.link);
218 wl_list_remove(&view->set_skip_focus.link);
219 wl_list_remove(&view->unset_skip_focus.link);
220 wl_list_remove(&view->link);
226 view_handle_shell_surface_destroy(struct wl_listener *listener, void *data)
228 headless_view_t *view;
229 headless_server_t *server;
231 view = wl_container_of(listener, view, shell_surface_destroy);
232 server = view->server;
234 if (server->top_view == view) {
235 server->top_view = NULL;
236 wl_signal_emit(&server->events.top_change, NULL);
239 server_schedule_idle_task(server);
245 view_handle_surface_commit(struct wl_listener *listener, void *data)
247 headless_view_t *view;
249 view = wl_container_of(listener, view, commit);
250 view->buffer = ds_surface_get_buffer(view->surface);
252 // FIXME use the size of surface, not size of buffer
254 ds_buffer_get_size(view->buffer, &view->width, &view->height);
256 server_schedule_idle_task(view->server);
260 view_handle_shell_surface_map(struct wl_listener *listener, void *data)
262 headless_view_t *view;
264 view = wl_container_of(listener, view, map);
267 server_schedule_idle_task(view->server);
271 view_handle_shell_surface_unmap(struct wl_listener *listener, void *data)
273 headless_view_t *view;
275 view = wl_container_of(listener, view, unmap);
276 view->mapped = false;
278 server_schedule_idle_task(view->server);
282 view_handle_request_activate(struct wl_listener *listener, void *data)
284 headless_view_t *view;
286 view = wl_container_of(listener, view, request_activate);
288 wl_list_remove(&view->link);
289 wl_list_insert(&view->server->views, &view->link);
291 server_schedule_idle_task(view->server);
295 view_handle_shell_surface_set_skip_focus(struct wl_listener *listener,
298 headless_view_t *view;
300 view = wl_container_of(listener, view, set_skip_focus);
301 view->skip_focus = true;
303 server_schedule_idle_task(view->server);
307 view_handle_shell_surface_unset_skip_focus(struct wl_listener *listener,
310 headless_view_t *view;
312 view = wl_container_of(listener, view, unset_skip_focus);
313 view->skip_focus = false;
315 server_schedule_idle_task(view->server);
319 create_view(headless_server_t *server, headless_shell_surface_t *shell_surface)
321 headless_view_t *view;
323 view = calloc(1, sizeof *view);
327 view->server = server;
328 view->shell_surface = shell_surface;
329 view->surface = headless_shell_surface_get_surface(shell_surface);
331 wl_signal_init(&view->events.destroy);
333 view->shell_surface_destroy.notify = view_handle_shell_surface_destroy;
334 headless_shell_surface_add_destroy_listener(shell_surface,
335 &view->shell_surface_destroy);
337 view->commit.notify = view_handle_surface_commit;
338 ds_surface_add_commit_listener(view->surface, &view->commit);
340 view->map.notify = view_handle_shell_surface_map;
341 headless_shell_surface_add_map_listener(shell_surface, &view->map);
343 view->unmap.notify = view_handle_shell_surface_unmap;
344 headless_shell_surface_add_unmap_listener(shell_surface, &view->unmap);
346 view->request_activate.notify = view_handle_request_activate;
347 headless_shell_surface_add_request_activate_listener(shell_surface,
348 &view->request_activate);
350 view->set_skip_focus.notify = view_handle_shell_surface_set_skip_focus;
351 headless_shell_surface_add_set_skip_focus_listener(shell_surface,
352 &view->set_skip_focus);
354 view->unset_skip_focus.notify = view_handle_shell_surface_unset_skip_focus;
355 headless_shell_surface_add_unset_skip_focus_listener(shell_surface,
356 &view->unset_skip_focus);
358 wl_list_insert(&server->views, &view->link);
360 ds_inf("Create view(%p)", view);
366 server_handle_new_shell_surface(struct wl_listener *listener, void *data)
368 headless_server_t *server;
369 headless_shell_surface_t *shell_surface = data;
371 server = wl_container_of(listener, server, new_shell_surface);
373 ds_inf("shell surface added, server(%p)", server);
375 if (server->boot_animating) {
376 server->boot_animating = false;
377 headless_output_stop_boot_ani(server->output);
380 HEADLESS_CHECK(create_view(server, shell_surface), return,
381 "Could not create headless_view");
385 server_set_focus_view(headless_server_t *server, headless_view_t *view)
387 if (server->focus_view == view)
390 server->focus_view = view;
392 wl_signal_emit(&server->events.focus_change, view);
396 server_set_top_view(headless_server_t *server, headless_view_t *view)
398 if (server->top_view == view)
401 // TODO handle input and debug
403 if (server->top_view) {
404 headless_shell_send_visibility(server->top_view->shell_surface,
405 TIZEN_VISIBILITY_VISIBILITY_FULLY_OBSCURED);
409 headless_shell_send_visibility(view->shell_surface,
410 TIZEN_VISIBILITY_VISIBILITY_UNOBSCURED);
413 server->top_view = view;
415 wl_signal_emit(&server->events.top_change, view);
419 server_repaint(headless_server_t *server)
421 struct ds_surface *surface = NULL;
422 struct ds_buffer *buffer = NULL;
429 if (server->top_view) {
430 surface = server->top_view->surface;
431 buffer = server->top_view->buffer;
435 access = ds_buffer_begin_data_ptr_access(buffer,
436 DS_BUFFER_DATA_PTR_ACCESS_READ, &data, &format, &stride);
438 ds_err("Could not access ds_buffer(%p)", buffer);
441 headless_output_update_led(server->output, data);
444 ds_buffer_end_data_ptr_access(buffer);
447 clock_gettime(CLOCK_MONOTONIC, &now);
448 ds_surface_send_frame_done(surface, &now);
453 idle_task(void *data)
455 headless_server_t *server = data;
456 headless_view_t *view, *focus_view = NULL, *top_view = NULL;
458 server->idle_source = NULL;
460 /* This iterates over all our views and attemps to find focusable and
461 * visible views on the top. This relies on server->views being ordered
462 * from top-to-bottom. */
463 wl_list_for_each(view, &server->views, link) {
470 if (!focus_view && !view->skip_focus)
473 if (focus_view && top_view)
477 server_set_focus_view(server, focus_view);
478 server_set_top_view(server, top_view);
479 server_repaint(server);
483 server_schedule_idle_task(headless_server_t *server)
485 if (server->idle_source)
488 server->idle_source =
489 wl_event_loop_add_idle(wl_display_get_event_loop(server->display),