[PATCH weston v3 04/13] shell: keyboard focus and restacking fixes for sub-surfaces

Pekka Paalanen ppaalanen at gmail.com
Thu Apr 25 03:57:44 PDT 2013


The shell needs to redirect some actions to the parent surface, when
they originally target a sub-surface. This patch implements the
following:

- Move, resize, and rotate bindings always target the parent surface.

- Opacity (full-surface alpha) binding targets the parent surface. This
  is broken, because it should change the opacity of the whole compound
  window, which is difficult to implement in the renderer.

- click_to_activate_binding() needs to check the shell surface type from
  the main surface, because sub-surface would produce SHELL_SURFACE_NONE
  and prevent activation.

- Also activate() needs to check the type from the main surface, and
  restack the main surface. Keyboard focus is assigned to the original
  (sub-)surface.

- focus_state_surface_destroy() needs to handle sub-surfaces: only the
  main surface will be in a layer list. If the destroyed surface is
  indeed a sub-surface, activate the main surface next. This way a
  client that destroys a focused sub-surface still retains focus in the
  same window.

- The workspace_manager.move_surface request can accept also
  sub-surfaces, and it will move the corresponding main surface.

Changes in v2:
- do not special-case keyboard focus for sub-surfaces
- fix surface type checks for sub-surfaces in shell, fix restacking of
  sub-surfaces in shell, fix focus_state_surface_destroy()

Changes in v3:
- Renamed weston_surface_get_parent() to
  weston_surface_get_main_surface() to be more explicit that this is
  about sub-surfaces
- Fixed move_surface_to_workspace() to handle keyboard focus on a
  sub-surface.
- Used a temporary variable in several places to clarify code, instead
  of reassigning a variable.
- Fixed workspace_manager_move_surface() to deal with sub-surfaces.

Signed-off-by: Pekka Paalanen <ppaalanen at gmail.com>
---
 src/compositor.c | 11 ++++++++++
 src/compositor.h |  3 +++
 src/shell.c      | 67 +++++++++++++++++++++++++++++++++++++++++---------------
 3 files changed, 63 insertions(+), 18 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index 000f6bf..b9073f0 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1963,6 +1963,17 @@ weston_surface_to_subsurface(struct weston_surface *surface)
 	return NULL;
 }
 
+WL_EXPORT struct weston_surface *
+weston_surface_get_main_surface(struct weston_surface *surface)
+{
+	struct weston_subsurface *sub;
+
+	while (surface && (sub = weston_surface_to_subsurface(surface)))
+		surface = sub->parent;
+
+	return surface;
+}
+
 static void
 subsurface_set_position(struct wl_client *client,
 			struct wl_resource *resource, int32_t x, int32_t y)
diff --git a/src/compositor.h b/src/compositor.h
index f102a4a..53a8f91 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -789,6 +789,9 @@ weston_surface_move_to_plane(struct weston_surface *surface,
 void
 weston_surface_unmap(struct weston_surface *surface);
 
+struct weston_surface *
+weston_surface_get_main_surface(struct weston_surface *surface);
+
 void
 weston_buffer_reference(struct weston_buffer_reference *ref,
 			struct wl_buffer *buffer);
diff --git a/src/shell.c b/src/shell.c
index 9d0ae02..99949b7 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -427,17 +427,24 @@ focus_state_surface_destroy(struct wl_listener *listener, void *data)
 						 struct focus_state,
 						 surface_destroy_listener);
 	struct desktop_shell *shell;
+	struct weston_surface *main_surface;
 	struct weston_surface *surface, *next;
 
+	main_surface = weston_surface_get_main_surface(state->keyboard_focus);
+
 	next = NULL;
 	wl_list_for_each(surface, &state->ws->layer.surface_list, layer_link) {
-		if (surface == state->keyboard_focus)
+		if (surface == main_surface)
 			continue;
 
 		next = surface;
 		break;
 	}
 
