Skip to content

Commit

Permalink
Added fade-out animations for toplevels
Browse files Browse the repository at this point in the history
  • Loading branch information
ErikReider committed Sep 21, 2024
1 parent e34f1f0 commit 2f39171
Show file tree
Hide file tree
Showing 16 changed files with 242 additions and 43 deletions.
7 changes: 7 additions & 0 deletions include/comp/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ enum comp_object_type {
struct comp_object {
// The root of the toplevel/layer_surface/widget
struct wlr_scene_tree *scene_tree;
// Used to display the actual content
struct wlr_scene_tree *content_tree;
// Used for saved scene buffers
struct wlr_scene_tree *saved_tree;

enum comp_object_type type;
// The pointer to the ancestor which is type of `comp_object_type`
Expand All @@ -31,4 +35,7 @@ struct comp_object *comp_object_at(struct comp_server *server, double lx,
struct wlr_scene_buffer **scene_buffer,
struct wlr_surface **surface);

void comp_object_save_buffer(struct comp_object *object);
void comp_object_remove_buffer(struct comp_object *object);

#endif // !FX_COMP_OBJECT_H
1 change: 1 addition & 0 deletions include/desktop/toplevel.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ struct comp_toplevel {
} txn;

bool destroying;
bool unmapped;

struct comp_animation_client *animation;
bool mapped;
Expand Down
4 changes: 4 additions & 0 deletions include/seat/seat.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
#include "desktop/layer_shell.h"
#include "desktop/toplevel.h"

struct gesture_data {
float percent;
};

struct comp_seat {
struct comp_server *server;

Expand Down
14 changes: 14 additions & 0 deletions include/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include <cairo.h>
#include <gtk-3.0/gtk/gtk.h>
#include <scenefx/types/wlr_scene.h>
#include <wlr/util/box.h>

/*
Expand All @@ -27,6 +28,19 @@ void scale_box(struct wlr_box *box, float scale);

struct wlr_scene_tree *alloc_tree(struct wlr_scene_tree *parent);

/**
* Modified version of:
* https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4558
*
* Create a new scene node which represents a snapshot of another node.
*
* The snapshot displays the same contents as the source node at the time of
* its creation. The snapshot is completely independent from the source node:
* when the source node is updated, the snapshot will stay as-is.
*/
struct wlr_scene_tree *wlr_scene_tree_snapshot(struct wlr_scene_node *node,
struct wlr_scene_tree *parent);

/** Get red component from HEX color */
double hex_red(const uint32_t *const col);
/** Get green component from HEX color */
Expand Down
1 change: 1 addition & 0 deletions src/comp/animation_mgr.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ void comp_animation_client_add(struct comp_animation_mgr *mgr,
struct comp_animation_client *client) {
comp_animation_client_remove(client);
client->inited = true;
client->animating = true;

client->progress = 0.0;
wl_list_insert(&mgr->clients, &client->link);
Expand Down
23 changes: 23 additions & 0 deletions src/comp/object.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include <scenefx/types/wlr_scene.h>
#include <wlr/util/log.h>

#include "comp/object.h"
#include "util.h"

struct comp_object *comp_object_at(struct comp_server *server, double lx,
double ly, double *sx, double *sy,
Expand Down Expand Up @@ -33,3 +35,24 @@ struct comp_object *comp_object_at(struct comp_server *server, double lx,
}
return tree->node.data;
}

void comp_object_save_buffer(struct comp_object *object) {
// Thanks Sway for the simple implementation! :)
if (object->saved_tree) {
wlr_log(WLR_INFO, "Trying to save already saved buffer...");
comp_object_remove_buffer(object);
}

wlr_scene_node_set_enabled(&object->content_tree->node, true);
object->saved_tree = wlr_scene_tree_snapshot(&object->content_tree->node,
object->scene_tree);

wlr_scene_node_set_enabled(&object->content_tree->node, false);
wlr_scene_node_set_enabled(&object->saved_tree->node, true);
}

void comp_object_remove_buffer(struct comp_object *object) {
wlr_scene_node_destroy(&object->saved_tree->node);
object->saved_tree = NULL;
wlr_scene_node_set_enabled(&object->content_tree->node, true);
}
28 changes: 21 additions & 7 deletions src/comp/output.c
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,13 @@ static void output_frame(struct wl_listener *listener, void *data) {
struct wlr_scene_output *scene_output =
wlr_scene_get_scene_output(scene, output->wlr_output);

// output_configure_scene(output, &server.root_scene->tree.node, NULL);
// wlr_scene_optimized_blur_mark_dirty(scene);
// wlr_output_layout_get_box(server.output_layout, output->wlr_output,
// &output->geometry);
// wlr_scene_output_set_position(scene_output, output->geometry.x,
// output->geometry.y);

/* Render the scene if needed and commit the output */
wlr_scene_output_commit(scene_output, NULL);

Expand Down Expand Up @@ -238,18 +245,19 @@ struct comp_output *comp_output_create(struct comp_server *server,
output->server = server;

output->object.scene_tree = alloc_tree(&server->root_scene->tree);
output->object.content_tree = alloc_tree(output->object.scene_tree);
output->object.scene_tree->node.data = &output->object;
output->object.data = output;
output->object.type = COMP_OBJECT_TYPE_OUTPUT;

// Initialize layers
output->layers.shell_background = alloc_tree(output->object.scene_tree);
output->layers.shell_bottom = alloc_tree(output->object.scene_tree);
output->layers.workspaces = alloc_tree(output->object.scene_tree);
output->layers.shell_top = alloc_tree(output->object.scene_tree);
output->layers.shell_overlay = alloc_tree(output->object.scene_tree);
output->layers.seat = alloc_tree(output->object.scene_tree);
output->layers.session_lock = alloc_tree(output->object.scene_tree);
output->layers.shell_background = alloc_tree(output->object.content_tree);
output->layers.shell_bottom = alloc_tree(output->object.content_tree);
output->layers.workspaces = alloc_tree(output->object.content_tree);
output->layers.shell_top = alloc_tree(output->object.content_tree);
output->layers.shell_overlay = alloc_tree(output->object.content_tree);
output->layers.seat = alloc_tree(output->object.content_tree);
output->layers.session_lock = alloc_tree(output->object.content_tree);

wl_list_init(&output->workspaces);

Expand Down Expand Up @@ -403,6 +411,12 @@ void comp_output_update_sizes(struct comp_output *output) {

comp_output_arrange_layers(output);
comp_output_arrange_output(output);

// TODO: Update toplevel positions/tiling/sizes Only enable for actual
// outputs
// printf("GEO: %i %i %ix%i\n", output->geometry.width,
// output->geometry.height, output->geometry.x,
// output->geometry.y);
}

void comp_output_move_workspace_to(struct comp_output *dest_output,
Expand Down
81 changes: 58 additions & 23 deletions src/comp/toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -444,25 +444,24 @@ struct wlr_scene_tree *comp_toplevel_get_layer(struct comp_toplevel *toplevel) {

static void iter_scene_buffers_apply_effects(struct wlr_scene_buffer *buffer,
int sx, int sy, void *user_data) {
struct comp_toplevel *toplevel = user_data;

struct wlr_scene_surface *scene_surface =
wlr_scene_surface_try_from_buffer(buffer);
if (!scene_surface || !user_data) {
return;
}

struct comp_toplevel *toplevel = user_data;
switch (toplevel->type) {
case COMP_TOPLEVEL_TYPE_XDG:;
struct wlr_xdg_surface *xdg_surface;
if (!(xdg_surface = wlr_xdg_surface_try_from_wlr_surface(
scene_surface->surface)) ||
xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
return;
if (scene_surface) {
switch (toplevel->type) {
case COMP_TOPLEVEL_TYPE_XDG:;
struct wlr_xdg_surface *xdg_surface;
if (!(xdg_surface = wlr_xdg_surface_try_from_wlr_surface(
scene_surface->surface)) ||
xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) {
return;
}
break;
case COMP_TOPLEVEL_TYPE_XWAYLAND:
// TODO: Check here
break;
}
break;
case COMP_TOPLEVEL_TYPE_XWAYLAND:
// TODO: Check here
break;
}

bool has_effects = !toplevel->fullscreen;
Expand All @@ -488,9 +487,14 @@ static void iter_scene_buffers_apply_effects(struct wlr_scene_buffer *buffer,
}

void comp_toplevel_mark_effects_dirty(struct comp_toplevel *toplevel) {
if (toplevel->object.saved_tree) {
wlr_scene_node_for_each_buffer(&toplevel->object.saved_tree->node,
iter_scene_buffers_apply_effects,
toplevel);
return;
}
wlr_scene_node_for_each_buffer(&toplevel->toplevel_scene_tree->node,
iter_scene_buffers_apply_effects, toplevel);
comp_toplevel_commit_transaction(toplevel, false);
}

void comp_toplevel_move_into_parent_tree(struct comp_toplevel *toplevel,
Expand Down Expand Up @@ -893,6 +897,10 @@ void comp_toplevel_close(struct comp_toplevel *toplevel) {

void comp_toplevel_destroy(struct comp_toplevel *toplevel) {
toplevel->destroying = true;
if (toplevel->animation->animating) {
wlr_log(WLR_DEBUG, "Delaying destroy until animation finishes");
return;
}

comp_transaction_remove(&toplevel->txn.transaction);

Expand All @@ -914,17 +922,32 @@ const struct comp_transaction_impl transaction_impl = {
static void animation_update(struct comp_animation_mgr *mgr,
struct comp_animation_client *client) {
struct comp_toplevel *toplevel = client->data;
toplevel->opacity = client->progress;
toplevel->titlebar->widget.opacity = client->progress;
if (toplevel->unmapped) {
toplevel->opacity = 1 - client->progress;
toplevel->titlebar->widget.opacity = 1 - client->progress;
} else {
toplevel->opacity = client->progress;
toplevel->titlebar->widget.opacity = client->progress;
}
comp_toplevel_mark_effects_dirty(toplevel);
}

static void animation_done(struct comp_animation_mgr *mgr,
struct comp_animation_client *client) {
struct comp_toplevel *toplevel = client->data;
toplevel->opacity = 1.0f;
toplevel->titlebar->widget.opacity = 1.0f;
if (toplevel->unmapped) {
toplevel->opacity = 0.0f;
toplevel->titlebar->widget.opacity = 0.0f;
} else {
toplevel->opacity = 1.0f;
toplevel->titlebar->widget.opacity = 1.0f;
}
comp_toplevel_mark_effects_dirty(toplevel);

// Continue destroying the toplevel
if (toplevel->destroying) {
comp_toplevel_destroy(toplevel);
}
}

const struct comp_animation_client_impl animation_impl = {
Expand Down Expand Up @@ -963,12 +986,13 @@ comp_toplevel_init(struct comp_output *output, struct comp_workspace *workspace,
toplevel->state.workspace = workspace;
struct wlr_scene_tree *tree = comp_toplevel_get_layer(toplevel);
toplevel->object.scene_tree = alloc_tree(tree);
toplevel->object.content_tree = alloc_tree(toplevel->object.scene_tree);

toplevel->object.scene_tree->node.data = &toplevel->object;
toplevel->object.data = toplevel;
toplevel->object.type = COMP_OBJECT_TYPE_TOPLEVEL;

toplevel->decoration_scene_tree = alloc_tree(toplevel->object.scene_tree);
toplevel->decoration_scene_tree = alloc_tree(toplevel->object.content_tree);

// Initialize saved position/size
toplevel->saved_state.x = 0;
Expand Down Expand Up @@ -1026,6 +1050,8 @@ static inline void set_natural_size(struct comp_toplevel *toplevel) {
*/

void comp_toplevel_generic_map(struct comp_toplevel *toplevel) {
toplevel->unmapped = false;

struct comp_workspace *ws = toplevel->state.workspace;

comp_toplevel_set_pid(toplevel);
Expand Down Expand Up @@ -1073,11 +1099,20 @@ void comp_toplevel_generic_map(struct comp_toplevel *toplevel) {
}

void comp_toplevel_generic_unmap(struct comp_toplevel *toplevel) {
toplevel->unmapped = true;

if (toplevel->fullscreen) {
comp_toplevel_set_fullscreen(toplevel, false);
}

wlr_scene_node_set_enabled(&toplevel->object.scene_tree->node, false);
// Don't animate if already destroying
if (!toplevel->destroying) {
comp_object_save_buffer(&toplevel->object);
toplevel->opacity = 1;
toplevel->titlebar->widget.opacity = 1;
comp_toplevel_mark_effects_dirty(toplevel);
comp_animation_client_add(server.animation_mgr, toplevel->animation);
}

/* Reset the cursor mode if the grabbed toplevel was unmapped. */
if (toplevel == toplevel->server->seat->grabbed_toplevel) {
Expand Down
6 changes: 4 additions & 2 deletions src/comp/widget.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ bool comp_widget_init(struct comp_widget *widget, struct comp_server *server,
const struct comp_widget_impl *impl) {
assert(parent_obj);
widget->object.scene_tree = alloc_tree(parent_tree);
if (widget->object.scene_tree == NULL) {
widget->object.content_tree = alloc_tree(widget->object.scene_tree);
if (widget->object.scene_tree == NULL ||
widget->object.content_tree == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate comp_titlebar wlr_scene_tree");
return false;
}

widget->scene_buffer =
wlr_scene_buffer_create(widget->object.scene_tree, NULL);
wlr_scene_buffer_create(widget->object.content_tree, NULL);
if (widget->scene_buffer == NULL) {
wlr_log(WLR_ERROR, "Failed to allocate comp_titlebar wlr_scene_buffer");
wlr_scene_node_destroy(&widget->object.scene_tree->node);
Expand Down
9 changes: 5 additions & 4 deletions src/comp/workspace.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,27 +173,28 @@ struct comp_workspace *comp_workspace_new(struct comp_output *output,

// Create workspace tree
ws->object.scene_tree = alloc_tree(output->layers.workspaces);
if (!ws->object.scene_tree) {
ws->object.content_tree = alloc_tree(ws->object.scene_tree);
if (!ws->object.scene_tree || !ws->object.content_tree) {
return NULL;
}
ws->object.scene_tree->node.data = &ws->object;
ws->object.data = ws;
ws->object.type = COMP_OBJECT_TYPE_WORKSPACE;

// Create tiled/fullscreen
ws->layers.lower = alloc_tree(ws->object.scene_tree);
ws->layers.lower = alloc_tree(ws->object.content_tree);
if (!ws->layers.lower) {
return NULL;
}
ws->layers.lower->node.data = &ws->object;
// Create floating
ws->layers.floating = alloc_tree(ws->object.scene_tree);
ws->layers.floating = alloc_tree(ws->object.content_tree);
if (!ws->layers.floating) {
return NULL;
}
ws->layers.floating->node.data = &ws->object;
// Create unmanaged
ws->layers.unmanaged = alloc_tree(ws->object.scene_tree);
ws->layers.unmanaged = alloc_tree(ws->object.content_tree);
if (!ws->layers.unmanaged) {
return NULL;
}
Expand Down
7 changes: 5 additions & 2 deletions src/desktop/layer_shell.c
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,10 @@ void layer_shell_new_surface(struct wl_listener *listener, void *data) {
struct wlr_scene_tree *layer =
layer_get_scene_tree(output, wlr_layer_surface->pending.layer);
layer_surface->object.scene_tree = alloc_tree(layer);
if (layer_surface->object.scene_tree == NULL) {
layer_surface->object.content_tree =
alloc_tree(layer_surface->object.scene_tree);
if (layer_surface->object.scene_tree == NULL ||
layer_surface->object.content_tree == NULL) {
goto error;
}

Expand All @@ -229,7 +232,7 @@ void layer_shell_new_surface(struct wl_listener *listener, void *data) {
*/

layer_surface->scene_layer = wlr_scene_layer_surface_v1_create(
layer_surface->object.scene_tree, wlr_layer_surface);
layer_surface->object.content_tree, wlr_layer_surface);
if (layer_surface->scene_layer == NULL) {
wlr_log(WLR_ERROR, "Could not create wlr_scene_layer_surface");
goto error;
Expand Down
2 changes: 1 addition & 1 deletion src/desktop/xdg.c
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ void xdg_new_xdg_surface(struct wl_listener *listener, void *data) {

// TODO: event.output_enter/output_leave for primary output
toplevel->toplevel_scene_tree = wlr_scene_xdg_surface_create(
toplevel->object.scene_tree, toplevel_xdg->xdg_toplevel->base);
toplevel->object.content_tree, toplevel_xdg->xdg_toplevel->base);
toplevel->toplevel_scene_tree->node.data = &toplevel->object;
xdg_surface->data = toplevel->object.scene_tree;

Expand Down
Loading

0 comments on commit 2f39171

Please sign in to comment.