[PATCH 1/9] animation, shell: add kbd focus change animation

Emilio Pozuelo Monfort pochu27 at gmail.com
Tue Nov 19 02:37:11 PST 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.]

[pochu: rebased, ported to weston_view]
---
 src/animation.c  |  53 +++++++++--
 src/compositor.h |   8 ++
 src/shell.c      | 262 +++++++++++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 293 insertions(+), 30 deletions(-)

diff --git a/src/animation.c b/src/animation.c
index c71b506..8739f19 100644
--- a/src/animation.c
+++ b/src/animation.c
@@ -127,9 +127,10 @@ struct weston_view_animation {
 	weston_view_animation_frame_func_t reset;
 	weston_view_animation_done_func_t done;
 	void *data;
+	void *private;
 };
 
-static void
+WL_EXPORT void
 weston_view_animation_destroy(struct weston_view_animation *animation)
 {
 	wl_list_remove(&animation->animation.link);
@@ -185,7 +186,8 @@ weston_view_animation_run(struct weston_view *view,
 			  weston_view_animation_frame_func_t frame,
 			  weston_view_animation_frame_func_t reset,
 			  weston_view_animation_done_func_t done,
-			  void *data)
+			  void *data,
+			  void *private)
 {
 	struct weston_view_animation *animation;
 
@@ -200,6 +202,7 @@ weston_view_animation_run(struct weston_view *view,
 	animation->data = data;
 	animation->start = start;
 	animation->stop = stop;
+	animation->private = private;
 	weston_matrix_init(&animation->transform.matrix);
 	wl_list_insert(&view->geometry.transformation_list,
 		       &animation->transform.link);
@@ -257,7 +260,7 @@ weston_zoom_run(struct weston_view *view, float start, float stop,
 
 	zoom = weston_view_animation_run(view, start, stop,
 					 zoom_frame, reset_alpha,
-					 done, data);
+					 done, data, NULL);
 
 	weston_spring_init(&zoom->spring, 300.0, start, stop);
 	zoom->spring.friction = 1400;
@@ -286,7 +289,7 @@ weston_fade_run(struct weston_view *view,
 
 	fade = weston_view_animation_run(view, 0, end,
 					 fade_frame, reset_alpha,
-					 done, data);
+					 done, data, NULL);
 
 	weston_spring_init(&fade->spring, k, start, end);
 
@@ -305,6 +308,46 @@ weston_fade_update(struct weston_view_animation *fade, float target)
 }
 
 static void
+stable_fade_frame(struct weston_view_animation *animation)
+{
+	struct weston_view *back_view;
+
+	if (animation->spring.current > 0.999)
+		animation->view->alpha = 1;
+	else if (animation->spring.current < 0.001 )
+		animation->view->alpha = 0;
+	else
+		animation->view->alpha = animation->spring.current;
+
+	back_view = (struct weston_view *) animation->private;
+	back_view->alpha =
+		(animation->spring.target - animation->view->alpha) /
+		(1.0 - animation->view->alpha);
+	weston_view_geometry_dirty(back_view);
+}
+
+WL_EXPORT struct weston_view_animation *
+weston_stable_fade_run(struct weston_view *front_view, float start,
+		struct weston_view *back_view, float end,
+		weston_view_animation_done_func_t done, void *data)
+{
+	struct weston_view_animation *fade;
+
+	fade = weston_view_animation_run(front_view, 0, 0,
+					    stable_fade_frame, NULL,
+					    done, data, back_view);
+
+
+	weston_spring_init(&fade->spring, 400, start, end);
+	fade->spring.friction = 1150;
+
+	front_view->alpha = start;
+	back_view->alpha = end;
+
+	return fade;
+}
+
+static void
 slide_frame(struct weston_view_animation *animation)
 {
 	float scale;
@@ -324,7 +367,7 @@ weston_slide_run(struct weston_view *view, float start, float stop,
 
 	animation = weston_view_animation_run(view, start, stop,
 					      slide_frame, NULL, done,
-					      data);
+					      data, NULL);
 	if (!animation)
 		return NULL;
 
diff --git a/src/compositor.h b/src/compositor.h
index 80fe961..2ceac2d 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -1271,6 +1271,9 @@ weston_watch_process(struct weston_process *process);
 struct weston_view_animation;
 typedef	void (*weston_view_animation_done_func_t)(struct weston_view_animation *animation, void *data);
 
+void
+weston_view_animation_destroy(struct weston_view_animation *animation);
+
 struct weston_view_animation *
 weston_zoom_run(struct weston_view *view, float start, float stop,
 		weston_view_animation_done_func_t done, void *data);
@@ -1283,6 +1286,11 @@ void
 weston_fade_update(struct weston_view_animation *fade, float target);
 
 struct weston_view_animation *
+weston_stable_fade_run(struct weston_view *front_view, float start,
+		       struct weston_view *back_view, float end,
+		       weston_view_animation_done_func_t done, void *data);
+
+struct weston_view_animation *
 weston_slide_run(struct weston_view *view, float start, float stop,
 		 weston_view_animation_done_func_t done, void *data);
 
diff --git a/src/shell.c b/src/shell.c
index fe332e1..9ad36bb 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -47,7 +47,8 @@ enum animation_type {
 	ANIMATION_NONE,
 
 	ANIMATION_ZOOM,
-	ANIMATION_FADE
+	ANIMATION_FADE,
+	ANIMATION_DIM_LAYER,
 };
 
 enum fade_type {
@@ -64,11 +65,21 @@ struct focus_state {
 	struct wl_listener surface_destroy_listener;
 };
 
+struct focus_surface {
+	struct weston_surface *surface;
+	struct weston_view *view;
+	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_view_animation *focus_animation;
 };
 
 struct input_panel_surface {
@@ -172,6 +183,7 @@ struct desktop_shell {
 	uint32_t binding_modifier;
 	enum animation_type win_animation_type;
 	enum animation_type startup_animation_type;
+	enum animation_type focus_animation_type;
 
 	struct wl_listener output_create_listener;
 	struct wl_list output_list;
@@ -459,6 +471,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;
 }
@@ -492,12 +506,103 @@ shell_configuration(struct desktop_shell *shell)
 	free(s);
 	if (shell->startup_animation_type == ANIMATION_ZOOM)
 		shell->startup_animation_type = ANIMATION_NONE;
-
+	weston_config_section_get_string(section, "focus-animation", &s, "none");
+	shell->focus_animation_type = get_animation_type(s);
+	free(s);
 	weston_config_section_get_uint(section, "num-workspaces",
 				       &shell->workspaces.num,
 				       DEFAULT_NUM_WORKSPACES);
 }
 
+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 bool
+is_focus_view (struct weston_view *view)
+{
+	return is_focus_surface (view->surface);
+}
+
+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;
+
+	fsurf->view = weston_view_create (surface);
+
+	weston_view_configure(fsurf->view, 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_view_animation *animation, void *data)
+{
+	struct workspace *ws = data;
+
+	ws->focus_animation = NULL;
+}
+
 static void
 focus_state_destroy(struct focus_state *state)
 {
@@ -533,6 +638,8 @@ focus_state_surface_destroy(struct wl_listener *listener, void *data)
 	wl_list_for_each(view, &state->ws->layer.view_list, layer_link) {
 		if (view->surface == main_surface)
 			continue;
+		if (is_focus_view(view))
+			continue;
 
 		next = view->surface;
 		break;
@@ -542,10 +649,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_view_animation_destroy(state->ws->focus_animation);
+
+			state->ws->focus_animation = weston_fade_run(
+				state->ws->fsurf_front->view,
+				state->ws->fsurf_front->view->alpha, 0.0, 300,
+				focus_animation_done, state->ws);
+		}
+
 		wl_list_remove(&state->link);
 		focus_state_destroy(state);
 	}
@@ -560,6 +678,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);
@@ -630,6 +749,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_view *from, struct weston_view *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->view->alpha = 0.0;
+		ws->fsurf_back->view->alpha = 0.0;
+		focus_surface_created = true;
+	} else {
+		wl_list_remove(&ws->fsurf_front->view->layer_link);
+		wl_list_remove(&ws->fsurf_back->view->layer_link);
+	}
+
+	if (ws->focus_animation) {
+		weston_view_animation_destroy(ws->focus_animation);
+		ws->focus_animation = NULL;
+	}
+
+	if (to)
+		wl_list_insert(&to->layer_link,
+			       &ws->fsurf_front->view->layer_link);
+	else if (from)
+		wl_list_insert(&ws->layer.view_list,
+			       &ws->fsurf_front->view->layer_link);
+
+	if (focus_surface_created) {
+		ws->focus_animation = weston_fade_run(
+			ws->fsurf_front->view,
+			ws->fsurf_front->view->alpha, 0.6, 300,
+			focus_animation_done, ws);
+	} else if (from) {
+		wl_list_insert(&from->layer_link,
+			       &ws->fsurf_back->view->layer_link);
+		ws->focus_animation = weston_stable_fade_run(
+			ws->fsurf_front->view, 0.0,
+			ws->fsurf_back->view, 0.6,
+			focus_animation_done, ws);
+	} else if (to) {
+		wl_list_insert(&ws->layer.view_list,
+			       &ws->fsurf_back->view->layer_link);
+		ws->focus_animation = weston_stable_fade_run(
+			ws->fsurf_front->view, 0.0,
+			ws->fsurf_back->view, 0.6,
+			focus_animation_done, ws);
+	}
+}
+
+static void
 workspace_destroy(struct workspace *ws)
 {
 	struct focus_state *state, *next;
@@ -637,6 +813,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);
 }
 
