[PATCH weston 2/3] shell: Virtual workspaces

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


A workspace is a list of top level surfaces visible at a time. New
toplevel surfaces are added to the current workspace. Default
keybindings (modifier - Up and modifier - Down) are used for navigating
between workspaces. By default a single workspace is created.

Surfaces of inactive workspaces are hidden when changed away from the
containing workspace so that frame callbacks gets queued instead of
emitted. When changed back to the workspace its surfaces are again
shown.

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

diff --git a/src/shell.c b/src/shell.c
index ba421b5..56b271b 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -37,6 +37,8 @@
 #include "desktop-shell-server-protocol.h"
 #include "../shared/config-parser.h"
 
+#define DEFAULT_NUM_WORKSPACES 1
+
 enum animation_type {
 	ANIMATION_NONE,
 
@@ -44,6 +46,10 @@ enum animation_type {
 	ANIMATION_FADE
 };
 
+struct workspace {
+	struct weston_layer layer;
+};
+
 struct desktop_shell {
 	struct weston_compositor *compositor;
 
@@ -79,6 +85,12 @@ struct desktop_shell {
 	struct wl_list panels;
 
 	struct {
+		struct wl_array array;
+		unsigned int current;
+		unsigned int num;
+	} workspaces;
+
+	struct {
 		char *path;
 		int duration;
 		struct wl_resource *binding;
@@ -274,12 +286,15 @@ shell_configuration(struct desktop_shell *shell)
 	char *config_file;
 	char *path = NULL;
 	int duration = 60;
+	unsigned int num_workspaces = DEFAULT_NUM_WORKSPACES;
 	char *modifier = NULL;
 	char *win_animation = NULL;
 
 	struct config_key shell_keys[] = {
 		{ "binding-modifier",   CONFIG_KEY_STRING, &modifier },
 		{ "animation",          CONFIG_KEY_STRING, &win_animation},
+		{ "num-workspaces",
+			CONFIG_KEY_UNSIGNED_INTEGER, &num_workspaces },
 	};
 
 	struct config_key saver_keys[] = {
@@ -300,6 +315,7 @@ shell_configuration(struct desktop_shell *shell)
 	shell->screensaver.duration = duration;
 	shell->binding_modifier = get_modifier(modifier);
 	shell->win_animation_type = get_animation_type(win_animation);
+	shell->workspaces.num = num_workspaces > 0 ? num_workspaces : 1;
 }
 
 static void
@@ -1586,6 +1602,85 @@ get_shell_surface_type(struct weston_surface *surface)
 }
 
 static void
+workspace_destroy(struct workspace *ws)
+{
+	free(ws);
+}
+
+static struct workspace *
+workspace_create(void)
+{
+	struct workspace *ws = malloc(sizeof *ws);
+	if (ws == NULL)
+		return NULL;
+
+	weston_layer_init(&ws->layer, NULL);
+	wl_list_init(&ws->layer.link);
+
+	return ws;
+}
+
+static struct workspace *
+get_workspace(struct desktop_shell *shell, unsigned int index)
+{
+	struct workspace **pws = shell->workspaces.array.data;
+	pws += index;
+	return *pws;
+}
+
+static struct workspace *
+get_current_workspace(struct desktop_shell *shell)
+{
+	return get_workspace(shell, shell->workspaces.current);
+}
+
+static void
+activate_workspace(struct desktop_shell *shell, unsigned int index)
+{
+	struct workspace *ws;
+
+	ws = get_workspace(shell, index);
+	wl_list_insert(&shell->panel_layer.link, &ws->layer.link);
+
+	shell->workspaces.current = index;
+}
+
+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;
+
+	/* Don't change workspace when there is any fullscreen surfaces. */
+	if (!wl_list_empty(&shell->fullscreen_layer.surface_list))
+		return;
+
+	/* Clear keyboard focus so that no hidden surfaces will keep it. */
+	wl_list_for_each(seat, &shell->compositor->seat_list, link)
+		if (seat->seat.keyboard)
+			wl_keyboard_set_focus(seat->seat.keyboard, NULL);
+
+	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);
+
+	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);
+
+	weston_compositor_damage_all(shell->compositor);
+}
+
+static void
 move_binding(struct wl_seat *seat, uint32_t time, uint32_t button, void *data)
 {
 	struct weston_surface *surface =
@@ -1933,6 +2028,8 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 	 struct weston_seat *seat)
 {
 	struct weston_surface *surf, *prev;
+	struct workspace *ws;
+	struct weston_layer *ws_layer;
 
 	weston_surface_activate(es, seat);
 
@@ -1954,18 +2051,21 @@ activate(struct desktop_shell *shell, struct weston_surface *es,
 		shell_configure_fullscreen(get_shell_surface(es));
 		break;
 	default:
+		ws = get_current_workspace(shell);
+		ws_layer = &ws->layer;
+
 		/* move the fullscreen surfaces down into the toplevel layer */
 		if (!wl_list_empty(&shell->fullscreen_layer.surface_list)) {
 			wl_list_for_each_reverse_safe(surf,
 						      prev, 
 					              &shell->fullscreen_layer.surface_list, 
-						      layer_link)
+						      layer_link) {
 				weston_surface_restack(surf,
-						       &shell->toplevel_layer.surface_list); 
+						       &ws_layer->surface_list);
+			}
 		}
 
