[PATCH weston 10/11] shell: Update the workspace protocol to support multiple outputs

Jonas Ådahl jadahl at gmail.com
Sat Jan 26 06:33:40 PST 2013


Make so that the protocol can represent and manipulate workspace states
with multiple outputs. Per-output workspaces are represented as
containers with unique id's. If the compositor would implement global
workspaces one would just advertise one container.

The desktop shell and the weston client toolkit was updated to implement
the new protocol. Weston toolkit clients no links with libshared in
order to use the hash table implementation.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
 clients/Makefile.am     |    1 +
 clients/window.c        |   86 +++++++++++----
 protocol/workspaces.xml |   20 +++-
 src/shell.c             |  264 ++++++++++++++++++++++++++++++-----------------
 4 files changed, 256 insertions(+), 115 deletions(-)

diff --git a/clients/Makefile.am b/clients/Makefile.am
index c1b65ce..2878033 100644
--- a/clients/Makefile.am
+++ b/clients/Makefile.am
@@ -86,6 +86,7 @@ libtoytoolkit_a_SOURCES =			\
 
 toolkit_libs =						\
 	libtoytoolkit.a					\
+	../shared/libshared.la				\
 	../shared/libshared-cairo.la			\
 	$(CLIENT_LIBS) $(CAIRO_EGL_LIBS) -lrt -lm
 
diff --git a/clients/window.c b/clients/window.c
index 799926c..f824efb 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -68,6 +68,7 @@ typedef void *EGLContext;
 #include <linux/input.h>
 #include <wayland-client.h>
 #include "../shared/cairo-util.h"
+#include "../shared/hash.h"
 #include "text-cursor-position-client-protocol.h"
 #include "workspaces-client-protocol.h"
 #include "../shared/os-compatibility.h"
