shell: Animate workspace changes
Signed-off-by: Jonas Ådahl <jadahl@gmail.com>
This commit is contained in:
parent
e3cddce5a6
commit
62fcd0417e
245
src/shell.c
245
src/shell.c
@ -39,6 +39,7 @@
|
||||
#include "log.h"
|
||||
|
||||
#define DEFAULT_NUM_WORKSPACES 1
|
||||
#define DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH 200
|
||||
|
||||
enum animation_type {
|
||||
ANIMATION_NONE,
|
||||
@ -88,6 +89,13 @@ struct desktop_shell {
|
||||
struct wl_array array;
|
||||
unsigned int current;
|
||||
unsigned int num;
|
||||
|
||||
struct weston_animation animation;
|
||||
int anim_dir;
|
||||
uint32_t anim_timestamp;
|
||||
double anim_current;
|
||||
struct workspace *anim_from;
|
||||
struct workspace *anim_to;
|
||||
} workspaces;
|
||||
|
||||
struct {
|
||||
@ -166,6 +174,8 @@ struct shell_surface {
|
||||
|
||||
struct ping_timer *ping_timer;
|
||||
|
||||
struct weston_transform workspace_transform;
|
||||
|
||||
struct weston_output *fullscreen_output;
|
||||
struct weston_output *output;
|
||||
struct wl_list link;
|
||||
@ -337,6 +347,12 @@ workspace_create(void)
|
||||
return ws;
|
||||
}
|
||||
|
||||
static int
|
||||
workspace_is_empty(struct workspace *ws)
|
||||
{
|
||||
return wl_list_empty(&ws->layer.surface_list);
|
||||
}
|
||||
|
||||
static struct workspace *
|
||||
get_workspace(struct desktop_shell *shell, unsigned int index)
|
||||
{
|
||||
@ -362,6 +378,209 @@ activate_workspace(struct desktop_shell *shell, unsigned int index)
|
||||
shell->workspaces.current = index;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
get_output_height(struct weston_output *output)
|
||||
{
|
||||
return abs(output->region.extents.y1 - output->region.extents.y2);
|
||||
}
|
||||
|
||||
static void
|
||||
surface_translate(struct weston_surface *surface, double d)
|
||||
{
|
||||
struct shell_surface *shsurf = get_shell_surface(surface);
|
||||
struct weston_transform *transform;
|
||||
|
||||
transform = &shsurf->workspace_transform;
|
||||
if (wl_list_empty(&transform->link))
|
||||
wl_list_insert(surface->geometry.transformation_list.prev,
|
||||
&shsurf->workspace_transform.link);
|
||||
|
||||
weston_matrix_init(&shsurf->workspace_transform.matrix);
|
||||
weston_matrix_translate(&shsurf->workspace_transform.matrix,
|
||||
0.0, d, 0.0);
|
||||
surface->geometry.dirty = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
workspace_translate_out(struct workspace *ws, double fraction)
|
||||
{
|
||||
struct weston_surface *surface;
|
||||
unsigned int height;
|
||||
double d;
|
||||
|
||||
wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
|
||||
height = get_output_height(surface->output);
|
||||
d = height * fraction;
|
||||
|
||||
surface_translate(surface, d);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
workspace_translate_in(struct workspace *ws, double fraction)
|
||||
{
|
||||
struct weston_surface *surface;
|
||||
unsigned int height;
|
||||
double d;
|
||||
|
||||
wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
|
||||
height = get_output_height(surface->output);
|
||||
|
||||
if (fraction > 0)
|
||||
d = -(height - height * fraction);
|
||||
else
|
||||
d = height + height * fraction;
|
||||
|
||||
surface_translate(surface, d);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
workspace_damage_all_surfaces(struct workspace *ws)
|
||||
{
|
||||
struct weston_surface *surface;
|
||||
|
||||
wl_list_for_each(surface, &ws->layer.surface_list, layer_link)
|
||||
weston_surface_damage(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
reverse_workspace_change_animation(struct desktop_shell *shell,
|
||||
unsigned int index,
|
||||
struct workspace *from,
|
||||
struct workspace *to)
|
||||
{
|
||||
shell->workspaces.current = index;
|
||||
|
||||
shell->workspaces.anim_to = to;
|
||||
shell->workspaces.anim_from = from;
|
||||
shell->workspaces.anim_dir = -1 * shell->workspaces.anim_dir;
|
||||
shell->workspaces.anim_timestamp = 0;
|
||||
|
||||
workspace_damage_all_surfaces(from);
|
||||
workspace_damage_all_surfaces(to);
|
||||
}
|
||||
|
||||
static void
|
||||
workspace_deactivate_transforms(struct workspace *ws)
|
||||
{
|
||||
struct weston_surface *surface;
|
||||
struct shell_surface *shsurf;
|
||||
|
||||
wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
|
||||
shsurf = get_shell_surface(surface);
|
||||
wl_list_remove(&shsurf->workspace_transform.link);
|
||||
wl_list_init(&shsurf->workspace_transform.link);
|
||||
shsurf->surface->geometry.dirty = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
finish_workspace_change_animation(struct desktop_shell *shell,
|
||||
struct workspace *from,
|
||||
struct workspace *to)
|
||||
{
|
||||
workspace_damage_all_surfaces(from);
|
||||
workspace_damage_all_surfaces(to);
|
||||
|
||||
wl_list_remove(&shell->workspaces.animation.link);
|
||||
workspace_deactivate_transforms(from);
|
||||
workspace_deactivate_transforms(to);
|
||||
shell->workspaces.anim_to = NULL;
|
||||
|
||||
wl_list_remove(&shell->workspaces.anim_from->layer.link);
|
||||
}
|
||||
|
||||
static void
|
||||
animate_workspace_change_frame(struct weston_animation *animation,
|
||||
struct weston_output *output, uint32_t msecs)
|
||||
{
|
||||
struct desktop_shell *shell =
|
||||
container_of(animation, struct desktop_shell,
|
||||
workspaces.animation);
|
||||
struct workspace *from = shell->workspaces.anim_from;
|
||||
struct workspace *to = shell->workspaces.anim_to;
|
||||
uint32_t t;
|
||||
double x, y;
|
||||
|
||||
if (workspace_is_empty(from) && workspace_is_empty(to)) {
|
||||
finish_workspace_change_animation(shell, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
if (shell->workspaces.anim_timestamp == 0) {
|
||||
if (shell->workspaces.anim_current == 0.0)
|
||||
shell->workspaces.anim_timestamp = msecs;
|
||||
else
|
||||
shell->workspaces.anim_timestamp =
|
||||
msecs -
|
||||
/* Invers of movement function 'y' below. */
|
||||
(asin(1.0 - shell->workspaces.anim_current) *
|
||||
DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH *
|
||||
M_2_PI);
|
||||
}
|
||||
|
||||
t = msecs - shell->workspaces.anim_timestamp;
|
||||
|
||||
/*
|
||||
* x = [0, π/2]
|
||||
* y(x) = sin(x)
|
||||
*/
|
||||
x = t * (1.0/DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) * M_PI_2;
|
||||
y = sin(x);
|
||||
|
||||
if (t < DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH) {
|
||||
workspace_damage_all_surfaces(from);
|
||||
workspace_damage_all_surfaces(to);
|
||||
|
||||
workspace_translate_out(from, shell->workspaces.anim_dir * y);
|
||||
workspace_translate_in(to, shell->workspaces.anim_dir * y);
|
||||
shell->workspaces.anim_current = y;
|
||||
|
||||
workspace_damage_all_surfaces(from);
|
||||
workspace_damage_all_surfaces(to);
|
||||
}
|
||||
else {
|
||||
finish_workspace_change_animation(shell, from, to);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
animate_workspace_change(struct desktop_shell *shell,
|
||||
unsigned int index,
|
||||
struct workspace *from,
|
||||
struct workspace *to)
|
||||
{
|
||||
struct weston_output *output;
|
||||
|
||||
int dir;
|
||||
|
||||
if (index > shell->workspaces.current)
|
||||
dir = -1;
|
||||
else
|
||||
dir = 1;
|
||||
|
||||
shell->workspaces.current = index;
|
||||
|
||||
shell->workspaces.anim_dir = dir;
|
||||
shell->workspaces.anim_from = from;
|
||||
shell->workspaces.anim_to = to;
|
||||
shell->workspaces.anim_current = 0.0;
|
||||
shell->workspaces.anim_timestamp = 0;
|
||||
|
||||
output = container_of(shell->compositor->output_list.next,
|
||||
struct weston_output, link);
|
||||
wl_list_insert(&output->animation_list,
|
||||
&shell->workspaces.animation.link);
|
||||
|
||||
wl_list_insert(&from->layer.link, &to->layer.link);
|
||||
|
||||
workspace_translate_in(to, 0);
|
||||
|
||||
workspace_damage_all_surfaces(from);
|
||||
workspace_damage_all_surfaces(to);
|
||||
}
|
||||
|
||||
static void
|
||||
change_workspace(struct desktop_shell *shell, unsigned int index)
|
||||
{
|
||||
@ -384,11 +603,24 @@ change_workspace(struct desktop_shell *shell, unsigned int index)
|
||||
from = get_current_workspace(shell);
|
||||
to = get_workspace(shell, index);
|
||||
|
||||
shell->workspaces.current = index;
|
||||
wl_list_insert(&from->layer.link, &to->layer.link);
|
||||
wl_list_remove(&from->layer.link);
|
||||
if (shell->workspaces.anim_from == to &&
|
||||
shell->workspaces.anim_to == from) {
|
||||
reverse_workspace_change_animation(shell, index, from, to);
|
||||
return;
|
||||
}
|
||||
|
||||
weston_compositor_damage_all(shell->compositor);
|
||||
if (shell->workspaces.anim_to != NULL)
|
||||
finish_workspace_change_animation(shell,
|
||||
shell->workspaces.anim_from,
|
||||
shell->workspaces.anim_to);
|
||||
|
||||
if (workspace_is_empty(to) && workspace_is_empty(from)) {
|
||||
shell->workspaces.current = index;
|
||||
wl_list_insert(&from->layer.link, &to->layer.link);
|
||||
wl_list_remove(&from->layer.link);
|
||||
}
|
||||
else
|
||||
animate_workspace_change(shell, index, from, to);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1440,6 +1672,8 @@ create_shell_surface(void *shell, struct weston_surface *surface,
|
||||
wl_list_init(&shsurf->rotation.transform.link);
|
||||
weston_matrix_init(&shsurf->rotation.rotation);
|
||||
|
||||
wl_list_init(&shsurf->workspace_transform.link);
|
||||
|
||||
shsurf->type = SHELL_SURFACE_NONE;
|
||||
shsurf->next_type = SHELL_SURFACE_NONE;
|
||||
|
||||
@ -2927,6 +3161,9 @@ shell_init(struct weston_compositor *ec)
|
||||
}
|
||||
activate_workspace(shell, 0);
|
||||
|
||||
wl_list_init(&shell->workspaces.animation.link);
|
||||
shell->workspaces.animation.frame = animate_workspace_change_frame;
|
||||
|
||||
if (wl_display_add_global(ec->wl_display, &wl_shell_interface,
|
||||
shell, bind_shell) == NULL)
|
||||
return -1;
|
||||
|
Loading…
Reference in New Issue
Block a user