output.c 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. // Copyright 2019-2020 Inaban Authors <https://hacktivis.me/git/inaban>
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Based on wlroots's TinyWL which is distributed under CC0
  4. #include "inaban.h"
  5. #include "layers.h"
  6. #include "config.h"
  7. #include <stdlib.h>
  8. static void
  9. render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
  10. {
  11. /* This function is called for every surface that needs to be rendered. */
  12. struct render_data *rdata = data;
  13. struct inaban_view *view = rdata->view;
  14. struct wlr_output *output = rdata->output;
  15. /* We first obtain a wlr_texture, which is a GPU resource. wlroots
  16. * automatically handles negotiating these with the client. The underlying
  17. * resource could be an opaque handle passed from the client, or the client
  18. * could have sent a pixel buffer which we copied to the GPU, or a few other
  19. * means. You don't have to worry about this, wlroots takes care of it. */
  20. struct wlr_texture *texture = wlr_surface_get_texture(surface);
  21. if(texture == NULL) return;
  22. /* The view has a position in layout coordinates. If you have two displays,
  23. * one next to the other, both 1080p, a view on the rightmost display might
  24. * have layout coordinates of 2000,100. We need to translate that to
  25. * output-local coordinates, or (2000 - 1920). */
  26. double ox = 0, oy = 0;
  27. wlr_output_layout_output_coords(view->server->output_layout, output, &ox, &oy);
  28. ox += view->x + sx, oy += view->y + sy;
  29. /* We also have to apply the scale factor for HiDPI outputs. This is only
  30. * part of the puzzle, TinyWL does not fully support HiDPI. */
  31. struct wlr_box box = {
  32. .x = ox * output->scale,
  33. .y = oy * output->scale,
  34. .width = surface->current.width * output->scale,
  35. .height = surface->current.height * output->scale,
  36. };
  37. /*
  38. * Those familiar with OpenGL are also familiar with the role of matricies
  39. * in graphics programming. We need to prepare a matrix to render the view
  40. * with. wlr_matrix_project_box is a helper which takes a box with a desired
  41. * x, y coordinates, width and height, and an output geometry, then
  42. * prepares an orthographic projection and multiplies the necessary
  43. * transforms to produce a model-view-projection matrix.
  44. *
  45. * Naturally you can do this any way you like, for example to make a 3D
  46. * compositor.
  47. */
  48. float matrix[9];
  49. enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
  50. wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
  51. /* This takes our matrix, the texture, and an alpha, and performs the actual
  52. * rendering on the GPU. */
  53. wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
  54. /* This lets the client know that we've displayed that frame and it can
  55. * prepare another one now if it likes. */
  56. wlr_surface_send_frame_done(surface, rdata->when);
  57. }
  58. struct render_data_layer
  59. {
  60. struct wlr_output *output;
  61. struct wlr_renderer *renderer;
  62. struct inaban_view *view;
  63. struct timespec *when;
  64. };
  65. static void
  66. render_layer_surface(struct wlr_surface *surface, int sx, int sy, void *data)
  67. {
  68. struct inaban_layer_surface *layer_surface = data;
  69. struct wlr_texture *texture = wlr_surface_get_texture(surface);
  70. if(texture == NULL)
  71. {
  72. return;
  73. }
  74. struct wlr_output *output = layer_surface->layer_surface->output;
  75. double ox = 0, oy = 0;
  76. wlr_output_layout_output_coords(layer_surface->server->output_layout, output, &ox, &oy);
  77. ox += layer_surface->geo.x + sx, oy += layer_surface->geo.y + sy;
  78. float matrix[9];
  79. enum wl_output_transform transform = wlr_output_transform_invert(surface->current.transform);
  80. struct wlr_box box;
  81. memcpy(&box, &layer_surface->geo, sizeof(struct wlr_box));
  82. wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix);
  83. wlr_render_texture_with_matrix(layer_surface->server->renderer, texture, matrix, 1);
  84. // Hack because I'm too lazy to fish through a new rdata struct
  85. struct timespec now;
  86. clock_gettime(CLOCK_MONOTONIC, &now);
  87. wlr_surface_send_frame_done(surface, &now);
  88. }
  89. static void
  90. render_layer(struct inaban_output *output, struct wl_list *layer_surfaces)
  91. {
  92. struct inaban_layer_surface *layer_surface;
  93. wl_list_for_each(layer_surface, layer_surfaces, link)
  94. {
  95. struct wlr_layer_surface_v1 *wlr_layer_surface_v1 = layer_surface->layer_surface;
  96. wlr_surface_for_each_surface(
  97. wlr_layer_surface_v1->surface, render_layer_surface, layer_surface);
  98. }
  99. }
  100. static void
  101. output_frame(struct wl_listener *listener, void *data)
  102. {
  103. (void)data;
  104. /* This function is called every time an output is ready to display a frame,
  105. * generally at the output's refresh rate (e.g. 60Hz). */
  106. struct inaban_output *output = wl_container_of(listener, output, frame);
  107. struct wlr_renderer *renderer = output->server->renderer;
  108. struct timespec now;
  109. clock_gettime(CLOCK_MONOTONIC, &now);
  110. /* wlr_output_attach_render makes the OpenGL context current. */
  111. if(!wlr_output_attach_render(output->wlr_output, NULL)) return;
  112. /* The "effective" resolution can change if you rotate your outputs. */
  113. int width, height;
  114. wlr_output_effective_resolution(output->wlr_output, &width, &height);
  115. /* Begin the renderer (calls glViewport and some other GL sanity checks) */
  116. wlr_renderer_begin(renderer, width, height);
  117. float color[4] = {0.11f, 0.11f, 0.11f, 1.0f}; // approx. gruvbox hard-dark
  118. wlr_renderer_clear(renderer, color);
  119. render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
  120. render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
  121. render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
  122. render_layer(output, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
  123. /* Each subsequent window we render is rendered on top of the last. Because
  124. * our view list is ordered front-to-back, we iterate over it backwards. */
  125. struct inaban_view *view;
  126. wl_list_for_each_reverse(view, &output->server->views, link)
  127. {
  128. if(!view->mapped) continue; /* An unmapped view should not be rendered. */
  129. struct render_data rdata = {
  130. .output = output->wlr_output,
  131. .view = view,
  132. .renderer = renderer,
  133. .when = &now,
  134. };
  135. /* This calls our render_surface function for each surface among the
  136. * xdg_surface's toplevel and popups. */
  137. wlr_xdg_surface_for_each_surface(view->xdg_surface, render_surface, &rdata);
  138. }
  139. /* Hardware cursors are rendered by the GPU on a separate plane, and can be
  140. * moved around without re-rendering what's beneath them - which is more
  141. * efficient. However, not all hardware supports hardware cursors. For this
  142. * reason, wlroots provides a software fallback, which we ask it to render
  143. * here. wlr_cursor handles configuring hardware vs software cursors for you,
  144. * and this function is a no-op when hardware cursors are in use. */
  145. wlr_output_render_software_cursors(output->wlr_output, NULL);
  146. /* Conclude rendering and swap the buffers, showing the final frame
  147. * on-screen. */
  148. wlr_renderer_end(renderer);
  149. wlr_output_commit(output->wlr_output);
  150. }
  151. void
  152. server_new_output(struct wl_listener *listener, void *data)
  153. {
  154. /* This event is rasied by the backend when a new output (aka a display or
  155. * monitor) becomes available. */
  156. struct inaban_server *server = wl_container_of(listener, server, new_output);
  157. struct wlr_output *wlr_output = data;
  158. /* Some backends don't have modes. DRM+KMS does, and we need to set a mode
  159. * before we can use the output. The mode is a tuple of (width, height,
  160. * refresh rate), and each monitor supports only a specific set of modes. We
  161. * just pick the first, a more sophisticated compositor would let the user
  162. * configure it or pick the mode the display advertises as preferred. */
  163. if(!wl_list_empty(&wlr_output->modes))
  164. {
  165. struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.prev, mode, link);
  166. wlr_output_set_mode(wlr_output, mode);
  167. }
  168. /* Allocates and configures our state for this output */
  169. struct inaban_output *output = calloc(1, sizeof(struct inaban_output));
  170. output->wlr_output = wlr_output;
  171. output->server = server;
  172. /* Sets up a listener for the frame notify event. */
  173. output->frame.notify = output_frame;
  174. wl_signal_add(&wlr_output->events.frame, &output->frame);
  175. wl_list_insert(&server->outputs, &output->link);
  176. wlr_output->data = output;
  177. wl_list_init(&output->layers[0]);
  178. wl_list_init(&output->layers[1]);
  179. wl_list_init(&output->layers[2]);
  180. wl_list_init(&output->layers[3]);
  181. /* Adds this to the output layout. The add_auto function arranges outputs
  182. * from left-to-right in the order they appear. A more sophisticated
  183. * compositor would let the user configure the arrangement of outputs in the
  184. * layout. */
  185. wlr_output_layout_add_auto(server->output_layout, wlr_output);
  186. /* Creating the global adds a wl_output global to the display, which Wayland
  187. * clients can see to find out information about the output (such as
  188. * DPI, scale factor, manufacturer, etc). */
  189. wlr_output_create_global(wlr_output);
  190. }