#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include #include #include #include #include "output.h" #include "xdg.h" #include "cursor.h" #include "seat.h" #include "layer.h" #include "session.h" #define PROC_MON_TO 100 void help() { printf("Usage: diyac [-x] [startup command]\n"); printf("Options:\n"); printf(" -x exit with the session\n"); } /** * session process monitor */ static int session_monitor(void *data) { struct diyac_server *server = data; int status; pid_t pid; if (server->session_pid > 0) { pid = waitpid(server->session_pid, &status, WNOHANG); if (pid == -1) { wlr_log(WLR_ERROR, "session waitpid error: %s", strerror(errno)); // try to kill it (void)kill(server->session_pid, SIGKILL); } else if (pid == 0) { wl_event_source_timer_update(server->proc_mon, PROC_MON_TO); return 0; } server->session_pid = -1; } wl_display_terminate(server->wl_display); return 0; } int main(int argc, char *argv[]) { wlr_log_init(WLR_INFO, NULL); char *startup_cmd = NULL; int exit_with_session = 0; int c; while ((c = getopt(argc, argv, "xh")) != -1) { switch (c) { case 'x': exit_with_session = 1; break; default: printf("Usage: %s [-s] startup command]\n", argv[0]); return 0; } } if (exit_with_session && optind == argc) { help(); return 1; } if (optind < argc) { if(optind != argc - 1) { help(); return 1; } // the last argument is the startup command startup_cmd = argv[optind]; } struct diyac_server server = {0}; /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ server.wl_display = wl_display_create(); server.wl_event_loop = wl_display_get_event_loop(server.wl_display); /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window * if an X11 server is running. */ server.backend = wlr_backend_autocreate(server.wl_display, NULL); if (server.backend == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_backend"); return 1; } /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user * can also specify a renderer using the WLR_RENDERER env var. * The renderer is responsible for defining the various pixel formats it * supports for shared memory, this configures that for clients. */ server.renderer = wlr_renderer_autocreate(server.backend); if (server.renderer == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_renderer"); return 1; } wlr_renderer_init_wl_display(server.renderer, server.wl_display); /* Autocreates an allocator for us. * The allocator is the bridge between the renderer and the backend. It * handles the buffer creation, allowing wlroots to render onto the * screen */ server.allocator = wlr_allocator_autocreate(server.backend, server.renderer); if (server.allocator == NULL) { wlr_log(WLR_ERROR, "failed to create wlr_allocator"); return 1; } /* This creates some hands-off wlroots interfaces. The compositor is * necessary for clients to allocate surfaces, the subcompositor allows to * assign the role of subsurfaces to surfaces and the data device manager * handles the clipboard. Each of these wlroots interfaces has room for you * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the handling of the request_set_selection event below.*/ wlr_compositor_create(server.wl_display, 5, server.renderer); wlr_subcompositor_create(server.wl_display); wlr_data_device_manager_create(server.wl_display); /* * Empirically, primary selection doesn't work with Gtk apps unless the * device manager is one of the earliest globals to be advertised. All * credit to Wayfire for discovering this, though their symptoms * (crash) are not the same as ours (silently does nothing). When adding * more globals above this line it would be as well to check that * middle-button paste still works with any Gtk app of your choice * * https://wayfire.org/2020/08/04/Wayfire-0-5.html */ wlr_primary_selection_v1_device_manager_create(server.wl_display); /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ server.output_layout = wlr_output_layout_create(); /* Configure a listener to be notified when new outputs are available on the * backend. */ wl_list_init(&server.outputs); server.new_output.notify = diyac_server_new_output; wl_signal_add(&server.backend->events.new_output, &server.new_output); /* Create a scene graph. This is a wlroots abstraction that handles all * rendering and damage tracking. All the compositor author needs to do * is add things that should be rendered to the scene graph at the proper * positions and then call wlr_scene_output_commit() to render a frame if * necessary. */ server.scene = wlr_scene_create(); server.scene_layout = wlr_scene_attach_output_layout(server.scene, server.output_layout); wlr_fractional_scale_manager_v1_create(server.wl_display,1); /* Set up xdg-shell version 6 The xdg-shell is a Wayland protocol which is * used for application windows. For more detail on shells, refer to * https://drewdevault.com/2018/07/29/Wayland-shells.html. */ wl_list_init(&server.views); /** * TODO: free these tree when finish */ server.view_tree = wlr_scene_tree_create(&server.scene->tree); server.xdg_popup_tree = wlr_scene_tree_create(&server.scene->tree); server.xdg_shell = wlr_xdg_shell_create(server.wl_display, 6); server.new_xdg_surface.notify = diyac_new_xdg_surface; wl_signal_add(&server.xdg_shell->events.new_surface, &server.new_xdg_surface); server.layer_shell = wlr_layer_shell_v1_create(server.wl_display, 4); server.new_layer_surface.notify = diyac_new_layer_surface; wl_signal_add(&server.layer_shell->events.new_surface, &server.new_layer_surface); diyac_init_session_lock(&server); diyac_init_cursor_manager(&server); /* * Configures a seat, which is a single "seat" at which a user sits and * operates the computer. This conceptually includes up to one keyboard, * pointer, touch, and drawing tablet device. We also rig up a listener to * let us know when new input devices are available on the backend. */ wl_list_init(&server.seat.inputs); /* foreign toplevel manager for create shell panel for application control */ server.foreign_toplevel_manager = wlr_foreign_toplevel_manager_v1_create(server.wl_display); diyac_init_seat(&server); /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(server.wl_display); if (!socket) { wlr_backend_destroy(server.backend); return 1; } /* Start the backend. This will enumerate outputs and inputs, become the DRM * master, etc */ if (!wlr_backend_start(server.backend)) { wlr_backend_destroy(server.backend); wl_display_destroy(server.wl_display); return 1; } /* Set the WAYLAND_DISPLAY environment variable to our socket and run the * startup command if requested. */ setenv("WAYLAND_DISPLAY", socket, true); server.session_pid = -1; if (startup_cmd) { server.session_pid = fork(); if (server.session_pid == 0) { execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); } } /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event * loop configuration to listen to libinput events, DRM events, generate * frame events at the refresh rate, and so on. */ wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); server.proc_mon = NULL; if( startup_cmd && exit_with_session) { if (server.session_pid == -1) { wlr_log(WLR_ERROR, "Cannot run startup command: %s. Exit", strerror(errno)); } else { server.proc_mon = wl_event_loop_add_timer(server.wl_event_loop, session_monitor, &server); wl_event_source_timer_update(server.proc_mon, PROC_MON_TO); wl_display_run(server.wl_display); } } else { wl_display_run(server.wl_display); } /* Once wl_display_run returns, we destroy all clients then shut down the * server. */ if(server.proc_mon) { wl_event_source_remove(server.proc_mon); } wl_display_destroy_clients(server.wl_display); // wlr_scene_node_destroy(&server.scene->tree.node); wlr_xcursor_manager_destroy(server.seat.cursor_mgr); wlr_output_layout_destroy(server.output_layout); wl_display_destroy(server.wl_display); return 0; } /** * @brief TODO * reload configuration (keymap, etc.) when sighub is received * */