#define _POSIX_C_SOURCE 200112L #include #include #include #include #include #include "output.h" #include "xdg.h" #include "cursor.h" #include "seat.h" #include "layer.h" #include "session.h" int main(int argc, char *argv[]) { wlr_log_init(WLR_INFO, NULL); char *startup_cmd = NULL; int c; while ((c = getopt(argc, argv, "s:h")) != -1) { switch (c) { case 's': startup_cmd = optarg; break; default: printf("Usage: %s [-s startup command]\n", argv[0]); return 0; } } if (optind < argc) { printf("Usage: %s [-s startup command]\n", argv[0]); return 0; } 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.keyboards); /* 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); if (startup_cmd) { if (fork() == 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); wl_display_run(server.wl_display); /* Once wl_display_run returns, we destroy all clients then shut down the * server. */ 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; }