[PATCH weston 2/3] shell: Virtual workspaces

Kristian Høgsberg hoegsberg at gmail.com
Thu Jun 7 12:38:25 PDT 2012


On Wed, Jun 06, 2012 at 11:36:09AM +0200, Jonas Ådahl wrote:
> 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;
> +

We use wl_list for most things and I'd prefer we do that for
workspaces too.  current will be a struct workspace pointer then.

> +	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);
> +}

With the changes above, these reduce to just shell->worspace.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);

weston_surface_unmap() does this and for pointer focus as well (but
that's more subtle since we'll repick when the pointer moves).

> +	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--;

For a wl_list, this would be

	if (current->next != &shell->workspace_list)
		next = container_of(current->next, struct workspace, link);

> +
> +	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
> 
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel


More information about the wayland-devel mailing list