]> git.mdlowis.com Git - proto/labwc.git/commitdiff
Implement damage tracking
authorJohan Malm <jgm323@gmail.com>
Sat, 9 Jan 2021 22:51:20 +0000 (22:51 +0000)
committerJohan Malm <jgm323@gmail.com>
Sat, 9 Jan 2021 22:51:20 +0000 (22:51 +0000)
15 files changed:
README.md
include/labwc.h
meson.build
src/action.c
src/cursor.c
src/damage.c [new file with mode: 0644]
src/desktop.c
src/keyboard.c
src/layers.c
src/meson.build
src/output.c
src/view.c
src/xdg.c
src/xwayland-unmanaged.c
src/xwayland.c

index 51f67fdb305ce607ba466862a49c20ca0f723983..ee13705c7854889ace59ffdeba39f266423f9e9d 100644 (file)
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ The following were considered before choosing wlroots: [qtwayland](https://githu
 Dependencies include:
 
 - meson, ninja
-- wlroots (>=0.11.0)
+- wlroots (0.11.0 - 0.12.0)
 - wayland (>=1.16)
 - wayland-protocols
 - xwayland, xcb (optional)
@@ -95,8 +95,8 @@ No acceptance criteria exists, but the following list indicates the inteded high
 - [x] Show maximize, iconify, close buttons
 - [x] Catch SIGHUP to re-load config file and theme
 - [x] Support layer-shell protocol ('exclusive' not yet implemented)
-- [ ] Support root-menu and parse menu.xml (very simple implementation, not submenus yet)
-- [ ] Support damage tracking to reduce CPU usage
+- [x] Support damage tracking to reduce CPU usage
+- [ ] Support root-menu and parse menu.xml (very simple implementation, no submenus yet)
 - [ ] Support 'maximize'
 - [ ] Support wlr-output-management protocol and [kanshi](https://github.com/emersion/kanshi.git)
 - [ ] Support foreign-toplevel protocol (e.g. to integrate with wlroots panels/bars)
index b1932ba20a8f78ec39c5ef06afe33eaca89eb627..4829e0bce4ef066f9ee8f7b0dee90739e5d90f49 100644 (file)
@@ -19,6 +19,7 @@
 #include <wlr/types/wlr_layer_shell_v1.h>
 #include <wlr/types/wlr_matrix.h>
 #include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_damage.h>
 #include <wlr/types/wlr_output_layout.h>
 #include <wlr/types/wlr_pointer.h>
 #include <wlr/types/wlr_seat.h>
@@ -123,9 +124,11 @@ struct output {
        struct wl_list link;
        struct server *server;
        struct wlr_output *wlr_output;
+       struct wlr_output_damage *damage;
        struct wl_list layers[4];
-       struct wl_listener frame;
        struct wl_listener destroy;
+       struct wl_listener damage_frame;
+       struct wl_listener damage_destroy;
 };
 
 enum view_type {
@@ -151,6 +154,8 @@ enum deco_part {
 struct view_impl {
        void (*configure)(struct view *view, struct wlr_box geo);
        void (*close)(struct view *view);
+       void (*for_each_popup)(struct view *view,
+               wlr_surface_iterator_func_t iterator, void *data);
        void (*for_each_surface)(struct view *view,
                wlr_surface_iterator_func_t iterator, void *data);
        void (*map)(struct view *view);
@@ -211,6 +216,7 @@ struct view {
        struct wl_listener request_move;
        struct wl_listener request_resize;
        struct wl_listener request_configure;
+       struct wl_listener new_popup; /* xdg-shell only */
 };
 
 #if HAVE_XWAYLAND
@@ -228,6 +234,17 @@ struct xwayland_unmanaged {
 };
 #endif
 
+struct xdg_popup {
+       struct wlr_xdg_popup *wlr_popup;
+       struct view *view;
+
+       struct wl_listener destroy;
+       struct wl_listener commit;
+       struct wl_listener map;
+       struct wl_listener unmap;
+       struct wl_listener new_popup;
+};
+
 void xdg_toplevel_decoration(struct wl_listener *listener, void *data);
 void xdg_surface_new(struct wl_listener *listener, void *data);
 
@@ -243,6 +260,8 @@ void view_minimize(struct view *view);
 void view_unminimize(struct view *view);
 void view_for_each_surface(struct view *view,
        wlr_surface_iterator_func_t iterator, void *user_data);
+void view_for_each_popup(struct view *view,
+       wlr_surface_iterator_func_t iterator, void *data);
 
 void desktop_focus_view(struct seat *seat, struct view *view);
 
@@ -270,6 +289,12 @@ void interactive_begin(struct view *view, enum input_mode mode,
                       uint32_t edges);
 
 void output_init(struct server *server);
+void output_damage_surface(struct output *output, struct wlr_surface *surface,
+       double lx, double ly, bool whole);
+
+void damage_all_outputs(struct server *server);
+void damage_view_whole(struct view *view);
+void damage_view_part(struct view *view);
 
 void server_init(struct server *server);
 void server_start(struct server *server);
index 10776feb0a1bb8cd09510e16435d8a5e56f922fc..8c3509f2e6ec449a5b486cbb2cdb6e5ad138f598 100644 (file)
@@ -49,6 +49,7 @@ glib            = dependency('glib-2.0')
 cairo           = dependency('cairo')
 pangocairo      = dependency('pangocairo')
 input           = dependency('libinput', version: '>=1.14')
+pixman          = dependency('pixman-1')
 
 if get_option('xwayland').enabled() and not wlroots_has_xwayland
        error('no wlroots Xwayland support')
@@ -63,7 +64,7 @@ subdir('protocols')
 
 labwc_deps      = [
   server_protos, wayland_server, wlroots, xkbcommon, xml2, glib,
-  cairo, pangocairo, input
+  cairo, pangocairo, input, pixman
 ]
 
 subdir('include')
index 5d37653e33c332049146645edc668adad5aca56b..5510dc85730c54e717c7b606efdbc675eeae9ef3 100644 (file)
@@ -15,6 +15,7 @@ show_menu(struct server *server, const char *menu)
                menu_move(server->rootmenu, server->seat.cursor->x,
                        server->seat.cursor->y);
        }
+       damage_all_outputs(server);
 }
 
 void
@@ -29,6 +30,7 @@ action(struct server *server, const char *action, const char *command)
        } else if (!strcasecmp(action, "Exit")) {
                wl_display_terminate(server->wl_display);
        } else if (!strcasecmp(action, "NextWindow")) {
+               dbg_show_views(server);
                server->cycle_view =
                        desktop_cycle_view(server, server->cycle_view);
        } else if (!strcasecmp(action, "Reconfigure")) {
index 40ff39e98ae7d3bb681901c20ec6fa2914bf5e79..7adfed4d40820330577023532a449efe68014e01 100644 (file)
@@ -66,6 +66,7 @@ request_set_selection_notify(struct wl_listener *listener, void *data)
 static void
 process_cursor_move(struct server *server, uint32_t time)
 {
+       damage_all_outputs(server);
        /* Move the grabbed view to the new position. */
        double dx = server->seat.cursor->x - server->grab_x;
        double dy = server->seat.cursor->y - server->grab_y;
@@ -81,10 +82,7 @@ process_cursor_move(struct server *server, uint32_t time)
 static void
 process_cursor_resize(struct server *server, uint32_t time)
 {
-       /*
-        * TODO: Wait for the client to prepare a buffer at the new size, then
-        * commit any movement that was prepared.
-        */
+       damage_all_outputs(server);
        double dx = server->seat.cursor->x - server->grab_x;
        double dy = server->seat.cursor->y - server->grab_y;
 
@@ -128,6 +126,7 @@ process_cursor_motion(struct server *server, uint32_t time)
        } else if (server->input_mode == LAB_INPUT_STATE_MENU) {
                menu_set_selected(server->rootmenu,
                        server->seat.cursor->x, server->seat.cursor->y);
+               damage_all_outputs(server);
                return;
        }
 
@@ -285,6 +284,7 @@ cursor_button(struct wl_listener *listener, void *data)
                }
                /* Exit interactive move/resize/menu mode. */
                server->input_mode = LAB_INPUT_STATE_PASSTHROUGH;
+               damage_all_outputs(server);
                return;
        }
 
diff --git a/src/damage.c b/src/damage.c
new file mode 100644 (file)
index 0000000..49d9796
--- /dev/null
@@ -0,0 +1,32 @@
+#include "labwc.h"
+
+void
+damage_all_outputs(struct server *server)
+{
+       struct output *output;
+       wl_list_for_each(output, &server->outputs, link) {
+               if (output && output->wlr_output && output->damage) {
+                       wlr_output_damage_add_whole(output->damage);
+               }
+       }
+}
+
+void
+damage_view_part(struct view *view)
+{
+       struct output *output;
+       wl_list_for_each (output, &view->server->outputs, link) {
+               output_damage_surface(output, view->surface, view->x, view->y,
+                       false);
+       }
+}
+
+void
+damage_view_whole(struct view *view)
+{
+       struct output *output;
+       wl_list_for_each (output, &view->server->outputs, link) {
+               output_damage_surface(output, view->surface, view->x, view->y,
+                       true);
+       }
+}
index 534966a04e6a13a0add60dfd856d2588b01dc1bb..6a7bf2bc3f998aabd21ce21cf749857c32dd9878 100644 (file)
@@ -148,6 +148,7 @@ desktop_cycle_view(struct server *server, struct view *current)
        do {
                view = wl_container_of(view->link.next, view, link);
        } while (&view->link == &server->views || !isfocusable(view));
+       damage_all_outputs(server);
        return view;
 }
 
index 267d7fca5a5a3f4b10d26587ddf19a4a33e4863c..3c90950fc49f912eee7898d660dcbe280faebe29 100644 (file)
@@ -49,6 +49,7 @@ keyboard_key_notify(struct wl_listener *listener, void *data)
                wlr_keyboard_get_modifiers(device->keyboard);
 
        if (server->cycle_view) {
+               damage_all_outputs(server);
                if ((syms[0] == XKB_KEY_Alt_L) &&
                    event->state == WL_KEYBOARD_KEY_STATE_RELEASED) {
                        /* end cycle */
index 5041e4b1d513e0ce70d9c445c8df906a712ac0bf..d7651e78481afcb95c6fcd3d63d8d8bf1accb455 100644 (file)
@@ -258,6 +258,7 @@ surface_commit_notify(struct wl_listener *listener, void *data)
                wl_container_of(listener, layer, surface_commit);
        struct wlr_output *wlr_output = layer->layer_surface->output;
        arrange_layers(output_from_wlr_output(layer->server, wlr_output));
+       damage_all_outputs(layer->server);
 }
 
 static void
@@ -267,6 +268,7 @@ unmap(struct lab_layer_surface *layer)
        if (seat->focused_layer == layer->layer_surface) {
                seat_set_focus_layer(seat, NULL);
        }
+       damage_all_outputs(layer->server);
 }
 
 static void
index 7251875652e3a1a0204dfa1c6af7395b06999b3f..a21999715d8cf8d40a6bb979b1eb4ee462f7ebf5 100644 (file)
@@ -1,6 +1,7 @@
 labwc_sources = files(
   'action.c',
   'cursor.c',
+  'damage.c',
   'deco.c',
   'desktop.c',
   'interactive.c',
index 99cec3776ca692b228700183eb77c15c44cb4510..d4b99534dcf3be343bb2d413f6a05cd20108dac6 100644 (file)
+/*
+ * output.c: labwc output and rendering
+ *
+ * Copyright (C) 2019-2021 Johan Malm
+ * Copyright (C) 2020 The Sway authors
+ */
+
 #define _POSIX_C_SOURCE 200809L
 #include "config.h"
 #include <wlr/types/wlr_xdg_output_v1.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/util/region.h>
 #include "labwc.h"
 #include "menu/menu.h"
 #include "theme/theme.h"
 #include "layers.h"
 
-struct draw_data {
-       struct wlr_output *output;
-       struct wlr_output_layout *output_layout;
-       struct wlr_renderer *renderer;
-       float *transform_matrix;
-       float *rgba;
+//#define DEBUG 1
+
+typedef void (*surface_iterator_func_t)(struct output *output,
+               struct wlr_surface *surface, struct wlr_box *box,
+               void *user_data);
+
+struct surface_iterator_data {
+       surface_iterator_func_t user_iterator;
+       void *user_data;
+       struct output *output;
+       double ox, oy;
 };
 
+static bool
+intersects_with_output(struct output *output,
+               struct wlr_output_layout *output_layout,
+               struct wlr_box *surface_box)
+{
+       /* The resolution can change if outputs are rotated */
+       struct wlr_box output_box = {0};
+       wlr_output_effective_resolution(output->wlr_output, &output_box.width,
+               &output_box.height);
+       struct wlr_box intersection;
+       return wlr_box_intersection(&intersection, &output_box, surface_box);
+}
+
 static void
-draw_rect(struct draw_data *d, struct wlr_box box)
+output_for_each_surface_iterator(struct wlr_surface *surface, int sx, int sy,
+               void *user_data)
 {
-       double ox = 0, oy = 0;
-       wlr_output_layout_output_coords(d->output_layout, d->output, &ox, &oy);
-       box.x += ox;
-       box.y += oy;
-       wlr_render_rect(d->renderer, &box, d->rgba, d->transform_matrix);
+       struct surface_iterator_data *data = user_data;
+       struct output *output = data->output;
+       if (!surface || !wlr_surface_has_buffer(surface)) {
+               return;
+       }
+       struct wlr_box surface_box = {
+               .x = data->ox + sx + surface->sx,
+               .y = data->oy + sy + surface->sy,
+               .width = surface->current.width,
+               .height = surface->current.height,
+       };
+
+       if (!intersects_with_output(output, output->server->output_layout,
+                                   &surface_box)) {
+               return;
+       }
+       data->user_iterator(data->output, surface, &surface_box,
+               data->user_data);
+}
+
+void
+output_surface_for_each_surface(struct output *output,
+               struct wlr_surface *surface, double ox, double oy,
+               surface_iterator_func_t iterator, void *user_data)
+{
+       struct surface_iterator_data data = {
+               .user_iterator = iterator,
+               .user_data = user_data,
+               .output = output,
+               .ox = ox,
+               .oy = oy,
+       };
+       if (!surface) {
+               return;
+       }
+       wlr_surface_for_each_surface(surface,
+               output_for_each_surface_iterator, &data);
+}
+
+struct render_data {
+       pixman_region32_t *damage;
+};
+
+int
+scale_length(int length, int offset, float scale)
+{
+       return round((offset + length) * scale) - round(offset * scale);
+}
+
+void
+scale_box(struct wlr_box *box, float scale)
+{
+       box->width = scale_length(box->width, box->x, scale);
+       box->height = scale_length(box->height, box->y, scale);
+       box->x = round(box->x * scale);
+       box->y = round(box->y * scale);
 }
 
 static void
-draw_line(struct draw_data *d, int x1, int y1, int x2, int y2)
+scissor_output(struct wlr_output *output, pixman_box32_t *rect)
 {
+       struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend);
+
        struct wlr_box box = {
-               .x = x1,
-               .y = y1,
-               .width = abs(x2 - x1) + 1,
-               .height = abs(y2 - y1) + 1,
+               .x = rect->x1,
+               .y = rect->y1,
+               .width = rect->x2 - rect->x1,
+               .height = rect->y2 - rect->y1,
        };
-       wlr_render_rect(d->renderer, &box, d->rgba, d->transform_matrix);
+
+       int output_width, output_height;
+       wlr_output_transformed_resolution(output, &output_width, &output_height);
+       enum wl_output_transform transform =
+               wlr_output_transform_invert(output->transform);
+       wlr_box_transform(&box, &box, transform, output_width, output_height);
+
+       wlr_renderer_scissor(renderer, &box);
+}
+
+static void
+render_texture(struct wlr_output *wlr_output, pixman_region32_t *output_damage,
+               struct wlr_texture *texture, const struct wlr_box *box,
+               const float matrix[static 9])
+{
+       struct wlr_renderer *renderer =
+               wlr_backend_get_renderer(wlr_output->backend);
+
+       pixman_region32_t damage;
+       pixman_region32_init(&damage);
+       pixman_region32_union_rect(&damage, &damage, box->x, box->y,
+               box->width, box->height);
+       pixman_region32_intersect(&damage, &damage, output_damage);
+       if (!pixman_region32_not_empty(&damage)) {
+               goto damage_finish;
+       }
+
+       int nrects;
+       pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
+       for (int i = 0; i < nrects; i++) {
+               scissor_output(wlr_output, &rects[i]);
+               wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0f);
+       }
+
+damage_finish:
+       pixman_region32_fini(&damage);
 }
 
-static void draw_rect_unfilled(struct draw_data *d, struct wlr_box box)
+static void
+render_surface_iterator(struct output *output, struct wlr_surface *surface,
+               struct wlr_box *box, void *user_data)
 {
-       draw_line(d, box.x, box.y, box.x + box.width - 1, box.y);
-       draw_line(d, box.x + box.width - 1, box.y, box.x + box.width - 1,
-               box.y + box.height - 1);
-       draw_line(d, box.x, box.y + box.height - 1, box.x + box.width - 1,
-               box.y + box.height - 1);
-       draw_line(d, box.x, box.y, box.x, box.y + box.height - 1);
+       struct render_data *data = user_data;
+       struct wlr_output *wlr_output = output->wlr_output;
+       pixman_region32_t *output_damage = data->damage;
+
+       struct wlr_texture *texture = wlr_surface_get_texture(surface);
+       if (!texture) {
+               wlr_log(WLR_DEBUG, "Cannot obtain surface texture");
+               return;
+       }
+
+       scale_box(box, wlr_output->scale);
+
+       float matrix[9];
+       enum wl_output_transform transform =
+               wlr_output_transform_invert(surface->current.transform);
+       wlr_matrix_project_box(matrix, box, transform, 0.0f,
+               wlr_output->transform_matrix);
+
+       render_texture(wlr_output, output_damage, texture, box, matrix);
+}
+
+#if HAVE_XWAYLAND
+void
+output_unmanaged_for_each_surface(struct output *output,
+               struct wl_list *unmanaged, surface_iterator_func_t iterator,
+               void *user_data)
+{
+       struct xwayland_unmanaged *unmanaged_surface;
+       wl_list_for_each(unmanaged_surface, unmanaged, link) {
+               struct wlr_xwayland_surface *xsurface =
+                       unmanaged_surface->xwayland_surface;
+               double ox = unmanaged_surface->lx, oy = unmanaged_surface->ly;
+               wlr_output_layout_output_coords(
+                       output->server->output_layout, output->wlr_output, &ox, &oy);
+               output_surface_for_each_surface(output, xsurface->surface, ox, oy,
+                       iterator, user_data);
+       }
+}
+
+static void render_unmanaged(struct output *output, pixman_region32_t *damage,
+               struct wl_list *unmanaged) {
+       struct render_data data = {
+               .damage = damage,
+       };
+       output_unmanaged_for_each_surface(output, unmanaged,
+               render_surface_iterator, &data);
+}
+#endif
+
+static void
+output_view_for_each_surface(struct output *output, struct view *view,
+               surface_iterator_func_t iterator, void *user_data)
+{
+       struct surface_iterator_data data = {
+               .user_iterator = iterator,
+               .user_data = user_data,
+               .output = output,
+               .ox = view->x,
+               .oy = view->y,
+       };
+
+       wlr_output_layout_output_coords(output->server->output_layout,
+               output->wlr_output, &data.ox, &data.oy);
+       view_for_each_surface(view, output_for_each_surface_iterator, &data);
+}
+
+void
+output_view_for_each_popup(struct output *output, struct view *view,
+               surface_iterator_func_t iterator, void *user_data)
+{
+       struct surface_iterator_data data = {
+               .user_iterator = iterator,
+               .user_data = user_data,
+               .output = output,
+               .ox = view->x,
+               .oy = view->y,
+       };
+
+       wlr_output_layout_output_coords(output->server->output_layout,
+               output->wlr_output, &data.ox, &data.oy);
+       view_for_each_popup(view, output_for_each_surface_iterator, &data);
+}
+
+/* for sending frame done */
+void output_layer_for_each_surface(struct output *output,
+               struct wl_list *layer_surfaces, surface_iterator_func_t iterator,
+               void *user_data) {
+       struct lab_layer_surface *layer_surface;
+       wl_list_for_each(layer_surface, layer_surfaces, link) {
+               struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
+                       layer_surface->layer_surface;
+               output_surface_for_each_surface(output, wlr_layer_surface_v1->surface,
+                       layer_surface->geo.x, layer_surface->geo.y, iterator,
+                       user_data);
+               /* TODO: handle popups */
+       }
+}
+
+static void
+output_for_each_surface(struct output *output, surface_iterator_func_t iterator,
+               void *user_data)
+{
+       output_layer_for_each_surface(output,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND],
+               iterator, user_data);
+       output_layer_for_each_surface(output,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM],
+               iterator, user_data);
+
+       struct view *view;
+       wl_list_for_each_reverse(view, &output->server->views, link) {
+               if (!view->mapped) {
+                       continue;
+               }
+               output_view_for_each_surface(output, view, iterator, user_data);
+       }
+
+#if HAVE_XWAYLAND
+       output_unmanaged_for_each_surface(output, &output->server->unmanaged_surfaces,
+               iterator, user_data);
+#endif
+
+       output_layer_for_each_surface(output,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP],
+               iterator, user_data);
+       output_layer_for_each_surface(output,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY],
+               iterator, user_data);
+}
+
+struct send_frame_done_data {
+       struct timespec when;
+};
+
+static void
+send_frame_done_iterator(struct output *output, struct wlr_surface *surface,
+               struct wlr_box *box, void *user_data)
+{
+       struct send_frame_done_data *data = user_data;
+       wlr_surface_send_frame_done(surface, &data->when);
+}
+
+static void
+send_frame_done(struct output *output, struct send_frame_done_data *data)
+{
+       output_for_each_surface(output, send_frame_done_iterator, data);
+}
+
+void
+render_rect(struct output *output, pixman_region32_t *output_damage,
+               const struct wlr_box *_box, float color[static 4])
+{
+       struct wlr_output *wlr_output = output->wlr_output;
+       struct wlr_renderer *renderer =
+               wlr_backend_get_renderer(wlr_output->backend);
+
+       struct wlr_box box;
+       memcpy(&box, _box, sizeof(struct wlr_box));
+
+       pixman_region32_t damage;
+       pixman_region32_init(&damage);
+       pixman_region32_union_rect(&damage, &damage, box.x, box.y,
+               box.width, box.height);
+       pixman_region32_intersect(&damage, &damage, output_damage);
+       bool damaged = pixman_region32_not_empty(&damage);
+       if (!damaged) {
+               goto damage_finish;
+       }
+
+       int nrects;
+       pixman_box32_t *rects = pixman_region32_rectangles(&damage, &nrects);
+       for (int i = 0; i < nrects; ++i) {
+               scissor_output(wlr_output, &rects[i]);
+               wlr_render_rect(renderer, &box, color,
+                       wlr_output->transform_matrix);
+       }
+
+damage_finish:
+       pixman_region32_fini(&damage);
+}
+
+void
+render_rect_unfilled(struct output *output, pixman_region32_t *output_damage,
+               const struct wlr_box *_box, float color[static 4])
+{
+       struct wlr_box box;
+       memcpy(&box, _box, sizeof(struct wlr_box));
+       box.height = 1;
+       render_rect(output, output_damage, &box, color);
+       box.y += _box->height - 1;
+       render_rect(output, output_damage, &box, color);
+       memcpy(&box, _box, sizeof(struct wlr_box));
+       box.width = 1;
+       render_rect(output, output_damage, &box, color);
+       box.x += _box->width - 1;
+       render_rect(output, output_damage, &box, color);
 }
 
 static void
