diyac/output.c
2024-04-01 15:38:57 +02:00

203 lines
8.1 KiB
C

#define _POSIX_C_SOURCE 200112L
#include <time.h>
#include <stdlib.h>
#include <stdbool.h>
#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;
}