From: Jonas Ådahl Date: Tue, 12 Jun 2012 22:01:23 +0000 (+0200) Subject: shell: Animate workspace changes X-Git-Tag: 0.94.90~95 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=62fcd0417e996ca37323af0b863888abfaa8bf81;p=platform%2Fupstream%2Fweston.git shell: Animate workspace changes Signed-off-by: Jonas Ådahl --- diff --git a/src/shell.c b/src/shell.c index 28cf7d4..cdd4e12 100644 --- a/src/shell.c +++ b/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;