@@ -56,105 +364,61 @@ shrink(struct wlr_box *box, int size)
 }
 
 static void
-render_cycle_box(struct output *output)
+render_cycle_box(struct output *output, pixman_region32_t *output_damage,
+               struct view *view)
 {
        struct wlr_output_layout *layout = output->server->output_layout;
        double ox = 0, oy = 0;
        struct wlr_box box;
-       if (!output->server->cycle_view)
-               return;
-       struct view *view;
-       wl_list_for_each_reverse (view, &output->server->views, link) {
-               if (view == output->server->cycle_view)
-                       goto render_it;
-       }
-       return;
-render_it:
        wlr_output_layout_output_coords(layout, output->wlr_output, &ox, &oy);
        box.x = view->x - view->margin.left + ox;
        box.y = view->y - view->margin.top + oy;
        box.width = view->w + view->margin.left + view->margin.right;
        box.height = view->h + view->margin.top + view->margin.bottom;
-       struct draw_data dd = {
-               .renderer = view->server->renderer,
-               .transform_matrix = output->wlr_output->transform_matrix,
-       };
-       dd.rgba = (float[4]){ 1.0, 1.0, 1.0, 1.0 };
-       draw_rect_unfilled(&dd, box);
-       dd.rgba = (float[4]){ 0.0, 0.0, 0.0, 1.0 };
+
+       float white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+       float black[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
+       render_rect_unfilled(output, output_damage, &box, white);
+
        for (int i = 0; i < 4; i++) {
                shrink(&box, 1);
-               draw_rect_unfilled(&dd, box);
+               render_rect_unfilled(output, output_damage, &box, black);
        }
-       dd.rgba = (float[4]){ 1.0, 1.0, 1.0, 1.0 };
        shrink(&box, 1);
-       draw_rect_unfilled(&dd, box);
+       render_rect_unfilled(output, output_damage, &box, white);
 }
 
 static void
-render_rootmenu(struct output *output)
+render_icon(struct output *output, pixman_region32_t *output_damage,
+               struct wlr_box *box, struct wlr_texture *texture)
 {
-       struct server *server = output->server;
-       struct draw_data ddata = {
-               .renderer = server->renderer,
-               .transform_matrix = output->wlr_output->transform_matrix,
-       };
-       float matrix[9];
-
-       struct wlr_output_layout *output_layout = server->output_layout;
-       double ox = 0, oy = 0;
-       wlr_output_layout_output_coords(output_layout, output->wlr_output, &ox, &oy);
-       ddata.rgba = (float[4]){ 0.9, 0.3, 0.3, 0.5 };
-       struct menuitem *menuitem;
-       wl_list_for_each (menuitem, &server->rootmenu->menuitems, link) {
-               struct wlr_texture *t;
-               t = menuitem->selected ? menuitem->active_texture :
-                       menuitem->inactive_texture;
-               struct wlr_box box = {
-                       .x = menuitem->geo_box.x + ox,
-                       .y = menuitem->geo_box.y + oy,
-                       .width = menuitem->geo_box.width,
-                       .height = menuitem->geo_box.height,
-               };
-               wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL,
-                       0, ddata.transform_matrix);
-               wlr_render_texture_with_matrix(ddata.renderer, t, matrix, 1);
-       }
-
-}
-
-static void
-render_icon(struct draw_data *d, struct wlr_box box,
-           struct wlr_texture *texture)
-{
-       if (!texture)
-               return;
-       float matrix[9];
-
        /* centre-align icon if smaller than designated box */
        struct wlr_box button;
        wlr_texture_get_size(texture, &button.width, &button.height);
-       if (box.width > button.width) {
-               button.x = box.x + (box.width - button.width) / 2;
+       if (box->width > button.width) {
+               button.x = box->x + (box->width - button.width) / 2;
        } else {
-               button.x = box.x;
-               button.width = box.width;
+               button.x = box->x;
+               button.width = box->width;
        }
-       if (box.height > button.height) {
-               button.y = box.y + (box.height - button.height) / 2;
+       if (box->height > button.height) {
+               button.y = box->y + (box->height - button.height) / 2;
        } else {
-               button.y = box.y;
-               button.height = box.height;
+               button.y = box->y;
+               button.height = box->height;
        }
 
        double ox = 0, oy = 0;
-       wlr_output_layout_output_coords(d->output_layout, d->output, &ox, &oy);
+       wlr_output_layout_output_coords(output->server->output_layout,
+               output->wlr_output, &ox, &oy);
        button.x += ox;
        button.y += oy;
 
+       float matrix[9];
        wlr_matrix_project_box(matrix, &button, WL_OUTPUT_TRANSFORM_NORMAL, 0,
-                              d->transform_matrix);
-       wlr_render_texture_with_matrix(d->renderer, texture, matrix, 1);
+               output->wlr_output->transform_matrix);
+       render_texture(output->wlr_output, output_damage, texture, &button,
+               matrix);
 }
 
 static bool
@@ -166,251 +430,359 @@ isbutton(enum deco_part deco_part)
 }
 
 static void
