#define _POSIX_C_SOURCE 200112L #include #include #include #include "output.h" #include "layer.h" #include "node.h" #include "view.h" static void output_frame(struct wl_listener *listener, void *data) { /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ struct diyac_output *output = wl_container_of(listener, output, frame); struct wlr_scene *scene = output->server->scene; struct wlr_scene_output *scene_output = wlr_scene_get_scene_output( scene, output->wlr_output); /* Render the scene if needed and commit the output */ wlr_scene_output_commit(scene_output, NULL); struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(scene_output, &now); } static void output_request_state(struct wl_listener *listener, void *data) { /* This function is called when the backend requests a new state for * the output. For example, Wayland and X11 backends request a new mode * when the output window is resized. */ struct diyac_output *output = wl_container_of(listener, output, request_state); const struct wlr_output_event_request_state *event = data; wlr_output_commit_state(output->wlr_output, event->state); } static void output_destroy(struct wl_listener *listener, void *data) { struct diyac_output *output = wl_container_of(listener, output, destroy); wl_list_remove(&output->frame.link); wl_list_remove(&output->request_state.link); wl_list_remove(&output->destroy.link); wl_list_remove(&output->link); free(output); } void diyac_server_new_output(struct wl_listener *listener, void *data) { /* This event is raised by the backend when a new output (aka a display or * monitor) becomes available. */ struct diyac_server *server = wl_container_of(listener, server, new_output); struct wlr_output *wlr_output = data; /* Configures the output created by the backend to use our allocator * and our renderer. Must be done once, before commiting the output */ wlr_output_init_render(wlr_output, server->allocator, server->renderer); /* The output may be disabled, switch it on. */ struct wlr_output_state state; wlr_output_state_init(&state); wlr_output_state_set_enabled(&state, true); /* Some backends don't have modes. DRM+KMS does, and we need to set a mode * before we can use the output. The mode is a tuple of (width, height, * refresh rate), and each monitor supports only a specific set of modes. We * just pick the monitor's preferred mode, a more sophisticated compositor * would let the user configure it. */ struct wlr_output_mode *mode = wlr_output_preferred_mode(wlr_output); if (mode != NULL) { wlr_output_state_set_mode(&state, mode); } /* Atomically applies the new output state. */ wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); /* Allocates and configures our state for this output */ struct diyac_output *output = calloc(1, sizeof(*output)); output->wlr_output = wlr_output; output->server = server; wlr_output->data = output; output->usable_area.x = 0; output->usable_area.y = 0; output->usable_area.width = wlr_output->width; output->usable_area.height = wlr_output->height; /* Sets up a listener for the frame event. */ output->frame.notify = output_frame; wl_signal_add(&wlr_output->events.frame, &output->frame); /* Sets up a listener for the state request event. */ output->request_state.notify = output_request_state; wl_signal_add(&wlr_output->events.request_state, &output->request_state); /* Sets up a listener for the destroy event. */ output->destroy.notify = output_destroy; wl_signal_add(&wlr_output->events.destroy, &output->destroy); wl_list_insert(&server->outputs, &output->link); /* * Create layer-trees (background, bottom, top and overlay) and * a layer-popup-tree. */ output->scenes.background = wlr_scene_tree_create(&server->scene->tree); output->scenes.bottom = wlr_scene_tree_create(&server->scene->tree); output->scenes.top = wlr_scene_tree_create(&server->scene->tree); output->scenes.popup = wlr_scene_tree_create(&server->scene->tree); output->scenes.overlay = wlr_scene_tree_create(&server->scene->tree); output->scenes.session = wlr_scene_tree_create(&server->scene->tree); diyac_node_descriptor_create(&output->scenes.background->node, DIYAC_NODE_TREE, NULL); diyac_node_descriptor_create(&output->scenes.bottom->node, DIYAC_NODE_TREE, NULL); diyac_node_descriptor_create(&output->scenes.top->node, DIYAC_NODE_TREE, NULL); diyac_node_descriptor_create(&output->scenes.popup->node, DIYAC_NODE_TREE, NULL); diyac_node_descriptor_create(&output->scenes.overlay->node, DIYAC_NODE_TREE, NULL); diyac_node_descriptor_create(&output->scenes.session->node, DIYAC_NODE_TREE, NULL); output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND] = output->scenes.background; output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM] = output->scenes.bottom; output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_TOP] = output->scenes.top; output->layer_tree[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY] = output->scenes.overlay; /* * Set the z-positions to achieve the following order (from top to * bottom): * - session lock layer * - layer-shell popups * - overlay layer * - top layer * - views * - bottom layer * - background layer */ wlr_scene_node_lower_to_bottom(&output->scenes.bottom->node); wlr_scene_node_lower_to_bottom(&output->scenes.background->node); wlr_scene_node_raise_to_top(&output->scenes.top->node); wlr_scene_node_raise_to_top(&output->scenes.overlay->node); wlr_scene_node_raise_to_top(&output->scenes.popup->node); wlr_scene_node_raise_to_top(&output->scenes.session->node); /* Adds this to the output layout. The add_auto function arranges outputs * from left-to-right in the order they appear. A more sophisticated * compositor would let the user configure the arrangement of outputs in the * layout. * * The output layout utility automatically adds a wl_output global to the * display, which Wayland clients can see to find out information about the * output (such as DPI, scale factor, manufacturer, etc). */ struct wlr_output_layout_output *l_output = wlr_output_layout_add_auto(server->output_layout, wlr_output); struct wlr_scene_output *scene_output = wlr_scene_output_create(server->scene, wlr_output); wlr_scene_output_layout_add_output(server->scene_layout, l_output, scene_output); } /* returns true if usable area changed */ static bool update_usable_area(struct diyac_output *output) { struct wlr_box old = output->usable_area; diyac_layers_arrange(output); return !wlr_box_equal(&old, &output->usable_area); } void diyac_output_update_usable_area(struct diyac_output *output) { if (update_usable_area(output)) { // regions_update_geometry(output); diyac_arrange_all_views(output->server); // desktop_arrange_all_views(output->server); } } struct diyac_output *diyac_output_from_cursor(struct diyac_server *server) { double closest_x, closest_y; wlr_output_layout_closest_point(server->output_layout, NULL, server->seat.cursor->x, server->seat.cursor->y, &closest_x, &closest_y); struct wlr_output *output = wlr_output_layout_output_at(server->output_layout, closest_x, closest_y); if (!output) return NULL; return output->data; } struct wlr_box diyac_output_usable_area(struct diyac_output *output) { if (!output) { return (struct wlr_box){0}; } struct wlr_box box = output->usable_area; double ox = 0, oy = 0; wlr_output_layout_output_coords(output->server->output_layout, output->wlr_output, &ox, &oy); box.x -= ox; box.y -= oy; return box; }