@@ -666,6 +847,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;
 }
@@ -709,18 +893,24 @@ get_output_height(struct weston_output *output)
 }
 
 static void
-view_translate(struct weston_view *view, double d)
+view_translate(struct workspace *ws, struct weston_view *view, double d)
 {
-	struct shell_surface *shsurf = get_shell_surface(view->surface);
 	struct weston_transform *transform;
 
-	transform = &shsurf->workspace_transform;
+	if (is_focus_view(view)) {
+		struct focus_surface *fsurf = get_focus_surface(view->surface);
+		transform = &fsurf->workspace_transform;
+	} else {
+		struct shell_surface *shsurf = get_shell_surface(view->surface);
+		transform = &shsurf->workspace_transform;
+	}
+
 	if (wl_list_empty(&transform->link))
 		wl_list_insert(view->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_view_geometry_dirty(view);
 }
@@ -736,7 +926,7 @@ workspace_translate_out(struct workspace *ws, double fraction)
 		height = get_output_height(view->surface->output);
 		d = height * fraction;
 
-		view_translate(view, d);
+		view_translate(ws, view, d);
 	}
 }
 
@@ -755,7 +945,7 @@ workspace_translate_in(struct workspace *ws, double fraction)
 		else
 			d = height + height * fraction;
 
-		view_translate(view, d);
+		view_translate(ws, view, d);
 	}
 }
 