-render_decorations(struct wlr_output *output, struct view *view)
+render_deco(struct view *view, struct output *output,
+               pixman_region32_t *output_damage)
 {
-       if (!view->server_side_deco)
+       if (!view->server_side_deco) {
                return;
-       struct draw_data ddata = {
-               .output = output,
-               .output_layout = view->server->output_layout,
-               .renderer = view->server->renderer,
-               .transform_matrix = output->transform_matrix,
-       };
+       }
 
-       /* border */
-       ddata.rgba = theme.window_active_handle_bg_color;
-       draw_rect(&ddata, deco_box(view, LAB_DECO_PART_TOP));
-       draw_rect(&ddata, deco_box(view, LAB_DECO_PART_RIGHT));
-       draw_rect(&ddata, deco_box(view, LAB_DECO_PART_BOTTOM));
-       draw_rect(&ddata, deco_box(view, LAB_DECO_PART_LEFT));
+       /* render border */
+       float *color = theme.window_active_handle_bg_color;
+       enum deco_part border[4] = {
+               LAB_DECO_PART_TOP,
+               LAB_DECO_PART_RIGHT,
+               LAB_DECO_PART_BOTTOM,
+               LAB_DECO_PART_LEFT,
+       };
+       for (int i = 0; i < 4; i++) {
+               struct wlr_box box = deco_box(view, border[i]);
+               render_rect(output, output_damage, &box, color);
+       }
 
-       /* title */
+       /* render title */
        struct wlr_seat *seat = view->server->seat.seat;
-       if (view->surface == seat->keyboard_state.focused_surface)
-               ddata.rgba = theme.window_active_title_bg_color;
-       else
-               ddata.rgba = theme.window_inactive_title_bg_color;
-       draw_rect(&ddata, deco_box(view, LAB_DECO_PART_TITLE));
+       if (view->surface == seat->keyboard_state.focused_surface) {
+               color = theme.window_active_title_bg_color;
+       } else {
+               color = theme.window_inactive_title_bg_color;
+       }
+       struct wlr_box box = deco_box(view, LAB_DECO_PART_TITLE);
+       render_rect(output, output_damage, &box, color);
 
        /* button background */
        struct wlr_cursor *cur = view->server->seat.cursor;
        enum deco_part deco_part = deco_at(view, cur->x, cur->y);
-
-       struct wlr_box box = deco_box(view, deco_part);
+       box = deco_box(view, deco_part);
        if (isbutton(deco_part) &&
                        wlr_box_contains_point(&box, cur->x, cur->y)) {
-               ddata.rgba = (float[4]){ 0.5, 0.5, 0.5, 0.5 };
-               draw_rect(&ddata, deco_box(view, deco_part));
+               color = (float[4]){ 0.5, 0.5, 0.5, 0.5 };
+               render_rect(output, output_damage, &box, color);
        }
 
        /* buttons */
        if (view->surface == seat->keyboard_state.focused_surface) {
-               render_icon(&ddata, deco_box(view, LAB_DECO_BUTTON_CLOSE),
-                           theme.xbm_close_active_unpressed);
-               render_icon(&ddata, deco_box(view, LAB_DECO_BUTTON_MAXIMIZE),
-                           theme.xbm_maximize_active_unpressed);
-               render_icon(&ddata, deco_box(view, LAB_DECO_BUTTON_ICONIFY),
-                           theme.xbm_iconify_active_unpressed);
+               box = deco_box(view, LAB_DECO_BUTTON_CLOSE);
+               render_icon(output, output_damage, &box,
+                       theme.xbm_close_active_unpressed);
+               box = deco_box(view, LAB_DECO_BUTTON_MAXIMIZE);
+               render_icon(output, output_damage, &box,
+                       theme.xbm_maximize_active_unpressed);
+               box = deco_box(view, LAB_DECO_BUTTON_ICONIFY);
+               render_icon(output, output_damage, &box,
+                       theme.xbm_iconify_active_unpressed);
        } else {
-               render_icon(&ddata, deco_box(view, LAB_DECO_BUTTON_CLOSE),
-                           theme.xbm_close_inactive_unpressed);
-               render_icon(&ddata, deco_box(view, LAB_DECO_BUTTON_MAXIMIZE),
-                           theme.xbm_maximize_inactive_unpressed);
-               render_icon(&ddata, deco_box(view, LAB_DECO_BUTTON_ICONIFY),
-                           theme.xbm_iconify_inactive_unpressed);
+               box = deco_box(view, LAB_DECO_BUTTON_CLOSE);
+               render_icon(output, output_damage, &box,
+                       theme.xbm_close_inactive_unpressed);
+               box = deco_box(view, LAB_DECO_BUTTON_MAXIMIZE);
+               render_icon(output, output_damage, &box,
+                       theme.xbm_maximize_inactive_unpressed);
+               box = deco_box(view, LAB_DECO_BUTTON_ICONIFY);
+               render_icon(output, output_damage, &box,
+                       theme.xbm_iconify_inactive_unpressed);
        }
 }
 
