/* * Cage: A Wayland kiosk. * * Copyright (C) 2018 Jente Hidskes * * See the LICENSE file accompanying this file. */ #define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include #include #include #include #include "output.h" #include "seat.h" #include "server.h" #include "xdg_shell.h" static bool spawn_primary_client(char *argv[], pid_t *pid_out) { pid_t pid = fork(); if (pid == 0) { execvp(argv[0], argv); _exit(1); } else if (pid == -1) { wlr_log_errno(WLR_ERROR, "Unable to fork"); return false; } *pid_out = pid; wlr_log(WLR_DEBUG, "Child process created with pid %d", pid); return true; } static int handle_signal(int signal, void *data) { struct wl_display *display = data; switch (signal) { case SIGINT: /* Fallthrough */ case SIGTERM: wl_display_terminate(display); return 0; default: return 1; } } int main(int argc, char *argv[]) { struct cg_server server = {0}; struct wl_event_loop *event_loop = NULL; struct wlr_renderer *renderer = NULL; struct wlr_compositor *compositor = NULL; struct wlr_data_device_manager *data_device_mgr = NULL; struct wlr_xdg_shell *xdg_shell = NULL; int ret = 0; if (argc < 2) { printf("Usage: %s APPLICATION\n", argv[0]); return 1; } #ifdef DEBUG wlr_log_init(WLR_DEBUG, NULL); #else wlr_log_init(WLR_ERROR, NULL); #endif server.wl_display = wl_display_create(); if (!server.wl_display) { wlr_log(WLR_ERROR, "Cannot allocate a Wayland display"); return 1; } event_loop = wl_display_get_event_loop(server.wl_display); wl_event_loop_add_signal(event_loop, SIGINT, handle_signal, &server.wl_display); wl_event_loop_add_signal(event_loop, SIGTERM, handle_signal, &server.wl_display); server.backend = wlr_backend_autocreate(server.wl_display, NULL); if (!server.backend) { wlr_log(WLR_ERROR, "Unable to create the wlroots backend"); ret = 1; goto end; } renderer = wlr_backend_get_renderer(server.backend); wlr_renderer_init_wl_display(renderer, server.wl_display); server.output_layout = wlr_output_layout_create(); if (!server.output_layout) { wlr_log(WLR_ERROR, "Unable to create output layout"); ret = 1; goto end; } compositor = wlr_compositor_create(server.wl_display, renderer); if (!compositor) { wlr_log(WLR_ERROR, "Unable to create the wlroots compositor"); ret = 1; goto end; } data_device_mgr = wlr_data_device_manager_create(server.wl_display); if (!data_device_mgr) { wlr_log(WLR_ERROR, "Unable to create the data device manager"); ret = 1; goto end; } /* Configure a listener to be notified when new outputs are * available on the backend. We use this only to detect the * first output and ignore subsequent outputs. */ server.new_output.notify = handle_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); server.seat = cg_seat_create(&server); if (!server.seat) { wlr_log(WLR_ERROR, "Unable to create the seat"); ret = 1; goto end; } xdg_shell = wlr_xdg_shell_create(server.wl_display); if (!xdg_shell) { wlr_log(WLR_ERROR, "Unable to create the XDG shell interface"); ret = 1; goto end; } wl_list_init(&server.views); server.new_xdg_shell_surface.notify = handle_xdg_shell_surface_new; wl_signal_add(&xdg_shell->events.new_surface, &server.new_xdg_shell_surface); const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_log_errno(WLR_ERROR, "Unable to open Wayland socket"); ret = 1; goto end; } if (!wlr_backend_start(server.backend)) { wlr_log(WLR_ERROR, "Unable to start the wlroots backend"); ret = 1; goto end; } int rc = setenv("WAYLAND_DISPLAY", socket, true); if (rc < 0) { wlr_log_errno(WLR_ERROR, "Unable to set WAYLAND_DISPLAY.", "Clients may not be able to connect"); } pid_t pid; if (!spawn_primary_client(argv + 1, &pid)) { ret = 1; goto end; } wl_display_run(server.wl_display); wl_display_destroy_clients(server.wl_display); waitpid(pid, NULL, 0); end: cg_seat_destroy(server.seat); wlr_xdg_shell_destroy(xdg_shell); wlr_data_device_manager_destroy(data_device_mgr); wlr_compositor_destroy(compositor); wlr_output_layout_destroy(server.output_layout); wlr_backend_destroy(server.backend); /* This function is not null-safe, but we only ever get here with a proper wl_display. */ wl_display_destroy(server.wl_display); return ret; }