@@ -124,8 +125,7 @@ struct display {
 
 	struct xkb_context *xkb_context;
 
-	uint32_t workspace;
-	uint32_t workspace_count;
+	struct hash_table *workspace_containers;
 
 	/* A hack to get text extents for tooltips */
 	cairo_surface_t *dummy_surface;
@@ -191,6 +191,11 @@ struct toysurface {
 	void (*destroy)(struct toysurface *base);
 };
 
+struct workspace_container_state {
+	unsigned int current;
+	unsigned int count;
+};
+
 struct window {
 	struct display *display;
 	struct window *parent;
@@ -236,6 +241,9 @@ struct window {
 
 	void *user_data;
 	struct wl_list link;
+
+	uint32_t workspace_container;
+	uint32_t workspace_index;
 };
 
 struct widget {
@@ -1677,19 +1685,43 @@ widget_set_tooltip(struct widget *parent, char *entry, float x, float y)
 }
 
 static void
-workspace_manager_state(void *data,
-			struct workspace_manager *workspace_manager,
-			uint32_t current,
-			uint32_t count)
+workspace_manager_container_state(void *data,
+				  struct workspace_manager *workspace_manager,
+				  uint32_t container,
+				  uint32_t current,
+				  uint32_t count)
 {
 	struct display *display = data;
 
-	display->workspace = current;
-	display->workspace_count = count;
+	struct workspace_container_state *state =
+		hash_table_lookup(display->workspace_containers, container);
+
+	if (!state) {
+		state = malloc(sizeof *state);
+		hash_table_insert(display->workspace_containers,
+				  container, state);
+	}
+
+	state->current = current;
+	state->count = count;
+}
+
+static void
+workspace_manager_surface_state(void *data,
+				struct workspace_manager *workspace_manager,
+				struct wl_surface *surface,
+				uint32_t container,
+				uint32_t workspace)
+{
+	struct window *window = wl_surface_get_user_data(surface);
+
+	window->workspace_container = container;
+	window->workspace_index = workspace;
 }
 
 static const struct workspace_manager_listener workspace_manager_listener = {
-	workspace_manager_state
+	workspace_manager_container_state,
+	workspace_manager_surface_state
 };
 
 static void
@@ -2064,6 +2096,7 @@ static void
 frame_menu_func(struct window *window, int index, void *data)
 {
 	struct display *display;
+	struct workspace_container_state *state;
 
 	switch (index) {
 	case 0: /* close */
@@ -2075,17 +2108,23 @@ frame_menu_func(struct window *window, int index, void *data)
 		break;
 	case 1: /* move to workspace above */
 		display = window->display;
-		if (display->workspace > 0)
-			workspace_manager_move_surface(display->workspace_manager,
-						       window->surface,
-						       display->workspace - 1);
+		if (window->workspace_index > 0)
+			workspace_manager_move_surface(
+				display->workspace_manager,
+				window->surface,
+				window->workspace_container,
+				window->workspace_index - 1);
 		break;
 	case 2: /* move to workspace below */
 		display = window->display;
-		if (display->workspace < display->workspace_count - 1)
-			workspace_manager_move_surface(display->workspace_manager,
-						       window->surface,
-						       display->workspace + 1);
+		state = hash_table_lookup(display->workspace_containers,
+					  window->workspace_container);
+		if (window->workspace_index < state->count - 1)
+			workspace_manager_move_surface(
+				display->workspace_manager,
+				window->surface,
+				window->workspace_container,
+				window->workspace_index + 1);
 		break;
 	case 3: /* fullscreen */
 		/* we don't have a way to get out of fullscreen for now */
@@ -4284,8 +4323,7 @@ display_create(int argc, char *argv[])
 		return NULL;
 	}
 
-	d->workspace = 0;
-	d->workspace_count = 1;
+	d->workspace_containers = hash_table_create();
 
 	d->registry = wl_display_get_registry(d->display);
 	wl_registry_add_listener(d->registry, &registry_listener, d);
@@ -4325,6 +4363,12 @@ display_destroy_inputs(struct display *display)
 		input_destroy(input);
 }
 
+static void
+destroy_workspace_container_state(void *element, void *data)
+{
+	free(element);
+}
+
 void
 display_destroy(struct display *display)
 {
@@ -4351,6 +4395,10 @@ display_destroy(struct display *display)
 		fini_egl(display);
 #endif
 
+	hash_table_for_each(display->workspace_containers,
+			    destroy_workspace_container_state, NULL);
+	hash_table_destroy(display->workspace_containers);
+
 	if (display->shell)
 		wl_shell_destroy(display->shell);
 
diff --git a/protocol/workspaces.xml b/protocol/workspaces.xml
index 22f4802..b1db223 100644
--- a/protocol/workspaces.xml
+++ b/protocol/workspaces.xml
@@ -3,6 +3,9 @@
   <interface name="workspace_manager" version="1">
     <description summary="workspaces manager">
       An interface for managing surfaces in workspaces.
+
+      A workspace is identified by an index in an array associated with a
+      container id.
     </description>
 
     <request name="move_surface">
@@ -10,18 +13,29 @@
 	Move the given surface to the specified workspace.
       </description>
       <arg name="surface" type="object" interface="wl_surface"/>
+      <arg name="container" type="uint"/>
       <arg name="workspace" type="uint"/>
     </request>
 
-    <event name="state">
+    <event name="container_state">
       <description summary="workspace state">
-	The current workspace state, such as current workspace and workspace
-	count, has changed.
+	The current state of the workspace container specified by
+	`container'.
       </description>
+      <arg name="container" type="uint"/>
       <arg name="current" type="uint"/>
       <arg name="count" type="uint"/>
     </event>
 
+    <event name="surface_state">
+      <description summary="surface state">
+	The current workspace state of the given surface.
+      </description>
+      <arg name="surface" type="object" interface="wl_surface"/>
+      <arg name="container" type="uint"/>
+      <arg name="workspace" type="uint"/>
+    </event>
+
   </interface>
 
 </protocol>
diff --git a/src/shell.c b/src/shell.c
index ad5800d..af45347 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -585,15 +585,60 @@ workspace_is_empty(struct workspace *ws)
 	return true;
 }
 
+static uint32_t
+workspace_container_get_id(struct workspace_container *container)
+{
+	return container->output->id;
+}
+
+static uint32_t
+workspace_get_index(struct workspace *ws)
+{
+	struct workspace **pws;
+	uint32_t index = 0;
+
+	wl_array_for_each(pws, &ws->container->workspaces) {
+		if (*pws == ws)
+			return index;
+		index++;
+	}
+
+	return 0;
+}
+
+static void
+send_surface_workspace_state(struct shell_surface *shsurf)
+{
+	struct wl_resource *resource;
+	struct workspace *ws = shsurf->workspace;
+	struct desktop_shell *shell = ws->container->shell;
+
+	wl_list_for_each(resource, &shell->workspaces.client_list, link) {
+		if (resource->client == shsurf->resource.client) {
+			workspace_manager_send_surface_state(
+				resource,
+				&shsurf->surface->surface.resource,
+				workspace_container_get_id(ws->container),
+				workspace_get_index(ws));
+
+			return;
+		}
+	}
+}
+
 static void
 workspace_stack_surface_above(struct workspace *ws, struct wl_list *below,
 			      struct shell_surface *shsurf)
 {
 	struct desktop_shell *shell = shsurf->shell;
+	struct workspace *old_ws = shsurf->workspace;
 
 	wl_list_insert(below, &shsurf->link);
 	shell->stack_dirty = true;
 	shsurf->workspace = ws;
+
+	if (old_ws != ws)
+		send_surface_workspace_state(shsurf);
 }
 
 static void
@@ -800,16 +845,16 @@ workspace_translate_in(struct workspace *ws, double fraction)
 }
 
 static void
-broadcast_current_workspace_state(struct desktop_shell *shell)
+broadcast_current_workspace_state(struct workspace_container *container)
 {
+	struct desktop_shell *shell = container->shell;
 	struct wl_resource *resource;
-	struct workspace_container *container =
-		find_first_workspace_container(shell);
 
 	wl_list_for_each(resource, &shell->workspaces.client_list, link)
-		workspace_manager_send_state(resource,
-					     container->current,
-					     container->num);
+		workspace_manager_send_container_state(resource,
+						       container->output->id,
+						       container->current,
+						       container->num);
 }
 
 static void
@@ -986,7 +1031,7 @@ change_workspace(struct desktop_shell *shell,
 		restore_focus_state(shell, to);
 		reverse_workspace_change_animation(shell, container, index,
 						   from, to);
-		broadcast_current_workspace_state(shell);
+		broadcast_current_workspace_state(container);
 		return;
 	}
 
@@ -1002,7 +1047,7 @@ change_workspace(struct desktop_shell *shell,
 	else
 		animate_workspace_change(shell, container, index, from, to);
 
-	broadcast_current_workspace_state(shell);
+	broadcast_current_workspace_state(container);
 }
 
 static bool
@@ -1024,36 +1069,6 @@ workspace_has_only(struct workspace *ws, struct weston_surface *surface)
 }
 
 static void
-move_surface_to_workspace(struct desktop_shell *shell,
-			  struct weston_surface *surface,
-			  struct workspace_container *container,
-			  uint32_t workspace)
-{
-	struct workspace *from;
-	struct workspace *to;
-	struct shell_surface *shsurf = get_shell_surface(surface);
-	struct weston_seat *seat;
-
-	if (!shsurf ||
-	    workspace >= container->num ||
-	    workspace == container->current)
-		return;
-
-	from = shsurf->workspace;
-	to = workspace_container_get(container, workspace);
-
-	workspace_restack_surface(to, shsurf);
-
-	drop_focus_state(shell, from, surface);
-	wl_list_for_each(seat, &shell->compositor->seat_list, link)
-		if (seat->has_keyboard &&
-		    seat->keyboard.focus == &surface->surface)
-			wl_keyboard_set_focus(&seat->keyboard, NULL);
-
-	weston_surface_damage_below(surface);
-}
-
-static void
 take_surface_to_workspace_by_seat(struct desktop_shell *shell,
 				  struct wl_seat *wl_seat,
 				  struct workspace_container *container,
@@ -1087,7 +1102,7 @@ take_surface_to_workspace_by_seat(struct desktop_shell *shell,
 	    container->anim_to == from) {
 		reverse_workspace_change_animation(shell, container, index,
 						   from, to);
-		broadcast_current_workspace_state(shell);
+		broadcast_current_workspace_state(container);
 
 		return;
 	}
@@ -1108,26 +1123,108 @@ take_surface_to_workspace_by_seat(struct desktop_shell *shell,
 		animate_workspace_change(shell, container, index, from, to);
 	}
 
-	broadcast_current_workspace_state(shell);
+	broadcast_current_workspace_state(container);
 
 	state = ensure_focus_state(shell, to, seat);
 	if (state != NULL)
 		state->keyboard_focus = surface;
 }
 
+static int
+get_output_panel_height(struct desktop_shell *shell,
+			struct weston_output *output)
+{
+	struct weston_surface *surface;
+	int panel_height = 0;
+
+	if (!output)
+		return 0;
+
+	wl_list_for_each(surface, &shell->panel_layer.surface_list, layer_link) {
+		if (surface->output == output) {
+			panel_height = surface->geometry.height;
+			break;
+		}
+	}
+
+	return panel_height;
+}
+
+static void
+surface_position_on_output(struct weston_surface *surface,
+			   struct desktop_shell *shell,
+			   struct weston_output *output)
+{
+	int range_x, range_y;
+	int dx, dy, x, y, panel_height;
+
+	/* Valid range within output where the surface will still be onscreen.
+	 * If this is negative it means that the surface is bigger than
+	 * output.
+	 */
+	panel_height = get_output_panel_height(shell, output);
+	range_x = output->width - surface->geometry.width;
+	range_y = (output->height - panel_height) -
+		  surface->geometry.height;
+
+	if (range_x > 0)
+		dx = random() % range_x;
+	else
+		dx = 0;
+
+	if (range_y > 0)
+		dy = panel_height + random() % range_y;
+	else
+		dy = panel_height;
+
+	x = output->x + dx;
+	y = output->y + dy;
+
+	weston_surface_set_position(surface, x, y);
+}
+
 static void
 workspace_manager_move_surface(struct wl_client *client,
 			       struct wl_resource *resource,
 			       struct wl_resource *surface_resource,
+			       uint32_t container_id,
 			       uint32_t workspace)
 {
 	struct desktop_shell *shell = resource->data;
 	struct weston_surface *surface =
 		(struct weston_surface *) surface_resource;
-	struct workspace_container *container =
-		find_first_workspace_container(shell);
+	struct shell_surface *shsurf;
+	struct workspace_container *container;
+	struct workspace *from, *to;
+	struct weston_seat *seat;
+
+	shsurf = get_shell_surface(surface);
+	if (!shsurf)
+		return;
+
+	container = get_workspace_container(shell, container_id);
+	if (!container)
+		return;
+
+	if (workspace >= container->num ||
+	    workspace == container->current)
+		return;
+
+	from = shsurf->workspace;
+	to = workspace_container_get(container, workspace);
 
-	move_surface_to_workspace(shell, surface, container, workspace);
+	workspace_restack_surface(to, shsurf);
+
+	if (from->container != to->container)
+		surface_position_on_output(surface, shell, container->output);
+
+	drop_focus_state(shell, from, surface);
+	wl_list_for_each(seat, &shell->compositor->seat_list, link)
+		if (seat->has_keyboard &&
+		    seat->keyboard.focus == &surface->surface)
+			wl_keyboard_set_focus(&seat->keyboard, NULL);
+
+	weston_surface_damage_below(surface);
 }
 
 static const struct workspace_manager_interface workspace_manager_implementation = {
@@ -1142,12 +1239,27 @@ unbind_resource(struct wl_resource *resource)
 }
 
 static void
+send_workspace_container_state(void *element, void *data)
+{
+	struct workspace_container *container = element;
+	struct wl_resource *resource = data;
+	struct desktop_shell *shell = container->shell;
+
+	wl_list_for_each(resource, &shell->workspaces.client_list, link)
+		workspace_manager_send_container_state(resource,
+						       container->output->id,
+						       container->current,
+						       container->num);
+}
+
+static void
 bind_workspace_manager(struct wl_client *client,
 		       void *data, uint32_t version, uint32_t id)
 {
 	struct desktop_shell *shell = data;
-	struct workspace_container *container;
 	struct wl_resource *resource;
+	struct shell_surface *shsurf;
+	struct workspace *ws;
 
 	resource = wl_client_add_object(client, &workspace_manager_interface,
 					&workspace_manager_implementation,
@@ -1161,10 +1273,20 @@ bind_workspace_manager(struct wl_client *client,
 	resource->destroy = unbind_resource;
 	wl_list_insert(&shell->workspaces.client_list, &resource->link);
 
-	container = find_first_workspace_container(shell);
-	workspace_manager_send_state(resource,
-				     container->current,
-				     container->num);
+	hash_table_for_each(shell->workspaces.table,
+			    send_workspace_container_state,
+			    resource);
+
+	wl_list_for_each(shsurf, &shell->toplevel_surface_list, link) {
+		if (shsurf->resource.client == client) {
+			ws = shsurf->workspace;
+			workspace_manager_send_surface_state(
+				resource,
+				&shsurf->surface->surface.resource,
+				workspace_container_get_id(ws->container),
+				workspace_get_index(ws));
+		}
+	}
 }
 
 static void
@@ -1777,26 +1899,6 @@ shell_surface_get_shell(struct shell_surface *shsurf)
 	return shsurf->shell;
 }
 
-static int
-get_output_panel_height(struct desktop_shell *shell,
-			struct weston_output *output)
-{
-	struct weston_surface *surface;
-	int panel_height = 0;
-
-	if (!output)
-		return 0;
-
-	wl_list_for_each(surface, &shell->panel_layer.surface_list, layer_link) {
-		if (surface->output == output) {
-			panel_height = surface->geometry.height;
-			break;
-		}
-	}
-
-	return panel_height;
-}
-
 static void
 shell_surface_set_maximized(struct wl_client *client,
 			    struct wl_resource *resource,
@@ -3096,8 +3198,6 @@ weston_surface_set_initial_position (struct weston_surface *surface,
 {
 	struct weston_compositor *compositor = shell->compositor;
 	int ix = 0, iy = 0;
-	int range_x, range_y;
-	int dx, dy, x, y, panel_height;
 	struct weston_output *output, *target_output = NULL;
 	struct weston_seat *seat;
 
@@ -3127,29 +3227,7 @@ weston_surface_set_initial_position (struct weston_surface *surface,
 		return;
 	}
 
-	/* Valid range within output where the surface will still be onscreen.
-	 * If this is negative it means that the surface is bigger than
-	 * output.
-	 */
-	panel_height = get_output_panel_height(shell, target_output);
-	range_x = target_output->width - surface->geometry.width;
-	range_y = (target_output->height - panel_height) -
-		  surface->geometry.height;
-
-	if (range_x > 0)
-		dx = random() % range_x;
-	else
-		dx = 0;
-
-	if (range_y > 0)
-		dy = panel_height + random() % range_y;
-	else
-		dy = panel_height;
-
-	x = target_output->x + dx;
-	y = target_output->y + dy;
-
-	weston_surface_set_position (surface, x, y);
+	surface_position_on_output(surface, shell, target_output);
 }
 
 static void
-- 
1.7.10.4



More information about the wayland-devel mailing list