-struct render_data_layer {
-       struct lab_layer_surface *layer_surface;
-       struct timespec *now;
-};
-
 static void
-render_layer_surface(struct wlr_surface *surface, int sx, int sy, void *data)
+render_rootmenu(struct output *output, pixman_region32_t *output_damage)
 {
-       struct render_data_layer *context = data;
-       struct lab_layer_surface *layer_surface = context->layer_surface;
-       struct wlr_texture *texture = wlr_surface_get_texture(surface);
-       if (texture == NULL) {
-               return;
-       }
-       struct wlr_output *output = layer_surface->layer_surface->output;
-       double ox = 0, oy = 0;
-       wlr_output_layout_output_coords(
-                       layer_surface->server->output_layout, output, &ox, &oy);
-       ox += layer_surface->geo.x + sx, oy += layer_surface->geo.y + sy;
+       struct server *server = output->server;
        float matrix[9];
-       enum wl_output_transform transform =
-               wlr_output_transform_invert(surface->current.transform);
-       struct wlr_box box;
-       memcpy(&box, &layer_surface->geo, sizeof(struct wlr_box));
-       wlr_matrix_project_box(matrix, &box, transform, 0,
-               output->transform_matrix);
-       wlr_render_texture_with_matrix(layer_surface->server->renderer,
-                       texture, matrix, 1);
-       wlr_surface_send_frame_done(surface, context->now);
+
+       struct wlr_output_layout *output_layout = server->output_layout;
+       double ox = 0, oy = 0;
+       wlr_output_layout_output_coords(output_layout, output->wlr_output,
+               &ox, &oy);
+       struct menuitem *menuitem;
+       wl_list_for_each (menuitem, &server->rootmenu->menuitems, link) {
+               struct wlr_texture *t;
+               t = menuitem->selected ? menuitem->active_texture :
+                       menuitem->inactive_texture;
+               struct wlr_box box = {
+                       .x = menuitem->geo_box.x + ox,
+                       .y = menuitem->geo_box.y + oy,
+                       .width = menuitem->geo_box.width,
+                       .height = menuitem->geo_box.height,
+               };
+               wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL,
+                       0, output->wlr_output->transform_matrix);
+               render_texture(output->wlr_output, output_damage, t,
+                       &box, matrix);
+       }
 }
 
