[PATCH wayland 2/4] shell: Virtual workspaces

Jonas Ådahl jadahl at gmail.com
Tue Jun 12 15:01:22 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, modifier - Down, modifier - F1 up to F6) are
used for navigating between workspaces. By default a single workspace is
created.

Surfaces of inactive workspaces have their outputs NULL:ed so that frame
callbacks gets queued instead of emitted. When workspace gets visible
again surface's outputs are assigned.

Signed-off-by: Jonas Ådahl <jadahl at gmail.com>
---
 src/compositor.c |    3 +-
 src/shell.c      |  200 ++++++++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 183 insertions(+), 20 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index de8c605..51347dc 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -1125,7 +1125,8 @@ WL_EXPORT void
 weston_layer_init(struct weston_layer *layer, struct wl_list *below)
 {
 	wl_list_init(&layer->surface_list);
-	wl_list_insert(below, &layer->link);
+	if (below != NULL)
+		wl_list_insert(below, &layer->link);
 }
 
 WL_EXPORT void
diff --git a/src/shell.c b/src/shell.c
index 242b219..28cf7d4 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -38,6 +38,8 @@
 #include "../shared/config-parser.h"
 #include "log.h"
 
+#define DEFAULT_NUM_WORKSPACES 1
+
 enum animation_type {
 	ANIMATION_NONE,
 
@@ -45,6 +47,10 @@ enum animation_type {
 	ANIMATION_FADE
 };
 
+struct workspace {
+	struct weston_layer layer;
+};
+
 struct desktop_shell {
 	struct weston_compositor *compositor;
 
@@ -54,7 +60,6 @@ struct desktop_shell {
 
 	struct weston_layer fullscreen_layer;
 	struct weston_layer panel_layer;
-	struct weston_layer toplevel_layer;
 	struct weston_layer background_layer;
 	struct weston_layer lock_layer;
 
@@ -80,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;
@@ -276,12 +287,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[] = {
@@ -302,6 +316,79 @@ 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
+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);
+
+	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;
+
+	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);
+
+	weston_compositor_damage_all(shell->compositor);
 }
 
 static void
@@ -1540,7 +1627,6 @@ resume_desktop(struct desktop_shell *shell)
 		       &shell->fullscreen_layer.link);
 	wl_list_insert(&shell->fullscreen_layer.link,
 		       &shell->panel_layer.link);
-	wl_list_insert(&shell->panel_layer.link, &shell->toplevel_layer.link);
 
 	shell->locked = false;
 	shell->compositor->idle_time = shell->compositor->option_idle_time;
@@ -1935,6 +2021,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);
 
@@ -1956,18 +2044,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;
 	}
 }
@@ -2033,7 +2124,6 @@ lock(struct wl_listener *listener, void *data)
 	 * input events while we are locked. */
 
 	wl_list_remove(&shell->panel_layer.link);
-	wl_list_remove(&shell->toplevel_layer.link);
 	wl_list_remove(&shell->fullscreen_layer.link);
 	wl_list_insert(&shell->compositor->cursor_layer.link,
 		       &shell->lock_layer.link);
@@ -2101,16 +2191,13 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
     int32_t width, int32_t height, int32_t sx, int32_t sy)
 {
 	struct weston_compositor *compositor = shell->compositor;
-	struct shell_surface *shsurf;
-	enum shell_surface_type surface_type = SHELL_SURFACE_NONE;
+	struct shell_surface *shsurf = get_shell_surface(surface);
+	enum shell_surface_type surface_type = shsurf->type;
 	struct weston_surface *parent;
 	struct weston_seat *seat;
+	struct workspace *ws;
 	int panel_height = 0;
 
-	shsurf = get_shell_surface(surface);
-	if (shsurf)
-		surface_type = shsurf->type;
-
 	surface->geometry.width = width;
 	surface->geometry.height = height;
 	surface->geometry.dirty = 1;
@@ -2184,8 +2271,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;
 	}
 
@@ -2662,10 +2749,52 @@ 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
+workspace_f_binding(struct wl_seat *seat, uint32_t time,
+		    uint32_t key, void *data)
+{
+	struct desktop_shell *shell = data;
+	unsigned int new_index;
+
+	new_index = key - KEY_F1;
+	if (new_index >= shell->workspaces.num)
+		new_index = shell->workspaces.num - 1;
+
+	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);
@@ -2673,6 +2802,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);
 }
@@ -2681,6 +2814,7 @@ static void
 shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell)
 {
 	uint32_t mod;
+	int i, num_workspace_bindings;
 
 	/* fixed bindings */
 	weston_compositor_add_key_binding(ec, KEY_BACKSPACE,
@@ -2722,6 +2856,21 @@ 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);
+
+	/* Add bindings for mod+F[1-6] for workspace 1 to 6. */
+	if (shell->workspaces.num > 1) {
+		num_workspace_bindings = shell->workspaces.num;
+		if (num_workspace_bindings > 6)
+			num_workspace_bindings = 6;
+		for (i = 0; i < num_workspace_bindings; i++)
+			weston_compositor_add_key_binding(ec, KEY_F1 + i, mod,
+							  workspace_f_binding,
+							  shell);
+	}
 }
 
 int
@@ -2731,6 +2880,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)
@@ -2758,13 +2909,24 @@ shell_init(struct weston_compositor *ec)
 
 	weston_layer_init(&shell->fullscreen_layer, &ec->cursor_layer.link);
 	weston_layer_init(&shell->panel_layer, &shell->fullscreen_layer.link);
-	weston_layer_init(&shell->toplevel_layer, &shell->panel_layer.link);
-	weston_layer_init(&shell->background_layer,
-			  &shell->toplevel_layer.link);
-	wl_list_init(&shell->lock_layer.surface_list);
+	weston_layer_init(&shell->background_layer, &shell->panel_layer.link);
+	weston_layer_init(&shell->lock_layer, NULL);
+
+	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