[PATCH weston 10/18] animation, shell: add kbd focus change animation

ppaalanen at gmail.com ppaalanen at gmail.com
Wed May 22 08:03:13 PDT 2013


From: Louis-Francis Ratté-Boulianne <lfrb at collabora.com>

When enabled, this will make all but the keyboard-focused window dim.
Also the background gets dimmed, if there are any windows open. The
panel is not dimmed.

When the keyboard focus changes, the change in dimming is animated.

The dimming is implemented with transparent solid-color surfaces, two at
most. The net effect of two overlapping dim surfaces is kept constant
during animations (stable fade animation).

There is a new weston.ini option "focus-animation", that defaults to
none, and can be set to "dim-layer" to enable the focus change
animation.

[pq: Sliced, squashed, and rebased the patch series. Fixed surface alpha
interaction with the switcher. Wrote the commit message.]
---
 src/animation.c  |  52 ++++++++++--
 src/compositor.h |   8 ++
 src/shell.c      | 251 +++++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 282 insertions(+), 29 deletions(-)

diff --git a/src/animation.c b/src/animation.c
index e947d72..6f20179 100644
--- a/src/animation.c
+++ b/src/animation.c
@@ -108,9 +108,10 @@ struct weston_surface_animation {
 	weston_surface_animation_frame_func_t frame;
 	weston_surface_animation_done_func_t done;
 	void *data;
+	void *private;
 };
 