-static void
-render_layer(struct timespec *now, struct wl_list *layer_surfaces)
+void output_layer_for_each_surface_toplevel(struct output *output,
+               struct wl_list *layer_surfaces, surface_iterator_func_t iterator,
+               void *user_data)
 {
-       struct render_data_layer context = {
-               .now = now,
-       };
        struct lab_layer_surface *layer_surface;
        wl_list_for_each(layer_surface, layer_surfaces, link) {
                struct wlr_layer_surface_v1 *wlr_layer_surface_v1 =
                        layer_surface->layer_surface;
-               context.layer_surface = layer_surface;
-               wlr_surface_for_each_surface(wlr_layer_surface_v1->surface,
-                       render_layer_surface, &context);
+               output_surface_for_each_surface(output,
+                       wlr_layer_surface_v1->surface, layer_surface->geo.x,
+                       layer_surface->geo.y, iterator, user_data);
        }
 }
 
-struct render_data {
-       struct wlr_output *output;
-       struct wlr_output_layout *output_layout;
-       struct wlr_renderer *renderer;
-       int lx, ly;
-       struct timespec *when;
-};
+static void render_layer_toplevel(struct output *output,
+               pixman_region32_t *damage, struct wl_list *layer_surfaces) {
+       struct render_data data = {
+               .damage = damage,
+       };
+       output_layer_for_each_surface_toplevel(output, layer_surfaces,
+               render_surface_iterator, &data);
+}
 
 static void
