123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161 |
- // Copyright 2019-2020 Inaban Authors <https://hacktivis.me/git/inaban>
- // SPDX-License-Identifier: BSD-3-Clause
- // Based on wlroots's TinyWL which is distributed under CC0
- #include "inaban.h"
- #include "config.h"
- #include <stdlib.h>
- static void
- render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
- {
- /* This function is called for every surface that needs to be rendered. */
- struct render_data *rdata = data;
- struct inaban_view *view = rdata->view;
- struct wlr_output *output = rdata->output;
- /* We first obtain a wlr_texture, which is a GPU resource. wlroots
- * automatically handles negotiating these with the client. The underlying
- * resource could be an opaque handle passed from the client, or the client
- * could have sent a pixel buffer which we copied to the GPU, or a few other
- * means. You don't have to worry about this, wlroots takes care of it. */
- struct wlr_texture *texture = wlr_surface_get_texture(surface);
- if(texture == NULL) return;
- /* The view has a position in layout coordinates. If you have two displays,
- * one next to the other, both 1080p, a view on the rightmost display might
- * have layout coordinates of 2000,100. We need to translate that to
- * output-local coordinates, or (2000 - 1920). */
- double ox = 0, oy = 0;
- wlr_output_layout_output_coords(view->server->output_layout, output, &ox, &oy);
- ox += view->x + sx, oy += view->y + sy;
- /* We also have to apply the scale factor for HiDPI outputs. This is only
- * part of the puzzle, TinyWL does not fully support HiDPI. */
- struct wlr_box box = {
- .x = ox * output->scale,
- .y = oy * output->scale,
- .width = surface->current.width * output->scale,
- .height = surface->current.height * output->scale,
- };
- /*
- * Those familiar with OpenGL are also familiar with the role of matricies
- * in graphics programming. We need to prepare a matrix to render the view
- * with. wlr_matrix_project_box is a helper which takes a box with a desired
- * x, y coordinates, width and height, and an output geometry, then
- * prepares an orthographic projection and multiplies the necessary
- * transforms to produce a model-view-projection matrix.
- *
- * Naturally you can do this any way you like, for example to make a 3D
- * compositor.
- */
- float matrix[9];
- enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
- wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
- /* This takes our matrix, the texture, and an alpha, and performs the actual
- * rendering on the GPU. */
- wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
- /* This lets the client know that we've displayed that frame and it can
- * prepare another one now if it likes. */
- wlr_surface_send_frame_done(surface, rdata->when);
- }
- static void
- output_frame(struct wl_listener *listener, void *data)
- {
- (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 inaban_output *output = wl_container_of(listener, output, frame);
- struct wlr_renderer *renderer = output->server->renderer;
- struct timespec now;
- clock_gettime(CLOCK_MONOTONIC, &now);
- /* wlr_output_attach_render makes the OpenGL context current. */
- if(!wlr_output_attach_render(output->wlr_output, NULL)) return;
- /* The "effective" resolution can change if you rotate your outputs. */
- int width, height;
- wlr_output_effective_resolution(output->wlr_output, &width, &height);
- /* Begin the renderer (calls glViewport and some other GL sanity checks) */
- wlr_renderer_begin(renderer, width, height);
- float color[4] = {0.11f, 0.11f, 0.11f, 1.0f}; // approx. gruvbox hard-dark
- wlr_renderer_clear(renderer, color);
- /* Each subsequent window we render is rendered on top of the last. Because
- * our view list is ordered front-to-back, we iterate over it backwards. */
- struct inaban_view *view;
- wl_list_for_each_reverse(view, &output->server->views, link)
- {
- if(!view->mapped) continue; /* An unmapped view should not be rendered. */
- struct render_data rdata = {
- .output = output->wlr_output,
- .view = view,
- .renderer = renderer,
- .when = &now,
- };
- /* This calls our render_surface function for each surface among the
- * xdg_surface's toplevel and popups. */
- wlr_xdg_surface_for_each_surface(view->xdg_surface, render_surface, &rdata);
- }
- /* Hardware cursors are rendered by the GPU on a separate plane, and can be
- * moved around without re-rendering what's beneath them - which is more
- * efficient. However, not all hardware supports hardware cursors. For this
- * reason, wlroots provides a software fallback, which we ask it to render
- * here. wlr_cursor handles configuring hardware vs software cursors for you,
- * and this function is a no-op when hardware cursors are in use. */
- wlr_output_render_software_cursors(output->wlr_output, NULL);
- /* Conclude rendering and swap the buffers, showing the final frame
- * on-screen. */
- wlr_renderer_end(renderer);
- wlr_output_commit(output->wlr_output);
- }
- void
- server_new_output(struct wl_listener *listener, void *data)
- {
- /* This event is rasied by the backend when a new output (aka a display or
- * monitor) becomes available. */
- struct inaban_server *server = wl_container_of(listener, server, new_output);
- struct wlr_output *wlr_output = data;
- /* 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 first, a more sophisticated compositor would let the user
- * configure it or pick the mode the display advertises as preferred. */
- if(!wl_list_empty(&wlr_output->modes))
- {
- struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link);
- wlr_output_set_mode(wlr_output, mode);
- }
- /* Allocates and configures our state for this output */
- struct inaban_output *output = calloc(1, sizeof(struct inaban_output));
- output->wlr_output = wlr_output;
- output->server = server;
- /* Sets up a listener for the frame notify event. */
- output->frame.notify = output_frame;
- wl_signal_add(&wlr_output->events.frame, &output->frame);
- wl_list_insert(&server->outputs, &output->link);
- /* 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. */
- wlr_output_layout_add_auto(server->output_layout, wlr_output);
- /* Creating the global 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). */
- wlr_output_create_global(wlr_output);
- }
|