11#define _POSIX_C_SOURCE 200112L
2+ #include <assert.h>
23#include <getopt.h>
34#include <stdbool.h>
45#include <stdlib.h>
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-
339311static 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
353331static 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
359339static 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
601520static 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
658545static 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
708602static 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
714610static 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
814709int 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