From 66ae4071a7985fbc6e5d41f50e34ce4474ab4fe4 Mon Sep 17 00:00:00 2001 From: emersion Date: Sun, 21 Jan 2018 21:06:37 +0100 Subject: [PATCH] rootston: damage tracking for xdg popups --- include/rootston/view.h | 11 +- include/wlr/types/wlr_xdg_shell_v6.h | 5 +- rootston/output.c | 297 +++++++++++++-------------- rootston/xdg_shell_v6.c | 69 ++++++- types/wlr_xdg_shell_v6.c | 22 +- 5 files changed, 235 insertions(+), 169 deletions(-) diff --git a/include/rootston/view.h b/include/rootston/view.h index d07f9ca2..8cfdf321 100644 --- a/include/rootston/view.h +++ b/include/rootston/view.h @@ -23,13 +23,15 @@ struct roots_wl_shell_surface { struct roots_xdg_surface_v6 { struct roots_view *view; - struct wl_listener commit; struct wl_listener destroy; + struct wl_listener new_popup; struct wl_listener request_move; struct wl_listener request_resize; struct wl_listener request_maximize; struct wl_listener request_fullscreen; + struct wl_listener surface_commit; + uint32_t pending_move_resize_configure_serial; }; @@ -133,6 +135,13 @@ struct roots_subsurface { struct wl_listener destroy; }; +struct roots_xdg_popup_v6 { + struct roots_view_child view_child; + struct wlr_xdg_popup_v6 *wlr_popup; + struct wl_listener destroy; + struct wl_listener new_popup; +}; + void view_get_box(const struct roots_view *view, struct wlr_box *box); void view_activate(struct roots_view *view, bool active); void view_move(struct roots_view *view, double x, double y); diff --git a/include/wlr/types/wlr_xdg_shell_v6.h b/include/wlr/types/wlr_xdg_shell_v6.h index 280bea27..c7b1a24b 100644 --- a/include/wlr/types/wlr_xdg_shell_v6.h +++ b/include/wlr/types/wlr_xdg_shell_v6.h @@ -34,6 +34,7 @@ struct wlr_xdg_client_v6 { struct wlr_xdg_popup_v6 { struct wlr_xdg_surface_v6 *base; + struct wl_list link; struct wl_resource *resource; bool committed; @@ -104,8 +105,7 @@ struct wlr_xdg_surface_v6 { struct wlr_xdg_popup_v6 *popup_state; }; - struct wl_list popups; - struct wl_list popup_link; + struct wl_list popups; // wlr_xdg_popup_v6::link bool configured; bool added; @@ -126,6 +126,7 @@ struct wlr_xdg_surface_v6 { struct { struct wl_signal destroy; struct wl_signal ping_timeout; + struct wl_signal new_popup; struct wl_signal request_maximize; struct wl_signal request_fullscreen; diff --git a/rootston/output.c b/rootston/output.c index 1c966e08..d2ea2ade 100644 --- a/rootston/output.c +++ b/rootston/output.c @@ -13,6 +13,9 @@ #include "rootston/output.h" #include "rootston/config.h" +typedef void (*surface_iterator_func_t)(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data); + /** * Rotate a child's position relative to a parent. The parent size is (pw, ph), * the child position is (*sx, *sy) and its size is (sw, sh). @@ -31,6 +34,119 @@ static void rotate_child_position(double *sx, double *sy, double sw, double sh, } } +static void surface_for_each_surface(struct wlr_surface *surface, double lx, + double ly, float rotation, surface_iterator_func_t iterator, + void *user_data) { + iterator(surface, lx, ly, rotation, user_data); + + struct wlr_subsurface *subsurface; + wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { + struct wlr_surface_state *state = subsurface->surface->current; + double sx = state->subsurface_position.x; + double sy = state->subsurface_position.y; + rotate_child_position(&sx, &sy, state->width, state->height, + surface->current->width, surface->current->height, rotation); + + surface_for_each_surface(subsurface->surface, lx + sx, ly + sy, + rotation, iterator, user_data); + } +} + +static void xdg_surface_v6_for_each_surface(struct wlr_xdg_surface_v6 *surface, + double base_x, double base_y, float rotation, + surface_iterator_func_t iterator, void *user_data) { + double width = surface->surface->current->width; + double height = surface->surface->current->height; + + struct wlr_xdg_popup_v6 *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface_v6 *popup = popup_state->base; + if (!popup->configured) { + continue; + } + + double popup_width = popup->surface->current->width; + double popup_height = popup->surface->current->height; + + double popup_sx, popup_sy; + wlr_xdg_surface_v6_popup_get_position(popup, &popup_sx, &popup_sy); + rotate_child_position(&popup_sx, &popup_sy, popup_width, popup_height, + width, height, rotation); + + surface_for_each_surface(popup->surface, base_x + popup_sx, + base_y + popup_sy, rotation, iterator, user_data); + xdg_surface_v6_for_each_surface(popup, base_x + popup_sx, + base_y + popup_sy, rotation, iterator, user_data); + } +} + +static void wl_shell_surface_for_each_surface( + struct wlr_wl_shell_surface *surface, double lx, double ly, + float rotation, bool is_child, surface_iterator_func_t iterator, + void *user_data) { + if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { + surface_for_each_surface(surface->surface, lx, ly, rotation, iterator, + user_data); + + double width = surface->surface->current->width; + double height = surface->surface->current->height; + + struct wlr_wl_shell_surface *popup; + wl_list_for_each(popup, &surface->popups, popup_link) { + double popup_width = popup->surface->current->width; + double popup_height = popup->surface->current->height; + + double popup_x = popup->transient_state->x; + double popup_y = popup->transient_state->y; + rotate_child_position(&popup_x, &popup_y, popup_width, popup_height, + width, height, rotation); + + wl_shell_surface_for_each_surface(popup, lx + popup_x, ly + popup_y, + rotation, true, iterator, user_data); + } + } +} + +static void view_for_each_surface(struct roots_view *view, + surface_iterator_func_t iterator, void *user_data) { + switch (view->type) { + case ROOTS_XDG_SHELL_V6_VIEW: + surface_for_each_surface(view->wlr_surface, view->x, view->y, + view->rotation, iterator, user_data); + xdg_surface_v6_for_each_surface(view->xdg_surface_v6, view->x, view->y, + view->rotation, iterator, user_data); + break; + case ROOTS_WL_SHELL_VIEW: + wl_shell_surface_for_each_surface(view->wl_shell_surface, view->x, + view->y, view->rotation, false, iterator, user_data); + break; + case ROOTS_XWAYLAND_VIEW: + surface_for_each_surface(view->wlr_surface, view->x, view->y, + view->rotation, iterator, user_data); + break; + } +} + +static void xwayland_children_for_each_surface( + struct wlr_xwayland_surface *surface, + surface_iterator_func_t iterator, void *user_data) { + struct wlr_xwayland_surface *child; + wl_list_for_each(child, &surface->children, parent_link) { + if (child->surface != NULL && child->added) { + surface_for_each_surface(child->surface, child->x, child->y, 0, + iterator, user_data); + } + xwayland_children_for_each_surface(child, iterator, user_data); + } +} + + +struct render_data { + struct roots_output *output; + struct timespec *when; + pixman_region32_t *damage; +}; + /** * Checks whether a surface at (lx, ly) intersects an output. Sets `box` to the * surface box in the output, in output-local coordinates. @@ -52,9 +168,13 @@ static bool surface_intersect_output(struct wlr_surface *surface, return wlr_output_layout_intersects(output_layout, wlr_output, &layout_box); } -static void render_surface(struct wlr_surface *surface, - struct roots_output *output, struct timespec *when, - pixman_region32_t *damage, double lx, double ly, float rotation) { +static void render_surface(struct wlr_surface *surface, double lx, double ly, + float rotation, void *_data) { + struct render_data *data = _data; + struct roots_output *output = data->output; + struct timespec *when = data->when; + pixman_region32_t *damage = data->damage; + if (!wlr_surface_has_buffer(surface)) { return; } @@ -63,7 +183,7 @@ static void render_surface(struct wlr_surface *surface, bool intersects = surface_intersect_output(surface, output->desktop->layout, output->wlr_output, lx, ly, &box); if (!intersects) { - goto render_subsurfaces; + return; } // TODO: output scale, output transform support @@ -135,107 +255,6 @@ static void render_surface(struct wlr_surface *surface, surface_damage_finish: pixman_region32_fini(&surface_damage); - -render_subsurfaces:; - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { - struct wlr_surface_state *state = subsurface->surface->current; - double sx = state->subsurface_position.x; - double sy = state->subsurface_position.y; - rotate_child_position(&sx, &sy, state->width, state->height, - surface->current->width, surface->current->height, rotation); - - render_surface(subsurface->surface, output, when, damage, - lx + sx, ly + sy, rotation); - } -} - -static void render_xdg_v6_popups(struct wlr_xdg_surface_v6 *surface, - struct roots_output *output, struct timespec *when, - pixman_region32_t *damage, double base_x, double base_y, - float rotation) { - double width = surface->surface->current->width; - double height = surface->surface->current->height; - - struct wlr_xdg_surface_v6 *popup; - wl_list_for_each(popup, &surface->popups, popup_link) { - if (!popup->configured) { - continue; - } - - double popup_width = popup->surface->current->width; - double popup_height = popup->surface->current->height; - - double popup_sx, popup_sy; - wlr_xdg_surface_v6_popup_get_position(popup, &popup_sx, &popup_sy); - rotate_child_position(&popup_sx, &popup_sy, popup_width, popup_height, - width, height, rotation); - - render_surface(popup->surface, output, when, damage, - base_x + popup_sx, base_y + popup_sy, rotation); - render_xdg_v6_popups(popup, output, when, damage, - base_x + popup_sx, base_y + popup_sy, rotation); - } -} - -static void render_wl_shell_surface(struct wlr_wl_shell_surface *surface, - struct roots_output *output, struct timespec *when, - pixman_region32_t *damage, double lx, double ly, float rotation, - bool is_child) { - if (is_child || surface->state != WLR_WL_SHELL_SURFACE_STATE_POPUP) { - render_surface(surface->surface, output, when, damage, lx, ly, - rotation); - - double width = surface->surface->current->width; - double height = surface->surface->current->height; - - struct wlr_wl_shell_surface *popup; - wl_list_for_each(popup, &surface->popups, popup_link) { - double popup_width = popup->surface->current->width; - double popup_height = popup->surface->current->height; - - double popup_x = popup->transient_state->x; - double popup_y = popup->transient_state->y; - rotate_child_position(&popup_x, &popup_y, popup_width, popup_height, - width, height, rotation); - - render_wl_shell_surface(popup, output, when, damage, - lx + popup_x, ly + popup_y, rotation, true); - } - } -} - -static void render_xwayland_children(struct wlr_xwayland_surface *surface, - struct roots_output *output, struct timespec *when, - pixman_region32_t *damage) { - struct wlr_xwayland_surface *child; - wl_list_for_each(child, &surface->children, parent_link) { - if (child->surface != NULL && child->added) { - render_surface(child->surface, output, when, damage, - child->x, child->y, 0); - } - render_xwayland_children(child, output, when, damage); - } -} - -static void render_view(struct roots_view *view, struct roots_output *output, - struct timespec *when, pixman_region32_t *damage) { - switch (view->type) { - case ROOTS_XDG_SHELL_V6_VIEW: - render_surface(view->wlr_surface, output, when, damage, - view->x, view->y, view->rotation); - render_xdg_v6_popups(view->xdg_surface_v6, output, when, damage, - view->x, view->y, view->rotation); - break; - case ROOTS_WL_SHELL_VIEW: - render_wl_shell_surface(view->wl_shell_surface, output, when, damage, - view->x, view->y, view->rotation, false); - break; - case ROOTS_XWAYLAND_VIEW: - render_surface(view->wlr_surface, output, when, damage, - view->x, view->y, view->rotation); - break; - } } static bool has_standalone_surface(struct roots_view *view) { @@ -325,7 +344,11 @@ static void render_output(struct roots_output *output) { goto damage_finish; } - wlr_log(L_DEBUG, "render"); + struct render_data data = { + .output = output, + .when = &now, + .damage = &damage, + }; wlr_renderer_begin(server->renderer, wlr_output); glEnable(GL_SCISSOR_TEST); @@ -353,14 +376,14 @@ static void render_output(struct roots_output *output) { goto renderer_end; } - render_view(view, output, &now, &damage); + view_for_each_surface(view, render_surface, &data); // During normal rendering the xwayland window tree isn't traversed // because all windows are rendered. Here we only want to render // the fullscreen window's children so we have to traverse the tree. if (view->type == ROOTS_XWAYLAND_VIEW) { - render_xwayland_children(view->xwayland_surface, output, &now, - &damage); + xwayland_children_for_each_surface(view->xwayland_surface, + render_surface, &data); } goto renderer_end; @@ -369,7 +392,7 @@ static void render_output(struct roots_output *output) { // Render all views struct roots_view *view; wl_list_for_each_reverse(view, &desktop->views, link) { - render_view(view, output, &now, &damage); + view_for_each_surface(view, render_surface, &data); } // Render drag icons @@ -386,14 +409,14 @@ static void render_output(struct roots_output *output) { if (drag_icon->is_pointer) { icon_x = cursor->x + drag_icon->sx; icon_y = cursor->y + drag_icon->sy; - render_surface(icon, output, &now, &damage, icon_x, icon_y, 0); + render_surface(icon, icon_x, icon_y, 0, &data); } else { struct wlr_touch_point *point = wlr_seat_touch_get_point(seat->seat, drag_icon->touch_id); if (point) { icon_x = seat->touch_x + drag_icon->sx; icon_y = seat->touch_y + drag_icon->sy; - render_surface(icon, output, &now, &damage, icon_x, icon_y, 0); + render_surface(icon, icon_x, icon_y, 0, &data); } } } @@ -443,8 +466,10 @@ static void output_damage_whole(struct roots_output *output) { schedule_render(output); } -static void output_damage_whole_surface(struct roots_output *output, - struct wlr_surface *surface, double lx, double ly, float rotation) { +static void damage_whole_surface(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data) { + struct roots_output *output = data; + if (!wlr_surface_has_buffer(surface)) { return; } @@ -460,18 +485,6 @@ static void output_damage_whole_surface(struct roots_output *output, box.x, box.y, box.width, box.height); schedule_render(output); - - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { - struct wlr_surface_state *state = subsurface->surface->current; - double sx = state->subsurface_position.x; - double sy = state->subsurface_position.y; - rotate_child_position(&sx, &sy, state->width, state->height, - surface->current->width, surface->current->height, rotation); - - output_damage_whole_surface(output, subsurface->surface, - lx + sx, ly + sy, rotation); - } } void output_damage_whole_view(struct roots_output *output, @@ -480,16 +493,13 @@ void output_damage_whole_view(struct roots_output *output, return; } - if (view->wlr_surface != NULL) { - output_damage_whole_surface(output, view->wlr_surface, - view->x, view->y, view->rotation); - } - - // TODO: popups, etc + view_for_each_surface(view, damage_whole_surface, output); } -static void output_damage_from_surface(struct roots_output *output, - struct wlr_surface *surface, double lx, double ly, float rotation) { +static void damage_from_surface(struct wlr_surface *surface, + double lx, double ly, float rotation, void *data) { + struct roots_output *output = data; + if (!wlr_surface_has_buffer(surface)) { return; } @@ -510,18 +520,6 @@ static void output_damage_from_surface(struct roots_output *output, pixman_region32_fini(&damage); schedule_render(output); - - struct wlr_subsurface *subsurface; - wl_list_for_each(subsurface, &surface->subsurface_list, parent_link) { - struct wlr_surface_state *state = subsurface->surface->current; - double sx = state->subsurface_position.x; - double sy = state->subsurface_position.y; - rotate_child_position(&sx, &sy, state->width, state->height, - surface->current->width, surface->current->height, rotation); - - output_damage_from_surface(output, subsurface->surface, - lx + sx, ly + sy, rotation); - } } void output_damage_from_view(struct roots_output *output, @@ -530,12 +528,7 @@ void output_damage_from_view(struct roots_output *output, return; } - if (view->wlr_surface != NULL) { - output_damage_from_surface(output, view->wlr_surface, - view->x, view->y, view->rotation); - } - - // TODO: popups, etc + view_for_each_surface(view, damage_from_surface, output); } static void output_handle_mode(struct wl_listener *listener, void *data) { diff --git a/rootston/xdg_shell_v6.c b/rootston/xdg_shell_v6.c index 7337fb1e..0a3ca72c 100644 --- a/rootston/xdg_shell_v6.c +++ b/rootston/xdg_shell_v6.c @@ -10,6 +10,52 @@ #include "rootston/server.h" #include "rootston/input.h" +static void popup_destroy(struct roots_view_child *child) { + assert(child->destroy == popup_destroy); + struct roots_xdg_popup_v6 *popup = (struct roots_xdg_popup_v6 *)child; + if (popup == NULL) { + return; + } + wl_list_remove(&popup->destroy.link); + wl_list_remove(&popup->new_popup.link); + view_child_finish(&popup->view_child); + free(popup); +} + +static void popup_handle_destroy(struct wl_listener *listener, void *data) { + struct roots_xdg_popup_v6 *popup = + wl_container_of(listener, popup, destroy); + popup_destroy((struct roots_view_child *)popup); +} + +static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view, + struct wlr_xdg_popup_v6 *wlr_popup); + +static void popup_handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_xdg_popup_v6 *popup = + wl_container_of(listener, popup, new_popup); + struct wlr_xdg_popup_v6 *wlr_popup = data; + popup_create(popup->view_child.view, wlr_popup); +} + +static struct roots_xdg_popup_v6 *popup_create(struct roots_view *view, + struct wlr_xdg_popup_v6 *wlr_popup) { + struct roots_xdg_popup_v6 *popup = + calloc(1, sizeof(struct roots_xdg_popup_v6)); + if (popup == NULL) { + return NULL; + } + popup->wlr_popup = wlr_popup; + popup->view_child.destroy = popup_destroy; + view_child_init(&popup->view_child, view, wlr_popup->base->surface); + popup->destroy.notify = popup_handle_destroy; + wl_signal_add(&wlr_popup->base->events.destroy, &popup->destroy); + popup->new_popup.notify = popup_handle_new_popup; + wl_signal_add(&wlr_popup->base->events.new_popup, &popup->new_popup); + return popup; +} + + static void get_size(const struct roots_view *view, struct wlr_box *box) { assert(view->type == ROOTS_XDG_SHELL_V6_VIEW); struct wlr_xdg_surface_v6 *surface = view->xdg_surface_v6; @@ -191,9 +237,9 @@ static void handle_request_fullscreen(struct wl_listener *listener, view_set_fullscreen(view, e->fullscreen, e->output); } -static void handle_commit(struct wl_listener *listener, void *data) { +static void handle_surface_commit(struct wl_listener *listener, void *data) { struct roots_xdg_surface_v6 *roots_surface = - wl_container_of(listener, roots_surface, commit); + wl_container_of(listener, roots_surface, surface_commit); struct roots_view *view = roots_surface->view; struct wlr_xdg_surface_v6 *surface = view->xdg_surface_v6; @@ -223,13 +269,23 @@ static void handle_commit(struct wl_listener *listener, void *data) { } } +static void handle_new_popup(struct wl_listener *listener, void *data) { + struct roots_xdg_surface_v6 *roots_xdg_surface = + wl_container_of(listener, roots_xdg_surface, new_popup); + struct wlr_xdg_popup_v6 *wlr_popup = data; + popup_create(roots_xdg_surface->view, wlr_popup); +} + static void handle_destroy(struct wl_listener *listener, void *data) { struct roots_xdg_surface_v6 *roots_xdg_surface = wl_container_of(listener, roots_xdg_surface, destroy); - wl_list_remove(&roots_xdg_surface->commit.link); + wl_list_remove(&roots_xdg_surface->surface_commit.link); wl_list_remove(&roots_xdg_surface->destroy.link); + wl_list_remove(&roots_xdg_surface->new_popup.link); wl_list_remove(&roots_xdg_surface->request_move.link); wl_list_remove(&roots_xdg_surface->request_resize.link); + wl_list_remove(&roots_xdg_surface->request_maximize.link); + wl_list_remove(&roots_xdg_surface->request_fullscreen.link); wl_list_remove(&roots_xdg_surface->view->link); view_finish(roots_xdg_surface->view); free(roots_xdg_surface->view); @@ -257,8 +313,9 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { if (!roots_surface) { return; } - roots_surface->commit.notify = handle_commit; - wl_signal_add(&surface->surface->events.commit, &roots_surface->commit); + roots_surface->surface_commit.notify = handle_surface_commit; + wl_signal_add(&surface->surface->events.commit, + &roots_surface->surface_commit); roots_surface->destroy.notify = handle_destroy; wl_signal_add(&surface->events.destroy, &roots_surface->destroy); roots_surface->request_move.notify = handle_request_move; @@ -272,6 +329,8 @@ void handle_xdg_shell_v6_surface(struct wl_listener *listener, void *data) { roots_surface->request_fullscreen.notify = handle_request_fullscreen; wl_signal_add(&surface->events.request_fullscreen, &roots_surface->request_fullscreen); + roots_surface->new_popup.notify = handle_new_popup; + wl_signal_add(&surface->events.new_popup, &roots_surface->new_popup); struct roots_view *view = calloc(1, sizeof(struct roots_view)); if (!view) { diff --git a/types/wlr_xdg_shell_v6.c b/types/wlr_xdg_shell_v6.c index 1c46197e..daf5f867 100644 --- a/types/wlr_xdg_shell_v6.c +++ b/types/wlr_xdg_shell_v6.c @@ -203,7 +203,7 @@ static void xdg_surface_destroy(struct wlr_xdg_surface_v6 *surface) { } } - wl_list_remove(&surface->popup_link); + wl_list_remove(&surface->popup_state->link); free(surface->popup_state); } @@ -502,12 +502,13 @@ static void xdg_surface_get_popup(struct wl_client *client, surface->popup_state->parent = parent; surface->popup_state->geometry = xdg_positioner_get_geometry(positioner, surface, parent); - wl_list_insert(&surface->popup_state->parent->popups, - &surface->popup_link); + wl_list_insert(&parent->popups, &surface->popup_state->link); wl_resource_set_implementation(surface->popup_state->resource, &zxdg_popup_v6_implementation, surface, xdg_popup_resource_destroy); + + wl_signal_emit(&parent->events.new_popup, surface->popup_state); } @@ -1184,6 +1185,7 @@ static void xdg_shell_get_xdg_surface(struct wl_client *wl_client, wl_signal_init(&surface->events.request_show_window_menu); wl_signal_init(&surface->events.destroy); wl_signal_init(&surface->events.ping_timeout); + wl_signal_init(&surface->events.new_popup); wl_signal_add(&surface->surface->events.destroy, &surface->surface_destroy_listener); @@ -1401,14 +1403,16 @@ struct wlr_xdg_surface_v6 *wlr_xdg_surface_v6_popup_at( // XXX: I think this is so complicated because we're mixing geometry // coordinates with surface coordinates. Input handling should only deal // with surface coordinates. - struct wlr_xdg_surface_v6 *popup; - wl_list_for_each(popup, &surface->popups, popup_link) { + struct wlr_xdg_popup_v6 *popup_state; + wl_list_for_each(popup_state, &surface->popups, link) { + struct wlr_xdg_surface_v6 *popup = popup_state->base; + double _popup_sx = - surface->geometry->x + popup->popup_state->geometry.x; + surface->geometry->x + popup_state->geometry.x; double _popup_sy = - surface->geometry->y + popup->popup_state->geometry.y; - int popup_width = popup->popup_state->geometry.width; - int popup_height = popup->popup_state->geometry.height; + surface->geometry->y + popup_state->geometry.y; + int popup_width = popup_state->geometry.width; + int popup_height = popup_state->geometry.height; struct wlr_xdg_surface_v6 *_popup = wlr_xdg_surface_v6_popup_at(popup,