-static void
+WL_EXPORT void
 weston_surface_animation_destroy(struct weston_surface_animation *animation)
 {
 	wl_list_remove(&animation->animation.link);
@@ -162,7 +163,8 @@ weston_surface_animation_run(struct weston_surface *surface,
 			     float start, float stop,
 			     weston_surface_animation_frame_func_t frame,
 			     weston_surface_animation_done_func_t done,
-			     void *data)
+			     void *data,
+			     void *private)
 {
 	struct weston_surface_animation *animation;
 
@@ -176,6 +178,7 @@ weston_surface_animation_run(struct weston_surface *surface,
 	animation->data = data;
 	animation->start = start;
 	animation->stop = stop;
+	animation->private = private;
 	weston_matrix_init(&animation->transform.matrix);
 	wl_list_insert(&surface->geometry.transformation_list,
 		       &animation->transform.link);
@@ -222,7 +225,7 @@ weston_zoom_run(struct weston_surface *surface, float start, float stop,
 		weston_surface_animation_done_func_t done, void *data)
 {
 	return weston_surface_animation_run(surface, start, stop,
-					    zoom_frame, done, data);
+					    zoom_frame, done, data, NULL);
 }
 
 static void
@@ -244,7 +247,7 @@ weston_fade_run(struct weston_surface *surface,
 	struct weston_surface_animation *fade;
 
 	fade = weston_surface_animation_run(surface, 0, 0,
-					    fade_frame, done, data);
+					    fade_frame, done, data, NULL);
 
 	weston_spring_init(&fade->spring, k, start, end);
 	surface->alpha = start;
@@ -260,6 +263,45 @@ weston_fade_update(struct weston_surface_animation *fade,
 }
 
 static void
+stable_fade_frame(struct weston_surface_animation *animation)
+{
+	struct weston_surface *back_surface;
+
+	if (animation->spring.current > 0.999)
+		animation->surface->alpha = 1;
+	else if (animation->spring.current < 0.001 )
+		animation->surface->alpha = 0;
+	else
+		animation->surface->alpha = animation->spring.current;
+
+	back_surface = (struct weston_surface *) animation->private;
+	back_surface->alpha =
+		(animation->spring.target - animation->surface->alpha) /
+		(1.0 - animation->surface->alpha);
+	weston_surface_geometry_dirty(back_surface);
+}
+
+WL_EXPORT struct weston_surface_animation *
+weston_stable_fade_run(struct weston_surface *front_surface, float start,
+		struct weston_surface *back_surface, float end,
+		weston_surface_animation_done_func_t done, void *data)
+{
+	struct weston_surface_animation *fade;
+
+	fade = weston_surface_animation_run(front_surface, 0, 0,
+					    stable_fade_frame, done, data, back_surface);
+
+
+	weston_spring_init(&fade->spring, 400, start, end);
+	fade->spring.friction = 1150;
+
+	front_surface->alpha = start;
+	back_surface->alpha = end;
+
+	return fade;
+}
+
+static void
 slide_frame(struct weston_surface_animation *animation)
 {
 	float scale;
@@ -278,7 +320,7 @@ weston_slide_run(struct weston_surface *surface, float start, float stop,
 	struct weston_surface_animation *animation;
 
 	animation = weston_surface_animation_run(surface, start, stop,
-						 slide_frame, done, data);
+						 slide_frame, done, data, NULL);
 	if (!animation)
 		return NULL;
 
diff --git a/src/compositor.h b/src/compositor.h
index efc4102..61aeb9f 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -1105,6 +1105,9 @@ weston_watch_process(struct weston_process *process);
 struct weston_surface_animation;
 typedef	void (*weston_surface_animation_done_func_t)(struct weston_surface_animation *animation, void *data);
 
+void
+weston_surface_animation_destroy(struct weston_surface_animation *animation);
+
 struct weston_surface_animation *
 weston_zoom_run(struct weston_surface *surface, float start, float stop,
 		weston_surface_animation_done_func_t done, void *data);
@@ -1118,6 +1121,11 @@ weston_fade_update(struct weston_surface_animation *fade,
 		   float start, float end, float k);
 
 struct weston_surface_animation *
+weston_stable_fade_run(struct weston_surface *front_surface, float start,
+		       struct weston_surface *back_surface, float end,
+		       weston_surface_animation_done_func_t done, void *data);
+
+struct weston_surface_animation *
 weston_slide_run(struct weston_surface *surface, float start, float stop,
 		 weston_surface_animation_done_func_t done, void *data);
 
diff --git a/src/shell.c b/src/shell.c
index eb8d802..b7c372e 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -46,7 +46,8 @@ enum animation_type {
 	ANIMATION_NONE,
 
 	ANIMATION_ZOOM,
-	ANIMATION_FADE
+	ANIMATION_FADE,
+ 	ANIMATION_DIM_LAYER,
 };
 
 enum fade_type {
@@ -63,11 +64,20 @@ struct focus_state {
 	struct wl_listener surface_destroy_listener;
 };
 
+struct focus_surface {
+	struct weston_surface *surface;
+	struct weston_transform workspace_transform;
+};
+
 struct workspace {
 	struct weston_layer layer;
 
 	struct wl_list focus_list;
 	struct wl_listener seat_destroyed_listener;
+
+	struct focus_surface *fsurf_front;
+	struct focus_surface *fsurf_back;
+	struct weston_surface_animation *focus_animation;
 };
 
 struct input_panel_surface {
@@ -161,6 +171,7 @@ struct desktop_shell {
 
 	uint32_t binding_modifier;
 	enum animation_type win_animation_type;
+	enum animation_type focus_animation_type;
 };
 
 enum shell_surface_type {
@@ -374,6 +385,8 @@ get_animation_type(char *animation)
 		return ANIMATION_ZOOM;
 	else if (!strcmp("fade", animation))
 		return ANIMATION_FADE;
+	else if (!strcmp("dim-layer", animation))
+		return ANIMATION_DIM_LAYER;
 	else
 		return ANIMATION_NONE;
 }
@@ -386,10 +399,12 @@ shell_configuration(struct desktop_shell *shell, int config_fd)
 	unsigned int num_workspaces = DEFAULT_NUM_WORKSPACES;
 	char *modifier = NULL;
 	char *win_animation = NULL;
+	char *focus_animation = NULL;
 
 	struct config_key shell_keys[] = {
 		{ "binding-modifier",   CONFIG_KEY_STRING, &modifier },
 		{ "animation",          CONFIG_KEY_STRING, &win_animation},
+		{ "focus-animation",    CONFIG_KEY_STRING, &focus_animation},
 		{ "num-workspaces",
 			CONFIG_KEY_UNSIGNED_INTEGER, &num_workspaces },
 	};
@@ -410,9 +425,90 @@ shell_configuration(struct desktop_shell *shell, int config_fd)
 	shell->screensaver.duration = duration * 1000;
 	shell->binding_modifier = get_modifier(modifier);
 	shell->win_animation_type = get_animation_type(win_animation);
+	shell->focus_animation_type = get_animation_type(focus_animation);
 	shell->workspaces.num = num_workspaces > 0 ? num_workspaces : 1;
 }
 
+static struct weston_output *
+get_default_output(struct weston_compositor *compositor)
+{
+	return container_of(compositor->output_list.next,
+			    struct weston_output, link);
+}
+
+
+/* no-op func for checking focus surface */
+static void
+focus_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy,
+			int32_t width, int32_t height)
+{
+}
+
+static struct focus_surface *
+get_focus_surface(struct weston_surface *surface)
+{
+	if (surface->configure == focus_surface_configure)
+		return surface->configure_private;
+	else
+		return NULL;
+}
+
+static bool
+is_focus_surface (struct weston_surface *es)
+{
+	return (es->configure == focus_surface_configure);
+}
+
+static struct focus_surface *
+create_focus_surface(struct weston_compositor *ec,
+		     struct weston_output *output)
+{
+	struct focus_surface *fsurf = NULL;
+	struct weston_surface *surface = NULL;
+
+	fsurf = malloc(sizeof *fsurf);
+	if (!fsurf)
+		return NULL;
+
+	fsurf->surface = weston_surface_create(ec);
+	surface = fsurf->surface;
+	if (surface == NULL) {
+		free(fsurf);
+		return NULL;
+	}
+
+	surface->configure = focus_surface_configure;
+	surface->output = output;
+	surface->configure_private = fsurf;
+	weston_surface_configure(surface, output->x, output->y,
+				 output->width, output->height);
+	weston_surface_set_color(surface, 0.0, 0.0, 0.0, 1.0);
+	pixman_region32_fini(&surface->opaque);
+	pixman_region32_init_rect(&surface->opaque, output->x, output->y,
+				  output->width, output->height);
+	pixman_region32_fini(&surface->input);
+	pixman_region32_init(&surface->input);
+
+	wl_list_init(&fsurf->workspace_transform.link);
+
+	return fsurf;
+}
+
+static void
+focus_surface_destroy(struct focus_surface *fsurf)
+{
+	weston_surface_destroy(fsurf->surface);
+	free(fsurf);
+}
+
+static void
+focus_animation_done(struct weston_surface_animation *animation, void *data)
+{
+	struct workspace *ws = data;
+
+	ws->focus_animation = NULL;
+}
+
 static void
 focus_state_destroy(struct focus_state *state)
 {
@@ -448,6 +544,8 @@ focus_state_surface_destroy(struct wl_listener *listener, void *data)
 	wl_list_for_each(surface, &state->ws->layer.surface_list, layer_link) {
 		if (surface == main_surface)
 			continue;
+		if (is_focus_surface(surface))
+			continue;
 
 		next = surface;
 		break;
@@ -457,10 +555,21 @@ focus_state_surface_destroy(struct wl_listener *listener, void *data)
 	if (main_surface != state->keyboard_focus)
 		next = main_surface;
 
+	shell = state->seat->compositor->shell_interface.shell;
 	if (next) {
-		shell = state->seat->compositor->shell_interface.shell;
+		state->keyboard_focus = NULL;
 		activate(shell, next, state->seat);
 	} else {
+		if (shell->focus_animation_type == ANIMATION_DIM_LAYER) {
+			if (state ->ws->focus_animation)
+				weston_surface_animation_destroy(state->ws->focus_animation);
+				
+			state->ws->focus_animation = weston_fade_run(
+				state->ws->fsurf_front->surface,
+				state->ws->fsurf_front->surface->alpha, 0.0, 300,
+				focus_animation_done, state->ws);
+		}
+
 		wl_list_remove(&state->link);
 		focus_state_destroy(state);
 	}
@@ -475,6 +584,7 @@ focus_state_create(struct weston_seat *seat, struct workspace *ws)
 	if (state == NULL)
 		return NULL;
 
+	state->keyboard_focus = NULL;
 	state->ws = ws;
 	state->seat = seat;
 	wl_list_insert(&ws->focus_list, &state->link);
@@ -546,6 +656,63 @@ drop_focus_state(struct desktop_shell *shell, struct workspace *ws,
 }
 
 static void
+animate_focus_change(struct desktop_shell *shell, struct workspace *ws,
+		     struct weston_surface *from, struct weston_surface *to)
+{
+	struct weston_output *output;
+	bool focus_surface_created = false;
+
+	/* FIXME: Only support dim animation using two layers */
+	if (from == to || shell->focus_animation_type != ANIMATION_DIM_LAYER)
+		return;
+
+	output = get_default_output(shell->compositor);
+	if (ws->fsurf_front == NULL && (from || to)) {
+		ws->fsurf_front = create_focus_surface(shell->compositor, output);
+		ws->fsurf_back = create_focus_surface(shell->compositor, output);
+		ws->fsurf_front->surface->alpha = 0.0;
+		ws->fsurf_back->surface->alpha = 0.0;
+		focus_surface_created = true;
+	} else {
+		wl_list_remove(&ws->fsurf_front->surface->layer_link);
+		wl_list_remove(&ws->fsurf_back->surface->layer_link);
+	}
+
+	if (ws->focus_animation) {
+		weston_surface_animation_destroy(ws->focus_animation);
+		ws->focus_animation = NULL;
+	}
+
+	if (to)
+		wl_list_insert(&to->layer_link,
+			       &ws->fsurf_front->surface->layer_link);
+	else if (from)
+		wl_list_insert(&ws->layer.surface_list,
+			       &ws->fsurf_front->surface->layer_link);
+
+	if (focus_surface_created) {
+		ws->focus_animation = weston_fade_run(
+			ws->fsurf_front->surface,
+			ws->fsurf_front->surface->alpha, 0.6, 300,
+			focus_animation_done, ws);
+	} else if (from) {
+		wl_list_insert(&from->layer_link,
+			       &ws->fsurf_back->surface->layer_link);
+		ws->focus_animation = weston_stable_fade_run(
+			ws->fsurf_front->surface, 0.0,
+			ws->fsurf_back->surface, 0.6,
+			focus_animation_done, ws);
+	} else if (to) {
+		wl_list_insert(&ws->layer.surface_list,
+			       &ws->fsurf_back->surface->layer_link);
+		ws->focus_animation = weston_stable_fade_run(
+			ws->fsurf_front->surface, 0.0,
+			ws->fsurf_back->surface, 0.6,
+			focus_animation_done, ws);
+	}
+}
+
+static void
 workspace_destroy(struct workspace *ws)
 {
 	struct focus_state *state, *next;
@@ -553,6 +720,11 @@ workspace_destroy(struct workspace *ws)
 	wl_list_for_each_safe(state, next, &ws->focus_list, link)
 		focus_state_destroy(state);
 
+	if (ws->fsurf_front)
+		focus_surface_destroy(ws->fsurf_front);
+	if (ws->fsurf_back)
+		focus_surface_destroy(ws->fsurf_back);
+
 	free(ws);
 }
 
@@ -582,6 +754,9 @@ workspace_create(void)
 	wl_list_init(&ws->focus_list);
 	wl_list_init(&ws->seat_destroyed_listener.link);
 	ws->seat_destroyed_listener.notify = seat_destroyed;
+	ws->fsurf_front = NULL;
+	ws->fsurf_back = NULL;
+	ws->focus_animation = NULL;
 
 	return ws;
 }
@@ -625,18 +800,24 @@ get_output_height(struct weston_output *output)
 }
 
 static void
-surface_translate(struct weston_surface *surface, double d)
+surface_translate(struct workspace *ws, struct weston_surface *surface, double d)
 {
-	struct shell_surface *shsurf = get_shell_surface(surface);
 	struct weston_transform *transform;
 
-	transform = &shsurf->workspace_transform;
+	if (is_focus_surface(surface)) {
+		struct focus_surface *fsurf = get_focus_surface(surface);
+		transform = &fsurf->workspace_transform;
+	} else {
+		struct shell_surface *shsurf = get_shell_surface(surface);
+		transform = &shsurf->workspace_transform;
+	}
+
 	if (wl_list_empty(&transform->link))
 		wl_list_insert(surface->geometry.transformation_list.prev,
-			       &shsurf->workspace_transform.link);
+			       &transform->link);
 
-	weston_matrix_init(&shsurf->workspace_transform.matrix);
-	weston_matrix_translate(&shsurf->workspace_transform.matrix,
+	weston_matrix_init(&transform->matrix);
+	weston_matrix_translate(&transform->matrix,
 				0.0, d, 0.0);
 	weston_surface_geometry_dirty(surface);
 }
@@ -652,7 +833,7 @@ workspace_translate_out(struct workspace *ws, double fraction)
 		height = get_output_height(surface->output);
 		d = height * fraction;
 
-		surface_translate(surface, d);
+		surface_translate(ws, surface, d);
 	}
 }
 
