Skip to content
This repository was archived by the owner on Nov 1, 2021. It is now read-only.

Commit c260f28

Browse files
committed
tinywl: use wlr_scene
1 parent 1b65a80 commit c260f28

1 file changed

Lines changed: 61 additions & 162 deletions

File tree

tinywl/tinywl.c

Lines changed: 61 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#define _POSIX_C_SOURCE 200112L
2+
#include <assert.h>
23
#include <getopt.h>
34
#include <stdbool.h>
45
#include <stdlib.h>
@@ -17,6 +18,7 @@
1718
#include <wlr/types/wlr_output.h>
1819
#include <wlr/types/wlr_output_layout.h>
1920
#include <wlr/types/wlr_pointer.h>
21+
#include <wlr/types/wlr_scene.h>
2022
#include <wlr/types/wlr_seat.h>
2123
#include <wlr/types/wlr_xcursor_manager.h>
2224
#include <wlr/types/wlr_xdg_shell.h>
@@ -34,6 +36,7 @@ struct tinywl_server {
3436
struct wl_display *wl_display;
3537
struct wlr_backend *backend;
3638
struct wlr_renderer *renderer;
39+
struct wlr_scene *scene;
3740

3841
struct wlr_xdg_shell *xdg_shell;
3942
struct wl_listener new_xdg_surface;
@@ -74,6 +77,7 @@ struct tinywl_view {
7477
struct wl_list link;
7578
struct tinywl_server *server;
7679
struct wlr_xdg_surface *xdg_surface;
80+
struct wlr_scene_node *scene_node;
7781
struct wl_listener map;
7882
struct wl_listener unmap;
7983
struct wl_listener destroy;
@@ -116,6 +120,7 @@ static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) {
116120
}
117121
struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat);
118122
/* Move the view to the front */
123+
wlr_scene_node_raise_to_top(view->scene_node);
119124
wl_list_remove(&view->link);
120125
wl_list_insert(&server->views, &view->link);
121126
/* Activate the new surface */
@@ -164,14 +169,9 @@ static bool handle_keybinding(struct tinywl_server *server, xkb_keysym_t sym) {
164169
if (wl_list_length(&server->views) < 2) {
165170
break;
166171
}
167-
struct tinywl_view *current_view = wl_container_of(
168-
server->views.next, current_view, link);
169172
struct tinywl_view *next_view = wl_container_of(
170-
current_view->link.next, next_view, link);
173+
server->views.prev, next_view, link);
171174
focus_view(next_view, next_view->xdg_surface->surface);
172-
/* Move the previous view to the end of the list */
173-
wl_list_remove(&current_view->link);
174-
wl_list_insert(server->views.prev, &current_view->link);
175175
break;
176176
default:
177177
return false;
@@ -308,52 +308,32 @@ static void seat_request_set_selection(struct wl_listener *listener, void *data)
308308
wlr_seat_set_selection(server->seat, event->source, event->serial);
309309
}
310310