-render_surface(struct wlr_surface *surface, int sx, int sy, void *data)
+render_view_toplevels(struct view *view, struct output *output,
+               pixman_region32_t *damage)
 {
-       struct render_data *rdata = data;
-
-       struct wlr_texture *texture = wlr_surface_get_texture(surface);
-       if (!texture) {
-               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(
-               rdata->output_layout, rdata->output, &ox, &oy);
-       ox += rdata->lx + sx;
-       oy += rdata->ly + sy;
-
-       struct wlr_box box = {
-               .x = ox * rdata->output->scale,
-               .y = oy * rdata->output->scale,
-               .width = surface->current.width * rdata->output->scale,
-               .height = surface->current.height * rdata->output->scale,
+       struct render_data data = {
+               .damage = damage,
        };
+       double ox = view->x;
+       double oy = view->y;
+       wlr_output_layout_output_coords(output->server->output_layout,
+               output->wlr_output, &ox, &oy);
+       output_surface_for_each_surface(output, view->surface, ox, oy,
+               render_surface_iterator, &data);
+}
 
-       float matrix[9];
-       enum wl_output_transform transform =
-               wlr_output_transform_invert(surface->current.transform);
-       wlr_matrix_project_box(matrix, &box, transform, 0,
-              rdata->output->transform_matrix);
+static void
+render_popup_iterator(struct output *output, struct wlr_surface *surface,
+               struct wlr_box *box, void *data)
+{
+       /* Render this popup's surface */
+       render_surface_iterator(output, surface, box, data);
 
-       wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1);
-       wlr_surface_send_frame_done(surface, rdata->when);
+       /* Render this popup's child toplevels */
+       output_surface_for_each_surface(output, surface, box->x, box->y,
+               render_surface_iterator, data);
 }
 
 static void
-output_frame_notify(struct wl_listener *listener, void *data)
+render_view_popups(struct view *view, struct output *output,
+               pixman_region32_t *damage)
 {
-       /*
-        * 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 output *output = wl_container_of(listener, output, frame);
-       struct wlr_renderer *renderer = output->server->renderer;
+       struct render_data data = {
+               .damage = damage,
+       };
+       output_view_for_each_popup(output, view, render_popup_iterator, &data);
+}
 
-       struct timespec now;
-       clock_gettime(CLOCK_MONOTONIC, &now);
+void
+output_render(struct output *output, pixman_region32_t *damage)
+{
+       struct server *server = output->server;
+       struct wlr_output *wlr_output = output->wlr_output;
 
-       /* wlr_output_attach_render makes the OpenGL context current. */
-       if (!wlr_output_attach_render(output->wlr_output, NULL)) {
+       struct wlr_renderer *renderer =
+               wlr_backend_get_renderer(wlr_output->backend);
+       if (!renderer) {
+               wlr_log(WLR_DEBUG, "no renderer");
                return;
        }
 
-       /* The "effective" resolution can change if you rotate your outputs. */
-       int width, height;
-       wlr_output_effective_resolution(output->wlr_output, &width, &height);
-
        /* Calls glViewport and some other GL sanity checks */
-       wlr_renderer_begin(renderer, width, height);
+       wlr_renderer_begin(renderer, wlr_output->width, wlr_output->height);
 
-       float color[4] = { 0.3, 0.3, 0.3, 1.0 };
-       wlr_renderer_clear(renderer, color);
+       if (!pixman_region32_not_empty(damage)) {
+               goto renderer_end;
+       }
 
-       render_layer(&now, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
-       render_layer(&now, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
+#ifdef DEBUG
+       wlr_renderer_clear(renderer, (float[]){0.2f, 0.0f, 0.0f, 1.0f});
+#endif
+
+       float color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
+       int nrects;
+       pixman_box32_t *rects = pixman_region32_rectangles(damage, &nrects);
+       for (int i = 0; i < nrects; i++) {
+               scissor_output(wlr_output, &rects[i]);
+               wlr_renderer_clear(renderer, color);
+       }
+
+       render_layer_toplevel(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]);
+       render_layer_toplevel(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]);
 
        struct view *view;
-       wl_list_for_each_reverse (view, &output->server->views, link) {
-               if (!view->mapped)
+       wl_list_for_each_reverse (view, &server->views, link) {
+               if (!view->mapped) {
                        continue;
+               }
+               render_deco(view, output, damage);
+               render_view_toplevels(view, output, damage);
+               render_view_popups(view, output, damage);
+       }
 
-               render_decorations(output->wlr_output, view);
+#if HAVE_XWAYLAND
+       render_unmanaged(output, damage, &output->server->unmanaged_surfaces);
+#endif
 
-               struct render_data rdata = {
-                       .output = output->wlr_output,
-                       .output_layout = output->server->output_layout,
-                       .lx = view->x,
-                       .ly = view->y,
-                       .renderer = renderer,
-                       .when = &now,
-               };
+       /* 'alt-tab' border */
+       if (output->server->cycle_view) {
+               render_cycle_box(output, damage, output->server->cycle_view);
+       }
+
+       render_layer_toplevel(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
+       render_layer_toplevel(output, damage,
+               &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
 
-               view_for_each_surface(view, render_surface, &rdata);
+       if (output->server->input_mode == LAB_INPUT_STATE_MENU) {
+               render_rootmenu(output, damage);
        }
 
-       /* If in cycle (alt-tab) mode, highlight selected view */
-       render_cycle_box(output);
+renderer_end:
+       /* Just in case hardware cursors not supported by GPU */
+       wlr_output_render_software_cursors(wlr_output, damage);
+       wlr_renderer_scissor(renderer, NULL);
+       wlr_renderer_end(renderer);
 
-#if HAVE_XWAYLAND
-       /* Render xwayland override_redirect surfaces */
-       struct xwayland_unmanaged *unmanaged;
-       wl_list_for_each_reverse (unmanaged,
-                                 &output->server->unmanaged_surfaces, link) {
-               struct render_data rdata = {
-                       .output = output->wlr_output,
-                       .output_layout = output->server->output_layout,
-                       .lx = unmanaged->lx,
-                       .ly = unmanaged->ly,
-                       .renderer = renderer,
-                       .when = &now,
-               };
+       int output_width, output_height;
+       wlr_output_transformed_resolution(wlr_output, &output_width,
+               &output_height);
 
-               struct wlr_surface *s = unmanaged->xwayland_surface->surface;
-               render_surface(s, 0, 0, &rdata);
-       }
+       pixman_region32_t frame_damage;
+       pixman_region32_init(&frame_damage);
+
+       enum wl_output_transform transform =
+               wlr_output_transform_invert(wlr_output->transform);
+       wlr_region_transform(&frame_damage, &output->damage->current,
+               transform, output_width, output_height);
+
+#ifdef DEBUG
+       pixman_region32_union_rect(&frame_damage, &frame_damage, 0, 0,
+               output_width, output_height);
 #endif
 
-       render_layer(&now, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP]);
-       render_layer(&now, &output->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]);
+       wlr_output_set_damage(wlr_output, &frame_damage);
+       pixman_region32_fini(&frame_damage);
 
-       if (output->server->input_mode == LAB_INPUT_STATE_MENU) {
-               render_rootmenu(output);
+       if (!wlr_output_commit(wlr_output)) {
+               wlr_log(WLR_ERROR, "could not commit output");
        }
+}
 
-       /* Just in case hardware cursors not supported by GPU */
-       wlr_output_render_software_cursors(output->wlr_output, NULL);
+static void
+damage_surface_iterator(struct output *output, struct wlr_surface *surface,
+               struct wlr_box *box, void *user_data)
+{
+       struct wlr_output *wlr_output = output->wlr_output;
+       bool whole = *(bool *) user_data;
+
+       scale_box(box, output->wlr_output->scale);
+
+       if (whole) {
+               wlr_output_damage_add_box(output->damage, box);
+       } else if (pixman_region32_not_empty(&surface->buffer_damage)) {
+               pixman_region32_t damage;
+               pixman_region32_init(&damage);
+               wlr_surface_get_effective_damage(surface, &damage);
+
+               wlr_region_scale(&damage, &damage, wlr_output->scale);
+               if (ceil(wlr_output->scale) > surface->current.scale) {
+                       wlr_region_expand(&damage, &damage,
+                               ceil(wlr_output->scale) - surface->current.scale);
+               }
+               pixman_region32_translate(&damage, box->x, box->y);
+               wlr_output_damage_add(output->damage, &damage);
+               pixman_region32_fini(&damage);
+       }
+}
 
-       wlr_renderer_end(renderer);
-       wlr_output_commit(output->wlr_output);
+void
+output_damage_surface(struct output *output, struct wlr_surface *surface,
+               double lx, double ly, bool whole)
+{
+       if (!output->wlr_output->enabled) {
+               return;
+       }
+
+       double ox = lx, oy = ly;
+       wlr_output_layout_output_coords(output->server->output_layout,
+               output->wlr_output, &ox, &oy);
+       output_surface_for_each_surface(output, surface, ox, oy,
+               damage_surface_iterator, &whole);
 }
 
 static void
-output_destroy_notify(struct wl_listener *listener, void *data)
+output_damage_frame_notify(struct wl_listener *listener, void *data)
 {
-        struct output *output = wl_container_of(listener, output, destroy);
+       struct output *output = wl_container_of(listener, output, damage_frame);
+
+       if (!output->wlr_output->enabled) {
+               return;
+       }
+
+       bool needs_frame;
+       pixman_region32_t damage;
+       pixman_region32_init(&damage);
+       if (!wlr_output_damage_attach_render(output->damage,
+                       &needs_frame, &damage)) {
+               return;
+       }
+
+       if (needs_frame) {
+               output_render(output, &damage);
+       } else {
+               wlr_output_rollback(output->wlr_output);
+       }
+       pixman_region32_fini(&damage);
+
+       struct send_frame_done_data frame_data = {0};
+       clock_gettime(CLOCK_MONOTONIC, &frame_data.when);
+       send_frame_done(output, &frame_data);
+}
+
+static void
+output_destroy(struct output *output)
+{
+       if (!output || output->server->output_layout || !output->wlr_output) {
+               return;
+       }
         wl_list_remove(&output->link);
-        wl_list_remove(&output->frame.link);
         wl_list_remove(&output->destroy.link);
+        wl_list_remove(&output->damage_frame.link);
+        wl_list_remove(&output->damage_destroy.link);
+
+       struct server *server = output->server;
+       wlr_output_layout_remove(server->output_layout, output->wlr_output);
        free(output);
 }
 
+static void
+output_damage_destroy_notify(struct wl_listener *listener, void *data)
+{
+       struct output *output = wl_container_of(listener, output, damage_destroy);
+       output_destroy(output);
+}
+
+static void
+output_destroy_notify(struct wl_listener *listener, void *data)
+{
+        struct output *output = wl_container_of(listener, output, destroy);
+       if (!output || !output->wlr_output || !output->damage) {
+               return;
+       }
+       wlr_output_damage_destroy(output->damage);
+       output_destroy(output);
+}
+
 static void
 new_output_notify(struct wl_listener *listener, void *data)
 {
@@ -432,12 +804,17 @@ new_output_notify(struct wl_listener *listener, void *data)
        struct output *output = calloc(1, sizeof(struct output));
        output->wlr_output = wlr_output;
        output->server = server;
-       output->frame.notify = output_frame_notify;
-       wl_signal_add(&wlr_output->events.frame, &output->frame);
+       output->damage = wlr_output_damage_create(wlr_output);
        wl_list_insert(&server->outputs, &output->link);
+
        output->destroy.notify = output_destroy_notify;
        wl_signal_add(&wlr_output->events.destroy, &output->destroy);
 
+       output->damage_frame.notify = output_damage_frame_notify;
+       wl_signal_add(&output->damage->events.frame, &output->damage_frame);
+       output->damage_destroy.notify = output_damage_destroy_notify;
+       wl_signal_add(&output->damage->events.destroy, &output->damage_destroy);
+
        wl_list_init(&output->layers[0]);
        wl_list_init(&output->layers[1]);
        wl_list_init(&output->layers[2]);
index a6ab8971e15e1fa3e41eeeae57c4ebbf8c2c8b86..c1e456bf261975141a783007ec0fb44bcec2f908 100644 (file)
@@ -39,3 +39,13 @@ view_for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator,
        view->impl->for_each_surface(view, iterator, user_data);
 }
 
+void
+view_for_each_popup(struct view *view, wlr_surface_iterator_func_t iterator,
+               void *data)
+{
+       if (!view->impl->for_each_popup) {
+               return;
+       }
+       view->impl->for_each_popup(view, iterator, data);
+}
+
index 180228ac5c04df265432f8f5d39ec4b9532c7272..ee4d478bd1795df5efe93b0288e19320e3fe4b62 100644 (file)
--- a/src/xdg.c
+++ b/src/xdg.c
@@ -52,6 +52,86 @@ xdg_toplevel_decoration(struct wl_listener *listener, void *data)
        xdg_deco_request_mode(&xdg_deco->request_mode, wlr_decoration);
 }
 
+static void
+handle_xdg_popup_commit(struct wl_listener *listener, void *data)
+{
+       struct xdg_popup *popup = wl_container_of(listener, popup, map);
+       /* TODO */
+}
+
+static void
+handle_xdg_popup_map(struct wl_listener *listener, void *data)
+{
+       struct xdg_popup *popup = wl_container_of(listener, popup, map);
+       damage_view_whole(popup->view);
+}
+
+static void
+handle_xdg_popup_unmap(struct wl_listener *listener, void *data)
+{
+       struct xdg_popup *popup = wl_container_of(listener, popup, unmap);
+       damage_view_whole(popup->view);
+}
+
+static void
+handle_xdg_popup_destroy(struct wl_listener *listener, void *data)
+{
+       struct xdg_popup *popup = wl_container_of(listener, popup, destroy);
+       wl_list_remove(&popup->destroy.link);
+       wl_list_remove(&popup->commit.link);
+       wl_list_remove(&popup->map.link);
+       wl_list_remove(&popup->unmap.link);
+       wl_list_remove(&popup->new_popup.link);
+       free(popup);
+}
+
+static void xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup);
+
+static void
+popup_handle_new_xdg_popup(struct wl_listener *listener, void *data)
+{
+       struct xdg_popup *popup = wl_container_of(listener, popup, new_popup);
+       struct wlr_xdg_popup *wlr_popup = data;
+       xdg_popup_create(popup->view, wlr_popup);
+}
+
+/*
+ * We need to pass view to this function for damage tracking.
+ * TODO: Could we just damage surface or whole output?
+ *       That would allow us to only have one 'handle_new_*'
+ */
+static void
+xdg_popup_create(struct view *view, struct wlr_xdg_popup *wlr_popup)
+{
+       struct xdg_popup *popup = calloc(1, sizeof(struct xdg_popup));
+       if (!popup) {
+               return;
+       }
+
+       popup->wlr_popup = wlr_popup;
+       popup->view = view;
+
+       popup->destroy.notify = handle_xdg_popup_destroy;
+       wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy);
+       popup->commit.notify = handle_xdg_popup_commit;
+       wl_signal_add(&wlr_popup->base->surface->events.commit, &popup->commit);
+       popup->map.notify = handle_xdg_popup_map;
+       wl_signal_add(&wlr_popup->base->events.map, &popup->map);
+       popup->unmap.notify = handle_xdg_popup_unmap;
+       wl_signal_add(&wlr_popup->base->events.unmap, &popup->unmap);
+       popup->new_popup.notify = popup_handle_new_xdg_popup;
+       wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup);
+}
+
+/* This is merely needed to track damage */
+static void
+handle_new_xdg_popup(struct wl_listener *listener, void *data)
+{
+       struct view *view = wl_container_of(listener, view, new_popup);
+       struct wlr_xdg_popup *wlr_popup = data;
+       xdg_popup_create(view, wlr_popup);
+}
+
 static bool
 has_ssd(struct view *view)
 {
@@ -95,6 +175,7 @@ handle_commit(struct wl_listener *listener, void *data)
                        view->pending_move_resize.configure_serial = 0;
                }
        }