-		weston_surface_restack(es,
-				       &shell->toplevel_layer.surface_list);
+		weston_surface_restack(es, &ws_layer->surface_list);
 		break;
 	}
 }
@@ -2051,9 +2151,9 @@ lock(struct wl_listener *listener, void *data)
 	weston_compositor_schedule_repaint(shell->compositor);
 
 	/* reset keyboard foci */
-	wl_list_for_each(seat, &shell->compositor->seat_list, link) {
-		wl_keyboard_set_focus(seat->seat.keyboard, NULL);
-	}
+	wl_list_for_each(seat, &shell->compositor->seat_list, link)
+		if (seat->seat.keyboard)
+			wl_keyboard_set_focus(seat->seat.keyboard, NULL);
 
 	/* TODO: disable bindings that should not work while locked. */
 
@@ -2103,6 +2203,7 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
 	enum shell_surface_type surface_type = SHELL_SURFACE_NONE;
 	struct weston_surface *parent;
 	struct weston_seat *seat;
+	struct workspace *ws;
 	int panel_height = 0;
 
 	shsurf = get_shell_surface(surface);
@@ -2182,8 +2283,8 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
 	case SHELL_SURFACE_NONE:
 		break;
 	default:
-		wl_list_insert(&shell->toplevel_layer.surface_list,
-			       &surface->layer_link); 
+		ws = get_current_workspace(shell);
+		wl_list_insert(&ws->layer.surface_list, &surface->layer_link);
 		break;
 	}
 
@@ -2660,10 +2761,37 @@ force_kill_binding(struct wl_seat *seat, uint32_t time, uint32_t key,
 }
 
 static void
+workspace_up_binding(struct wl_seat *seat, uint32_t time,
+		     uint32_t key, void *data)
+{
+	struct desktop_shell *shell = data;
+	unsigned int new_index = shell->workspaces.current;
+
+	if (new_index != 0)
+		new_index--;
+
+	change_workspace(shell, new_index);
+}
+
+static void
+workspace_down_binding(struct wl_seat *seat, uint32_t time,
+		       uint32_t key, void *data)
+{
+	struct desktop_shell *shell = data;
+	unsigned int new_index = shell->workspaces.current;
+
+	if (new_index < shell->workspaces.num - 1)
+		new_index++;
+
+	change_workspace(shell, new_index);
+}
+
+static void
 shell_destroy(struct wl_listener *listener, void *data)
 {
 	struct desktop_shell *shell =
 		container_of(listener, struct desktop_shell, destroy_listener);
+	struct workspace **ws;
 
 	if (shell->child.client)
 		wl_client_destroy(shell->child.client);
@@ -2671,6 +2799,10 @@ shell_destroy(struct wl_listener *listener, void *data)
 	wl_list_remove(&shell->lock_listener.link);
 	wl_list_remove(&shell->unlock_listener.link);
 
+	wl_array_for_each(ws, &shell->workspaces.array)
+		workspace_destroy(*ws);
+	wl_array_release(&shell->workspaces.array);
+
 	free(shell->screensaver.path);
 	free(shell);
 }
@@ -2720,6 +2852,10 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell)
 				          debug_repaint_binding, shell);
 	weston_compositor_add_key_binding(ec, KEY_K, mod,
 				          force_kill_binding, shell);
+	weston_compositor_add_key_binding(ec, KEY_UP, mod,
+					  workspace_up_binding, shell);
+	weston_compositor_add_key_binding(ec, KEY_DOWN, mod,
+					  workspace_down_binding, shell);
 }
 
 int
@@ -2729,6 +2865,8 @@ WL_EXPORT int
 shell_init(struct weston_compositor *ec)
 {
 	struct desktop_shell *shell;
+	struct workspace **pws;
+	unsigned int i;
 
 	shell = malloc(sizeof *shell);
 	if (shell == NULL)
@@ -2761,8 +2899,21 @@ shell_init(struct weston_compositor *ec)
 			  &shell->toplevel_layer.link);
 	wl_list_init(&shell->lock_layer.surface_list);
 
+	wl_array_init(&shell->workspaces.array);
+
 	shell_configuration(shell);
 
+	for (i = 0; i < shell->workspaces.num; i++) {
+		pws = wl_array_add(&shell->workspaces.array, sizeof *pws);
+		if (pws == NULL)
+			return -1;
+
+		*pws = workspace_create();
+		if (*pws == NULL)
+			return -1;
+	}
+	activate_workspace(shell, 0);
+
 	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