@@ -790,13 +980,20 @@ static void
 workspace_deactivate_transforms(struct workspace *ws)
 {
 	struct weston_view *view;
-	struct shell_surface *shsurf;
+	struct weston_transform *transform;
 
 	wl_list_for_each(view, &ws->layer.view_list, layer_link) {
-		shsurf = get_shell_surface(view->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_view(view)) {
+			struct focus_surface *fsurf = get_focus_surface(view->surface);
+			transform = &fsurf->workspace_transform;
+		} else {
+			struct shell_surface *shsurf = get_shell_surface(view->surface);
+			transform = &shsurf->workspace_transform;
+		}
+
+		if (!wl_list_empty(&transform->link)) {
+			wl_list_remove(&transform->link);
+			wl_list_init(&transform->link);
 		}
 		weston_view_geometry_dirty(view);
 	}
@@ -919,6 +1116,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;
@@ -945,6 +1143,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,
+						     get_default_view(state->keyboard_focus), NULL);
+
+		wl_list_for_each(state, &to->focus_list, link)
+			if (state->keyboard_focus)
+				animate_focus_change(shell, to,
+						     NULL, get_default_view(state->keyboard_focus));
+	}
+
 	if (workspace_is_empty(to) && workspace_is_empty(from))
 		update_workspace(shell, index, from, to);
 	else
@@ -1022,7 +1232,8 @@ take_surface_to_workspace_by_seat(struct desktop_shell *shell,
 	surface = weston_surface_get_main_surface(seat->keyboard->focus);
 	view = get_default_view(surface);
 	if (view == NULL ||
-	    index == shell->workspaces.current)
+	    index == shell->workspaces.current ||
+	    is_focus_view(view))
 		return;
 
 	from = get_current_workspace(shell);
@@ -1727,13 +1938,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
 restore_output_mode(struct weston_output *output)
 {
@@ -3277,6 +3481,7 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 	struct weston_view *main_view;
 	struct focus_state *state;
 	struct workspace *ws;
+	struct weston_surface *old_es;
 
 	main_surface = weston_surface_get_main_surface(es);
 
@@ -3286,6 +3491,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->destroy_signal, &state->surface_destroy_listener);
@@ -3295,7 +3501,7 @@ 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:
 		restore_all_output_modes(shell->compositor);
 		ws = get_current_workspace(shell);
@@ -3304,6 +3510,9 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 			weston_view_restack(main_view, &ws->layer.view_list);
 		break;
 	}
+
+	if (shell->focus_animation_type != ANIMATION_NONE)
+		animate_focus_change(shell, ws, get_default_view(old_es), get_default_view(es));
 }
 
 /* no-op func for checking black surface */
@@ -4391,6 +4600,9 @@ switcher_destroy(struct switcher *switcher)
 	struct workspace *ws = get_current_workspace(switcher->shell);
 
 	wl_list_for_each(view, &ws->layer.view_list, layer_link) {
+		if (is_focus_view(view))
+			continue;
+
 		view->alpha = 1.0;
 		weston_surface_damage(view->surface);
 	}
-- 
1.8.4.rc3



More information about the wayland-devel mailing list