+       damage_view_part(view);
 }
 
 static void
@@ -165,6 +246,7 @@ xdg_toplevel_view_configure(struct view *view, struct wlr_box geo)
        } else if (view->pending_move_resize.configure_serial == 0) {
                view->x = geo.x;
                view->y = geo.y;
+               damage_all_outputs(view->server);
        }
 }
 
@@ -173,6 +255,7 @@ xdg_toplevel_view_move(struct view *view, double x, double y)
 {
        view->x = x;
        view->y = y;
+       damage_all_outputs(view->server);
 }
 
 static void
@@ -181,6 +264,13 @@ xdg_toplevel_view_close(struct view *view)
        wlr_xdg_toplevel_send_close(view->xdg_surface);
 }
 
+static void
+xdg_toplevel_view_for_each_popup(struct view *view,
+               wlr_surface_iterator_func_t iterator, void *data)
+{
+       wlr_xdg_surface_for_each_popup(view->xdg_surface, iterator, data);
+}
+
 static void
 xdg_toplevel_view_for_each_surface(struct view *view,
                wlr_surface_iterator_func_t iterator, void *data)
@@ -234,12 +324,14 @@ xdg_toplevel_view_map(struct view *view)
        view->commit.notify = handle_commit;
 
        desktop_focus_view(&view->server->seat, view);
