[PATCH weston 3/3] shell: Animate workspace changes

Jonas Ådahl jadahl at gmail.com
Wed Jun 6 02:36:10 PDT 2012


Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
 src/shell.c |  252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 243 insertions(+), 9 deletions(-)

diff --git a/src/shell.c b/src/shell.c
index 56b271b..27f55b4 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -38,6 +38,7 @@
 #include "../shared/config-parser.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;
@@ -1365,6 +1375,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;
 
@@ -1620,6 +1632,99 @@ workspace_create(void)
 	return ws;
 }
 
+static int
+workspace_is_empty(struct workspace *ws)
+{
+	return wl_list_empty(&ws->layer.surface_list);
+}
+
+static unsigned int
+get_output_height(struct weston_output *output)
+{
+	return abs(output->region.extents.y1 - output->region.extents.y2);
+}
+
+static void
+workspace_translate_out(struct workspace *ws, double fraction)
+{
+	struct weston_surface *surface;
+	struct shell_surface *shsurf;
+	unsigned int height;
+	double d;
+
+	wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+		shsurf = get_shell_surface(surface);
+		height = get_output_height(surface->output);
+		d = height * fraction;
+
+		weston_matrix_init(&shsurf->workspace_transform.matrix);
+		weston_matrix_translate(&shsurf->workspace_transform.matrix,
+					0.0, d, 0.0);
+		shsurf->surface->geometry.dirty = 1;
+	}
+}
+
+static void
+workspace_translate_in(struct workspace *ws, double fraction)
+{
+	struct weston_surface *surface;
+	struct shell_surface *shsurf;
+	unsigned int height;
+	double d;
+
+	wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+		shsurf = get_shell_surface(surface);
+		height = get_output_height(surface->output);
+
+		if (fraction > 0)
+			d = -(height - height * fraction);
+		else
+			d = height + height * fraction;
+
+		weston_matrix_init(&shsurf->workspace_transform.matrix);
+		weston_matrix_translate(&shsurf->workspace_transform.matrix,
+					0.0, d, 0.0);
+		shsurf->surface->geometry.dirty = 1;
+	}
+}
+
+static void
+workspace_activate_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);
+		weston_matrix_init(&shsurf->workspace_transform.matrix);
+		wl_list_insert(shsurf->surface->geometry.transformation_list.prev,
+			       &shsurf->workspace_transform.link);
+		shsurf->surface->geometry.dirty = 1;
+	}
+}
+
+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);
+		shsurf->surface->geometry.dirty = 1;
+	}
+}
+
+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 struct workspace *
 get_workspace(struct desktop_shell *shell, unsigned int index)
 {
@@ -1646,12 +1751,130 @@ activate_workspace(struct desktop_shell *shell, unsigned int index)
 }
 
 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 =
+		weston_compositor_get_time() -
+		/* Invers of movement function in
+		 * `animate_workspace_change_frame()' */
+		(asin(1.0 - shell->workspaces.anim_current) *
+		 DEFAULT_WORKSPACE_CHANGE_ANIMATION_LENGTH *
+		 M_2_PI);
+
+	workspace_damage_all_surfaces(from);
+	workspace_damage_all_surfaces(to);
+}
+
+
+static void
+finish_workspace_change_animation(struct desktop_shell *shell,
+				  struct workspace *from,
+				  struct workspace *to)
+{
+	struct weston_surface *surface;
+
+	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);
+	wl_list_for_each(surface, &from->layer.surface_list, layer_link)
+		weston_surface_hide(surface);
+}
+
+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 (msecs < shell->workspaces.anim_timestamp)
+		t = 0;
+	else
+		t = msecs - shell->workspaces.anim_timestamp;
+
+	/*
+	 * x = [0, π/2]
+	 * y = 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_translate_out(from, shell->workspaces.anim_dir * y);
+		workspace_translate_in(to, shell->workspaces.anim_dir * y);
+		shell->workspaces.anim_current = y;
+	}
+	else {
+		finish_workspace_change_animation(shell, from, to);
+	}
+
+	workspace_damage_all_surfaces(from);
+	workspace_damage_all_surfaces(to);
+}
+
+static void
+animate_workspace_change(struct desktop_shell *shell,
+			 unsigned int index,
+			 struct workspace *from,
+			 struct workspace *to)
+{
+	struct weston_surface *surface;
+	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 = weston_compositor_get_time();
+
+	wl_list_insert(&shell->compositor->animation_list,
+		       &shell->workspaces.animation.link);
+
+	wl_list_insert(&from->layer.link, &to->layer.link);
+	workspace_activate_transforms(from);
+	workspace_activate_transforms(to);
+	workspace_translate_in(to, 0);
+	wl_list_for_each(surface, &to->layer.surface_list, layer_link)
+		weston_surface_show(surface);
+
+	workspace_damage_all_surfaces(from);
+	workspace_damage_all_surfaces(to);
+}
+
+static void
 change_workspace(struct desktop_shell *shell, unsigned int index)
 {
 	struct workspace *from;
 	struct workspace *to;
 	struct weston_seat *seat;
-	struct weston_surface *surface;
 
 	if (index == shell->workspaces.current)
 		return;
@@ -1668,16 +1891,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;
+	}
 
-	wl_list_for_each(surface, &from->layer.surface_list, layer_link)
-		weston_surface_hide(surface);
-	wl_list_for_each(surface, &to->layer.surface_list, layer_link)
-		weston_surface_show(surface);
+	if (shell->workspaces.anim_to != NULL)
+		finish_workspace_change_animation(shell,
+						  shell->workspaces.anim_from,
+						  shell->workspaces.anim_to);
 
-	weston_compositor_damage_all(shell->compositor);
+	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
@@ -2914,6 +3145,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;
-- 
1.7.9.5



More information about the wayland-devel mailing list