+	/* if the focus was a sub-surface, activate its main surface */
+	if (main_surface != state->keyboard_focus)
+		next = main_surface;
+
 	if (next) {
 		shell = state->seat->compositor->shell_interface.shell;
 		activate(shell, next, state->seat);
@@ -876,6 +883,9 @@ move_surface_to_workspace(struct desktop_shell *shell,
 	struct workspace *from;
 	struct workspace *to;
 	struct weston_seat *seat;
+	struct weston_surface *focus;
+
+	assert(weston_surface_get_main_surface(surface) == surface);
 
 	if (workspace == shell->workspaces.current)
 		return;
@@ -890,10 +900,14 @@ move_surface_to_workspace(struct desktop_shell *shell,
 	wl_list_insert(&to->layer.surface_list, &surface->layer_link);
 
 	drop_focus_state(shell, from, surface);
-	wl_list_for_each(seat, &shell->compositor->seat_list, link)
-		if (seat->has_keyboard &&
-		    seat->keyboard.keyboard.focus == &surface->surface)
+	wl_list_for_each(seat, &shell->compositor->seat_list, link) {
+		if (!seat->has_keyboard)
+			continue;
+
+		focus = (struct weston_surface *)seat->keyboard.keyboard.focus;
+		if (weston_surface_get_main_surface(focus) == surface)
 			wl_keyboard_set_focus(&seat->keyboard.keyboard, NULL);
+	}
 
 	weston_surface_damage_below(surface);
 }
@@ -904,13 +918,15 @@ take_surface_to_workspace_by_seat(struct desktop_shell *shell,
 				  unsigned int index)
 {
 	struct weston_seat *seat = (struct weston_seat *) wl_seat;
-	struct weston_surface *surface =
+	struct weston_surface *focus =
 		(struct weston_surface *) wl_seat->keyboard->focus;
+	struct weston_surface *surface;
 	struct shell_surface *shsurf;
 	struct workspace *from;
 	struct workspace *to;
 	struct focus_state *state;
 
+	surface = weston_surface_get_main_surface(focus);
 	if (surface == NULL ||
 	    index == shell->workspaces.current)
 		return;
@@ -968,8 +984,10 @@ workspace_manager_move_surface(struct wl_client *client,
 	struct desktop_shell *shell = resource->data;
 	struct weston_surface *surface =
 		(struct weston_surface *) surface_resource;
+	struct weston_surface *main_surface;
 
-	move_surface_to_workspace(shell, surface, workspace);
+	main_surface = weston_surface_get_main_surface(surface);
+	move_surface_to_workspace(shell, main_surface, workspace);
 }
 
 static const struct workspace_manager_interface workspace_manager_implementation = {
@@ -2511,10 +2529,12 @@ get_shell_surface_type(struct weston_surface *surface)
 static void
 move_binding(struct wl_seat *seat, uint32_t time, uint32_t button, void *data)
 {
-	struct weston_surface *surface =
+	struct weston_surface *focus =
 		(struct weston_surface *) seat->pointer->focus;
+	struct weston_surface *surface;
 	struct shell_surface *shsurf;
 
+	surface = weston_surface_get_main_surface(focus);
 	if (surface == NULL)
 		return;
 
@@ -2529,12 +2549,14 @@ move_binding(struct wl_seat *seat, uint32_t time, uint32_t button, void *data)
 static void
 resize_binding(struct wl_seat *seat, uint32_t time, uint32_t button, void *data)
 {
-	struct weston_surface *surface =
+	struct weston_surface *focus =
 		(struct weston_surface *) seat->pointer->focus;
+	struct weston_surface *surface;
 	uint32_t edges = 0;
 	int32_t x, y;
 	struct shell_surface *shsurf;
 
+	surface = weston_surface_get_main_surface(focus);
 	if (surface == NULL)
 		return;
 
@@ -2571,9 +2593,12 @@ surface_opacity_binding(struct wl_seat *seat, uint32_t time, uint32_t axis,
 {
 	float step = 0.005;
 	struct shell_surface *shsurf;
-	struct weston_surface *surface =
+	struct weston_surface *focus =
 		(struct weston_surface *) seat->pointer->focus;
+	struct weston_surface *surface;
 
+	/* XXX: broken for windows containing sub-surfaces */
+	surface = weston_surface_get_main_surface(focus);
 	if (surface == NULL)
 		return;
 
@@ -2791,10 +2816,12 @@ static void
 rotate_binding(struct wl_seat *seat, uint32_t time, uint32_t button,
 	       void *data)
 {
-	struct weston_surface *base_surface =
+	struct weston_surface *focus =
 		(struct weston_surface *) seat->pointer->focus;
+	struct weston_surface *base_surface;
 	struct shell_surface *surface;
 
+	base_surface = weston_surface_get_main_surface(focus);
 	if (base_surface == NULL)
 		return;
 
@@ -2823,9 +2850,12 @@ static void
 activate(struct desktop_shell *shell, struct weston_surface *es,
 	 struct weston_seat *seat)
 {
+	struct weston_surface *main_surface;
 	struct focus_state *state;
 	struct workspace *ws;
 
+	main_surface = weston_surface_get_main_surface(es);
+
 	weston_surface_activate(es, seat);
 
 	state = ensure_focus_state(shell, seat);
@@ -2837,15 +2867,15 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 	wl_signal_add(&es->surface.resource.destroy_signal,
 		      &state->surface_destroy_listener);
 
-	switch (get_shell_surface_type(es)) {
+	switch (get_shell_surface_type(main_surface)) {
 	case SHELL_SURFACE_FULLSCREEN:
 		/* should on top of panels */
-		shell_stack_fullscreen(get_shell_surface(es));
-		shell_configure_fullscreen(get_shell_surface(es));
+		shell_stack_fullscreen(get_shell_surface(main_surface));
+		shell_configure_fullscreen(get_shell_surface(main_surface));
 		break;
 	default:
 		ws = get_current_workspace(shell);
-		weston_surface_restack(es, &ws->layer.surface_list);
+		weston_surface_restack(main_surface, &ws->layer.surface_list);
 		break;
 	}
 }
@@ -2874,16 +2904,17 @@ click_to_activate_binding(struct wl_seat *seat, uint32_t time, uint32_t button,
 	struct weston_seat *ws = (struct weston_seat *) seat;
 	struct desktop_shell *shell = data;
 	struct weston_surface *focus;
-	struct weston_surface *upper;
+	struct weston_surface *main_surface;
 
 	focus = (struct weston_surface *) seat->pointer->focus;
 	if (!focus)
 		return;
 
-	if (is_black_surface(focus, &upper))
-		focus = upper;
+	if (is_black_surface(focus, &main_surface))
+		focus = main_surface;
 
-	if (get_shell_surface_type(focus) == SHELL_SURFACE_NONE)
+	main_surface = weston_surface_get_main_surface(focus);
+	if (get_shell_surface_type(main_surface) == SHELL_SURFACE_NONE)
 		return;
 
 	if (seat->pointer->grab == &seat->pointer->default_grab)
-- 
1.8.1.5



More information about the wayland-devel mailing list