+       damage_all_outputs(view->server);
 }
 
 static void
 xdg_toplevel_view_unmap(struct view *view)
 {
        view->mapped = false;
+       damage_all_outputs(view->server);
        wl_list_remove(&view->commit.link);
        desktop_focus_topmost_mapped_view(view->server);
 }
@@ -247,6 +339,7 @@ xdg_toplevel_view_unmap(struct view *view)
 static const struct view_impl xdg_toplevel_view_impl = {
        .configure = xdg_toplevel_view_configure,
        .close = xdg_toplevel_view_close,
+       .for_each_popup = xdg_toplevel_view_for_each_popup,
        .for_each_surface = xdg_toplevel_view_for_each_surface,
        .map = xdg_toplevel_view_map,
        .move = xdg_toplevel_view_move,
@@ -277,6 +370,9 @@ xdg_surface_new(struct wl_listener *listener, void *data)
        view->destroy.notify = handle_destroy;
        wl_signal_add(&xdg_surface->events.destroy, &view->destroy);
 
+       view->new_popup.notify = handle_new_xdg_popup;
+       wl_signal_add(&xdg_surface->events.new_popup, &view->new_popup);
+
        struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel;
        view->request_move.notify = handle_request_move;
        wl_signal_add(&toplevel->events.request_move, &view->request_move);
index a9855d35dc8f27606c6b1370c837096198ce9d16..67c5acfccbcafb93f7fa3ce5e651418c8a9ac2d8 100644 (file)
@@ -19,6 +19,7 @@ unmanaged_handle_commit(struct wl_listener *listener, void *data)
        struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface;
        unmanaged->lx = xsurface->x;
        unmanaged->ly = xsurface->y;
+       damage_all_outputs(unmanaged->server);
 }
 
 static void
@@ -36,7 +37,7 @@ unmanaged_handle_map(struct wl_listener *listener, void *data)
 
        unmanaged->lx = xsurface->x;
        unmanaged->ly = xsurface->y;
-
+       damage_all_outputs(unmanaged->server);
        if (wlr_xwayland_or_surface_wants_focus(xsurface)) {
                seat_focus_surface(&unmanaged->server->seat, xsurface->surface);
        }
@@ -48,6 +49,7 @@ unmanaged_handle_unmap(struct wl_listener *listener, void *data)
        struct xwayland_unmanaged *unmanaged =
                wl_container_of(listener, unmanaged, unmap);
        struct wlr_xwayland_surface *xsurface = unmanaged->xwayland_surface;
+       damage_all_outputs(unmanaged->server);
        wl_list_remove(&unmanaged->link);
        wl_list_remove(&unmanaged->commit.link);
 
index 9b2b2d49bfe0c9efb810fdc3ddd118198eb1dc57..6697580d58332dc83cf15d489850fcb927a6caa2 100644 (file)
@@ -21,6 +21,7 @@ handle_commit(struct wl_listener *listener, void *data)
                        view->pending_move_resize.height - view->h;
                view->pending_move_resize.update_y = false;
        }
+       damage_view_whole(view);
 }
 
 static void
@@ -56,6 +57,7 @@ handle_request_configure(struct wl_listener *listener, void *data)
        struct wlr_xwayland_surface_configure_event *event = data;
        wlr_xwayland_surface_configure(view->xwayland_surface, event->x,
                                       event->y, event->width, event->height);
+       damage_all_outputs(view->server);
 }
 
 static void
@@ -70,6 +72,7 @@ configure(struct view *view, struct wlr_box geo)
        wlr_xwayland_surface_configure(view->xwayland_surface, (int16_t)geo.x,
                                       (int16_t)geo.y, (uint16_t)geo.width,
                                       (uint16_t)geo.height);
+       damage_all_outputs(view->server);
 }
 
 static void
@@ -80,18 +83,23 @@ move(struct view *view, double x, double y)
        struct wlr_xwayland_surface *s = view->xwayland_surface;
        wlr_xwayland_surface_configure(s, (int16_t)x, (int16_t)y,
                (uint16_t)s->width, (uint16_t)s->height);
+       damage_all_outputs(view->server);
 }
 
 static void
 _close(struct view *view)
 {
        wlr_xwayland_surface_close(view->xwayland_surface);
+       damage_all_outputs(view->server);
 }
 
 static void
 for_each_surface(struct view *view, wlr_surface_iterator_func_t iterator,
                void *data)
 {
+       if (!view->surface) {
+               return;
+       }
         wlr_surface_for_each_surface(view->surface, iterator, data);
 }
 
@@ -139,12 +147,14 @@ map(struct view *view)
        view->commit.notify = handle_commit;
 
        desktop_focus_view(&view->server->seat, view);
+       damage_all_outputs(view->server);
 }
 
 static void
 unmap(struct view *view)
 {
        view->mapped = false;
+       damage_all_outputs(view->server);
        wl_list_remove(&view->commit.link);
        desktop_focus_topmost_mapped_view(view->server);
 }