311-
static bool view_at(struct tinywl_view *view,
312-
double lx, double ly, struct wlr_surface **surface,
313-
double *sx, double *sy) {
314-
/*
315-
* XDG toplevels may have nested surfaces, such as popup windows for context
316-
* menus or tooltips. This function tests if any of those are underneath the
317-
* coordinates lx and ly (in output Layout Coordinates). If so, it sets the
318-
* surface pointer to that wlr_surface and the sx and sy coordinates to the
319-
* coordinates relative to that surface's top-left corner.
320-
*/
321-
double view_sx = lx - view->x;
322-
double view_sy = ly - view->y;
323-
324-
double _sx, _sy;
325-
struct wlr_surface *_surface = NULL;
326-
_surface = wlr_xdg_surface_surface_at(
327-
view->xdg_surface, view_sx, view_sy, &_sx, &_sy);
328-
329-
if (_surface != NULL) {
330-
*sx = _sx;
331-
*sy = _sy;
332-
*surface = _surface;
333-
return true;
334-
}
335-
336-
return false;
337-
}
338-
339311
static struct tinywl_view *desktop_view_at(
340312
struct tinywl_server *server, double lx, double ly,
341313
struct wlr_surface **surface, double *sx, double *sy) {
342-
/* This iterates over all of our surfaces and attempts to find one under the
343-
* cursor. This relies on server->views being ordered from top-to-bottom. */
344-
struct tinywl_view *view;
345-
wl_list_for_each(view, &server->views, link) {
346-
if (view_at(view, lx, ly, surface, sx, sy)) {
347-
return view;
348-
}
314+
/* This returns the topmost node in the scene at the given layout coords.
315+
* we only care about surface nodes as we are specifically looking for a
316+
* surface in the surface tree of a tinywl_view. */
317+
struct wlr_scene_node *node = wlr_scene_node_at(&server->scene->node,
318+
lx, ly, sx, sy);
319+
if (node == NULL || node->type != WLR_SCENE_NODE_SURFACE) {
320+
return NULL;
349321
}
350-
return NULL;
322+
*surface = wlr_scene_surface_from_node(node)->surface;
323+
/* Find the node corresponding to the tinywl_view at the root of this
324+
* surface tree, it is the only one for which we set the data field. */
325+
while (node != NULL && node->data == NULL) {
326+
node = node->parent;
327+
}
328+
return node->data;
351329
}
352330

353331
static void process_cursor_move(struct tinywl_server *server, uint32_t time) {
354332
/* Move the grabbed view to the new position. */
355-
server->grabbed_view->x = server->cursor->x - server->grab_x;
356-
server->grabbed_view->y = server->cursor->y - server->grab_y;
333+
struct tinywl_view *view = server->grabbed_view;
334+
view->x = server->cursor->x - server->grab_x;
335+
view->y = server->cursor->y - server->grab_y;
336+
wlr_scene_node_set_position(view->scene_node, view->x, view->y);
357337
}
358338

359339
static void process_cursor_resize(struct tinywl_server *server, uint32_t time) {
@@ -402,6 +382,7 @@ static void process_cursor_resize(struct tinywl_server *server, uint32_t time) {
402382
wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box);
403383
view->x = new_left - geo_box.x;
404384
view->y = new_top - geo_box.y;
385+
wlr_scene_node_set_position(view->scene_node, view->x, view->y);
405386

406387
int new_width = new_right - new_left;
407388
int new_height = new_bottom - new_top;
@@ -528,131 +509,37 @@ static void server_cursor_frame(struct wl_listener *listener, void *data) {
528509
wlr_seat_pointer_notify_frame(server->seat);
529510
}
530511

531-
/* Used to move all of the data necessary to render a surface from the top-level
532-
* frame handler to the per-surface render function. */
533-
struct render_data {
534-
struct wlr_output *output;
535-
struct wlr_renderer *renderer;
536-
struct tinywl_view *view;
537-
struct timespec *when;
538-
};
539-
540-
static void render_surface(struct wlr_surface *surface,
512+
// TODO: We should avoid sending the frame done event twice if a surface
513+
// appears on multiple outputs.
514+
// https://github.com/swaywm/wlroots/issues/3210
515+
static void send_frame_done(struct wlr_surface *surface,
541516
int sx, int sy, void *data) {
542-
/* This function is called for every surface that needs to be rendered. */
543-
struct render_data *rdata = data;
544-
struct tinywl_view *view = rdata->view;
545-
struct wlr_output *output = rdata->output;
546-
547-
/* We first obtain a wlr_texture, which is a GPU resource. wlroots
548-
* automatically handles negotiating these with the client. The underlying
549-
* resource could be an opaque handle passed from the client, or the client
550-
* could have sent a pixel buffer which we copied to the GPU, or a few other
551-
* means. You don't have to worry about this, wlroots takes care of it. */
552-
struct wlr_texture *texture = wlr_surface_get_texture(surface);
553-
if (texture == NULL) {
554-
return;
555-
}
556-
557-
/* The view has a position in layout coordinates. If you have two displays,
558-
* one next to the other, both 1080p, a view on the rightmost display might
559-
* have layout coordinates of 2000,100. We need to translate that to
560-
* output-local coordinates, or (2000 - 1920). */
561-
double ox = 0, oy = 0;
562-
wlr_output_layout_output_coords(
563-
view->server->output_layout, output, &ox, &oy);
564-
ox += view->x + sx, oy += view->y + sy;
565-
566-
/* We also have to apply the scale factor for HiDPI outputs. This is only
567-
* part of the puzzle, TinyWL does not fully support HiDPI. */
568-
struct wlr_box box = {
569-
.x = ox * output->scale,
570-
.y = oy * output->scale,
571-
.width = surface->current.width * output->scale,
572-
.height = surface->current.height * output->scale,
573-
};
574-
575-
/*
576-
* Those familiar with OpenGL are also familiar with the role of matricies
577-
* in graphics programming. We need to prepare a matrix to render the view
578-
* with. wlr_matrix_project_box is a helper which takes a box with a desired
579-
* x, y coordinates, width and height, and an output geometry, then
580-
* prepares an orthographic projection and multiplies the necessary
581-
* transforms to produce a model-view-projection matrix.
582-
*
583-
* Naturally you can do this any way you like, for example to make a 3D
584-
* compositor.
585-
*/
586-
float matrix[9];
587-
enum wl_output_transform transform =
588-
wlr_output_transform_invert(surface->current.transform);
589-
wlr_matrix_project_box(matrix, &box, transform, 0,
590-
output->transform_matrix);
591-
592-
/* This takes our matrix, the texture, and an alpha, and performs the actual
593-
* rendering on the GPU. */
594-
wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
595-
596-
/* This lets the client know that we've displayed that frame and it can
597-
* prepare another one now if it likes. */
598-
wlr_surface_send_frame_done(surface, rdata->when);
517+
wlr_surface_send_frame_done(surface, data);
599518
}
600519

601520
static void output_frame(struct wl_listener *listener, void *data) {
602521
/* This function is called every time an output is ready to display a frame,
603522
* generally at the output's refresh rate (e.g. 60Hz). */
604523
struct tinywl_output *output =
605524
wl_container_of(listener, output, frame);
606-
struct wlr_renderer *renderer = output->server->renderer;
607525

608-
struct timespec now;
609-
clock_gettime(CLOCK_MONOTONIC, &now);
610-
611-
/* wlr_output_attach_render makes the OpenGL context current. */
612-
if (!wlr_output_attach_render(output->wlr_output, NULL)) {
613-
return;
614-
}
615-
/* The "effective" resolution can change if you rotate your outputs. */
616-
int width, height;
617-
wlr_output_effective_resolution(output->wlr_output, &width, &height);
618-
/* Begin the renderer (calls glViewport and some other GL sanity checks) */
619-
wlr_renderer_begin(renderer, width, height);
620-
621-
float color[4] = {0.3, 0.3, 0.3, 1.0};
622-
wlr_renderer_clear(renderer, color);
623-
624-
/* Each subsequent window we render is rendered on top of the last. Because
625-
* our view list is ordered front-to-back, we iterate over it backwards. */
626-
struct tinywl_view *view;
627-
wl_list_for_each_reverse(view, &output->server->views, link) {
628-
if (!view->mapped) {
629-
/* An unmapped view should not be rendered. */
630-
continue;
526+
bool found = false;
527+
struct wlr_scene_output *scene_output;
528+
wl_list_for_each(scene_output, &output->server->scene->outputs, link) {
529+
if (scene_output->output == output->wlr_output) {
530+
found = true;
531+
break;
631532
}
632-
struct render_data rdata = {
633-
.output = output->wlr_output,
634-
.view = view,
635-
.renderer = renderer,
636-
.when = &now,
637-
};
638-
/* This calls our render_surface function for each surface among the
639-
* xdg_surface's toplevel and popups. */
640-
wlr_xdg_surface_for_each_surface(view->xdg_surface,
641-
render_surface, &rdata);
642533
}
534+
assert(found);
643535

644-
/* Hardware cursors are rendered by the GPU on a separate plane, and can be
645-
* moved around without re-rendering what's beneath them - which is more
646-
* efficient. However, not all hardware supports hardware cursors. For this
647-
* reason, wlroots provides a software fallback, which we ask it to render
648-
* here. wlr_cursor handles configuring hardware vs software cursors for you,
649-
* and this function is a no-op when hardware cursors are in use. */
650-
wlr_output_render_software_cursors(output->wlr_output, NULL);
651-
652-
/* Conclude rendering and swap the buffers, showing the final frame
653-
* on-screen. */
654-
wlr_renderer_end(renderer);
655-
wlr_output_commit(output->wlr_output);
536+
/* Render the scene if needed and commit the output */
537+
wlr_scene_output_commit(scene_output);
538+
539+
struct timespec now;
540+
clock_gettime(CLOCK_MONOTONIC, &now);
541+
wlr_scene_node_for_each_surface(&output->server->scene->node,
542+
send_frame_done, &now);
656543
}
657544

658545
static void server_new_output(struct wl_listener *listener, void *data) {
@@ -702,19 +589,29 @@ static void xdg_surface_map(struct wl_listener *listener, void *data) {
702589
/* Called when the surface is mapped, or ready to display on-screen. */
703590
struct tinywl_view *view = wl_container_of(listener, view, map);
704591
view->mapped = true;
592+
593+
view->scene_node = wlr_scene_subsurface_tree_create(
594+
&view->server->scene->node, view->xdg_surface->surface);
595+
view->scene_node->data = view;
596+
597+
wl_list_insert(&view->server->views, &view->link);
598+
705599
focus_view(view, view->xdg_surface->surface);
706600
}
707601

708602
static void xdg_surface_unmap(struct wl_listener *listener, void *data) {
709603
/* Called when the surface is unmapped, and should no longer be shown. */
710604
struct tinywl_view *view = wl_container_of(listener, view, unmap);
711605
view->mapped = false;
606+
607+
wlr_scene_node_destroy(view->scene_node);
712608
}
713609

714610
static void xdg_surface_destroy(struct wl_listener *listener, void *data) {
715611
/* Called when the surface is destroyed and should never be shown again. */
716612
struct tinywl_view *view = wl_container_of(listener, view, destroy);
717613
wl_list_remove(&view->link);
614+
718615
free(view);
719616
}
720617

@@ -807,8 +704,6 @@ static void server_new_xdg_surface(struct wl_listener *listener, void *data) {
807704
view->request_resize.notify = xdg_toplevel_request_resize;
808705
wl_signal_add(&toplevel->events.request_resize, &view->request_resize);
809706

810-
/* Add it to the list of views. */
811-
wl_list_insert(&server->views, &view->link);
812707
}
813708

814709
int main(int argc, char *argv[]) {
@@ -866,9 +761,13 @@ int main(int argc, char *argv[]) {
866761
server.new_output.notify = server_new_output;
867762
wl_signal_add(&server.backend->events.new_output, &server.new_output);
868763

869-
/* Set up our list of views and the xdg-shell. The xdg-shell is a Wayland
870-
* protocol which is used for application windows. For more detail on
871-
* shells, refer to my article:
764+
/* Create a scene
765+
*/
766+
server.scene = wlr_scene_create();
767+
wlr_scene_attach_output_layout(server.scene, server.output_layout);
768+
769+
/* Set up the xdg-shell. The xdg-shell is a Wayland protocol which is used
770+
* for application windows. For more detail on shells, refer to my article:
872771
*
873772
* https://drewdevault.com/2018/07/29/Wayland-shells.html
874773
*/

0 commit comments

Comments
 (0)