@@ -671,7 +852,7 @@ workspace_translate_in(struct workspace *ws, double fraction)
 		else
 			d = height + height * fraction;
 
-		surface_translate(surface, d);
+		surface_translate(ws, surface, d);
 	}
 }
 
@@ -706,13 +887,20 @@ static void
 workspace_deactivate_transforms(struct workspace *ws)
 {
 	struct weston_surface *surface;
-	struct shell_surface *shsurf;
+	struct weston_transform *transform;
 
 	wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
-		shsurf = get_shell_surface(surface);
-		if (!wl_list_empty(&shsurf->workspace_transform.link)) {
-			wl_list_remove(&shsurf->workspace_transform.link);
-			wl_list_init(&shsurf->workspace_transform.link);
+		if (is_focus_surface(surface)) {
+			struct focus_surface *fsurf = get_focus_surface(surface);
+			transform = &fsurf->workspace_transform;
+		} else {
+			struct shell_surface *shsurf = get_shell_surface(surface);
+			transform = &shsurf->workspace_transform;
+		}
+
+		if (!wl_list_empty(&transform->link)) {
+			wl_list_remove(&transform->link);
+			wl_list_init(&transform->link);
 		}
 		weston_surface_geometry_dirty(surface);
 	}
@@ -835,6 +1023,7 @@ change_workspace(struct desktop_shell *shell, unsigned int index)
 {
 	struct workspace *from;
 	struct workspace *to;
+	struct focus_state *state;
 
 	if (index == shell->workspaces.current)
 		return;
@@ -861,6 +1050,18 @@ change_workspace(struct desktop_shell *shell, unsigned int index)
 
 	restore_focus_state(shell, to);
 
+	if (shell->focus_animation_type != ANIMATION_NONE) {
+		wl_list_for_each(state, &from->focus_list, link)
+			if (state->keyboard_focus)
+				animate_focus_change(shell, from,
+						     state->keyboard_focus, NULL);
+
+		wl_list_for_each(state, &to->focus_list, link)
+			if (state->keyboard_focus)
+				animate_focus_change(shell, to,
+						     NULL, state->keyboard_focus);
+	}
+
 	if (workspace_is_empty(to) && workspace_is_empty(from))
 		update_workspace(shell, index, from, to);
 	else
@@ -936,7 +1137,8 @@ take_surface_to_workspace_by_seat(struct desktop_shell *shell,
 
 	surface = weston_surface_get_main_surface(seat->keyboard->focus);
 	if (surface == NULL ||
-	    index == shell->workspaces.current)
+	    index == shell->workspaces.current ||
+	    is_focus_surface(surface))
 		return;
 
 	from = get_current_workspace(shell);
@@ -1497,13 +1699,6 @@ shell_surface_set_class(struct wl_client *client,
 	shsurf->class = strdup(class);
 }
 
-static struct weston_output *
-get_default_output(struct weston_compositor *compositor)
-{
-	return container_of(compositor->output_list.next,
-			    struct weston_output, link);
-}
-
 static void
 shell_unset_fullscreen(struct shell_surface *shsurf)
 {
@@ -2907,6 +3102,7 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 	struct weston_surface *main_surface;
 	struct focus_state *state;
 	struct workspace *ws;
+	struct weston_surface *old_es;
 
 	main_surface = weston_surface_get_main_surface(es);
 
@@ -2916,6 +3112,7 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 	if (state == NULL)
 		return;
 
+	old_es = state->keyboard_focus;
 	state->keyboard_focus = es;
 	wl_list_remove(&state->surface_destroy_listener.link);
 	wl_signal_add(&es->resource.destroy_signal,
@@ -2926,12 +3123,15 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 		/* should on top of panels */
 		shell_stack_fullscreen(get_shell_surface(main_surface));
 		shell_configure_fullscreen(get_shell_surface(main_surface));
-		break;
+		return;
 	default:
 		ws = get_current_workspace(shell);
 		weston_surface_restack(main_surface, &ws->layer.surface_list);
 		break;
 	}
+
+	if (shell->focus_animation_type != ANIMATION_NONE)
+		animate_focus_change(shell, ws, old_es, es);
 }
 
 /* no-op func for checking black surface */
@@ -3967,6 +4167,9 @@ switcher_destroy(struct switcher *switcher)
 	struct workspace *ws = get_current_workspace(switcher->shell);
 
 	wl_list_for_each(surface, &ws->layer.surface_list, layer_link) {
+		if (is_focus_surface(surface))
+			continue;
+
 		surface->alpha = 1.0;
 		weston_surface_damage(surface);
 	}
-- 
1.8.1.5



More information about the wayland-devel mailing list