[PATCH] shell: Tiling wm proof of concept

Kristian Høgsberg krh at bitplanet.net
Wed May 21 16:17:25 PDT 2014


---

Jasper and have been talking about how one could implement a tiling wm by
using the 'maximized' state in xdg-shell and I decided to give it a quick try.
This is obviously a bit crude, but it was done in a couple of hours.  It
supports two tiling layouts: all vertical split:

  http://people.freedesktop.org/~krh/layout-1.png

or rows of three windows:

  http://people.freedesktop.org/~krh/layout-2.png

which can be chosen by mod-1 and mod-2, respectively.  One of the questions
we need to figure out is whether reusing the 'maximized' state is ok or if we
need a specific 'tiled' state.

There are lots of loose ends with this code, but I don't intend to
develop this much further.  If somebody wants to pick this up and develop
it into a more complete, polished feature, that would fun.

Kristian

 clients/window.c      |  10 +--
 desktop-shell/shell.c | 196 +++++++++++++++++++++++++++++++++++++++++++++-----
 desktop-shell/shell.h |   1 +
 shared/cairo-util.c   |   2 +-
 shared/frame.c        |   2 +-
 5 files changed, 187 insertions(+), 24 deletions(-)

diff --git a/clients/window.c b/clients/window.c
index 7cb4b27..3df7394 100644
--- a/clients/window.c
+++ b/clients/window.c
@@ -3814,10 +3814,12 @@ window_schedule_resize(struct window *window, int width, int height)
 			window->min_allocation.height = height;
 	}
 
-	if (window->pending_allocation.width < window->min_allocation.width)
-		window->pending_allocation.width = window->min_allocation.width;
-	if (window->pending_allocation.height < window->min_allocation.height)
-		window->pending_allocation.height = window->min_allocation.height;
+	if (!window->maximized) {
+		if (window->pending_allocation.width < window->min_allocation.width)
+			window->pending_allocation.width = window->min_allocation.width;
+		if (window->pending_allocation.height < window->min_allocation.height)
+			window->pending_allocation.height = window->min_allocation.height;
+	}
 
 	window->resize_needed = 1;
 	window_schedule_redraw(window);
diff --git a/desktop-shell/shell.c b/desktop-shell/shell.c
index dd0b2f9..52d0f61 100644
--- a/desktop-shell/shell.c
+++ b/desktop-shell/shell.c
@@ -153,6 +153,11 @@ struct shell_surface {
 		struct weston_view *black_view;
 	} fullscreen;
 
+	struct {
+		int x, y, width, height;
+		struct wl_list link;
+	} tile;
+
 	struct weston_transform workspace_transform;
 
 	struct weston_output *fullscreen_output;
@@ -251,6 +256,9 @@ shell_fade_startup(struct desktop_shell *shell);
 static struct shell_seat *
 get_shell_seat(struct weston_seat *seat);
 
+static struct shell_output *
+get_shell_output(struct weston_output *output);
+
 static int
 get_output_panel_height(struct desktop_shell *shell,
 			struct weston_output *output);
@@ -388,14 +396,8 @@ send_configure_for_surface(struct shell_surface *shsurf)
 		width = shsurf->output->width;
 		height = shsurf->output->height;
 	} else if (state->maximized) {
-		struct desktop_shell *shell;
-		uint32_t panel_height = 0;
-
-		shell = shell_surface_get_shell(shsurf);
-		panel_height = get_output_panel_height(shell, shsurf->output);
-
-		width = shsurf->output->width;
-		height = shsurf->output->height - panel_height;
+		width = shsurf->tile.width;
+		height = shsurf->tile.height;
 	} else {
 		width = 0;
 		height = 0;
@@ -405,6 +407,141 @@ send_configure_for_surface(struct shell_surface *shsurf)
 }
 
 static void
+layout_tiles0(struct shell_output *output)
+{
+	struct shell_surface *s;
+	int32_t panel_height, x, y, width, height;
+	int i, count, remainder;
+
+	count = wl_list_length(&output->tile_list);
+	panel_height = get_output_panel_height(output->shell, output->output);
+	x = 0;
+	y = output->output->y + panel_height;
+	width = output->output->width / count;
+	height = output->output->height - panel_height;
+	remainder = output->output->width - count * width;
+
+	i = 0;
+	wl_list_for_each(s, &output->tile_list, tile.link) {
+		s->tile.x = x;
+		s->tile.y = y;
+		s->tile.width = width + (i < remainder);
+		s->tile.height = height;
+		x += s->tile.width;
+
+		send_configure_for_surface(s);
+	}
+}
+
+static void
+layout_tiles1(struct shell_output *output)
+{
+	struct shell_surface *s;
+	int32_t panel_height, x, y, next_x, next_y, width, height, output_height;
+	int i, count, rows;
+	const int grid_width = 3;
+
+	count = wl_list_length(&output->tile_list);
+	panel_height = get_output_panel_height(output->shell, output->output);
+	output_height = output->output->height - panel_height;
+	x = 0;
+	y = output->output->y + panel_height;
+	rows = (count + grid_width - 1) / grid_width;
+
+	i = 0;
+	wl_list_for_each(s, &output->tile_list, tile.link) {
+		if (i >= rows * grid_width)
+			height = output->output->y + output->output->height - y;
+		else
+			height = output_height / rows;
+		if (i % grid_width < grid_width - 1 && i < count - 1) {
+			width = output->output->width / grid_width;
+			next_x = x + width;
+			next_y = y;
+		} else {
+			width = output->output->width - x;
+			next_x = 0;
+			next_y = y + height;
+		}
+
+		s->tile.x = x;
+		s->tile.y = y;
+		s->tile.width = width;
+		s->tile.height = height;
+
+		x = next_x;
+		y = next_y;
+		i++;
+
+		send_configure_for_surface(s);
+	}
+}
+
+static int tile_layout;
+
+static void
+layout_tiles(struct shell_output *output)
+{
+	switch (tile_layout) {
+	case 0:
+	default:
+		layout_tiles0(output);
+		break;
+	case 1:
+		layout_tiles1(output);
+		break;
+	}
+}
+
+static void
+add_tile_surface(struct shell_surface *shsurf)
+{
+	struct shell_output *output;
+
+	shsurf->state_requested = true;
+	shsurf->requested_state.maximized = 1;
+
+	output = get_shell_output(shsurf->output);
+	wl_list_insert(output->tile_list.prev, &shsurf->tile.link);
+	layout_tiles(output);
+}
+
+static void
+delete_tile_surface(struct shell_surface *shsurf)
+{
+	struct shell_output *output;
+
+	output = get_shell_output(shsurf->output);
+	wl_list_remove(&shsurf->tile.link);
+	layout_tiles(output);
+}
+
+static void
+tile_layout_key_binding(struct weston_seat *seat,
+			uint32_t time, uint32_t key, void *data)
+{
+	struct weston_output *output;
+	struct shell_output *shell_output;
+	double x, y;
+
+	wl_list_for_each(output, &seat->compositor->output_list, link) {
+		x = wl_fixed_to_double(seat->pointer->x);
+		y = wl_fixed_to_double(seat->pointer->y);
+
+		if (pixman_region32_contains_point(&output->region, x, y, NULL))
+			break;
+	}
+
+	if (&output->link == &seat->compositor->output_list)
+		return;
+
+	shell_output = get_shell_output(output);
+	tile_layout = key - KEY_1;
+	layout_tiles(shell_output);
+}
+
+
+static void
 shell_surface_state_changed(struct shell_surface *shsurf)
 {
 	if (shell_surface_is_xdg_surface(shsurf))
@@ -3137,6 +3274,8 @@ destroy_shell_surface(struct shell_surface *shsurf)
 {
 	struct shell_surface *child, *next;
 
+	delete_tile_surface(shsurf);
+
 	wl_signal_emit(&shsurf->destroy_signal, shsurf);
 
 	if (!wl_list_empty(&shsurf->popup.grab_link)) {
@@ -3208,6 +3347,7 @@ handle_resource_destroy(struct wl_listener *listener, void *data)
 	if (!weston_surface_is_mapped(shsurf->surface))
 		return;
 
+#if 0
 	shsurf->surface->ref_count++;
 
 	pixman_region32_fini(&shsurf->surface->pending.input);
@@ -3216,6 +3356,7 @@ handle_resource_destroy(struct wl_listener *listener, void *data)
 	pixman_region32_init(&shsurf->surface->input);
 	weston_fade_run(shsurf->view, 1.0, 0.0, 300.0,
 			fade_out_done, shsurf);
+#endif
 }
 
 static void
@@ -3297,6 +3438,8 @@ create_common_surface(struct shell_client *owner, void *shell,
 
 	shsurf->type = SHELL_SURFACE_NONE;
 
+	wl_list_init(&shsurf->tile.link);
+
 	shsurf->client = client;
 
 	return shsurf;
@@ -3638,6 +3781,8 @@ xdg_get_xdg_surface(struct wl_client *client,
 	wl_resource_set_implementation(shsurf->resource,
 				       &xdg_surface_implementation,
 				       shsurf, shell_destroy_shell_surface);
+
+	add_tile_surface(shsurf);
 }
 
 static bool
@@ -4983,7 +5128,6 @@ map(struct desktop_shell *shell, struct shell_surface *shsurf,
 {
 	struct weston_compositor *compositor = shell->compositor;
 	struct weston_seat *seat;
-	int panel_height = 0;
 	int32_t surf_x, surf_y;
 
 	/* initial positioning, see also configure() */
@@ -4994,13 +5138,11 @@ map(struct desktop_shell *shell, struct shell_surface *shsurf,
 			shell_map_fullscreen(shsurf);
 		} else if (shsurf->state.maximized) {
 			/* use surface configure to set the geometry */
-			panel_height = get_output_panel_height(shell, shsurf->output);
 			surface_subsurfaces_boundingbox(shsurf->surface,
 							&surf_x, &surf_y, NULL, NULL);
 			weston_view_set_position(shsurf->view,
-						 shsurf->output->x - surf_x,
-						 shsurf->output->y +
-						 panel_height - surf_y);
+						 shsurf->tile.x - surf_x,
+						 shsurf->tile.y - surf_y);
 		} else if (!shsurf->state.relative) {
 			weston_view_set_initial_position(shsurf->view, shell);
 		}
@@ -5073,7 +5215,7 @@ configure(struct desktop_shell *shell, struct weston_surface *surface,
 {
 	struct shell_surface *shsurf;
 	struct weston_view *view;
-	int32_t mx, my, surf_x, surf_y;
+	int32_t surf_x, surf_y;
 
 	shsurf = get_shell_surface(surface);
 
@@ -5085,10 +5227,9 @@ configure(struct desktop_shell *shell, struct weston_surface *surface,
 		/* setting x, y and using configure to change that geometry */
 		surface_subsurfaces_boundingbox(shsurf->surface, &surf_x, &surf_y,
 		                                                 NULL, NULL);
-		mx = shsurf->output->x - surf_x;
-		my = shsurf->output->y +
-		     get_output_panel_height(shell,shsurf->output) - surf_y;
-		weston_view_set_position(shsurf->view, mx, my);
+		weston_view_set_position(shsurf->view,
+					 shsurf->tile.x - surf_x,
+					 shsurf->tile.y - surf_y);
 	} else {
 		weston_view_set_position(shsurf->view, x, y);
 	}
@@ -5937,6 +6078,19 @@ handle_output_destroy(struct wl_listener *listener, void *data)
 	free(output_listener);
 }
 
+static struct shell_output *
+get_shell_output(struct weston_output *output)
+{
+	struct wl_listener *listener;
+
+	listener = wl_signal_get(&output->destroy_signal,
+				 handle_output_destroy);
+	assert(listener != NULL);
+
+	return container_of(listener,
+			    struct shell_output, destroy_listener);
+}
+
 static void
 create_shell_output(struct desktop_shell *shell,
 					struct weston_output *output)
@@ -5952,6 +6106,7 @@ create_shell_output(struct desktop_shell *shell,
 	shell_output->destroy_listener.notify = handle_output_destroy;
 	wl_signal_add(&output->destroy_signal,
 		      &shell_output->destroy_listener);
+	wl_list_init(&shell_output->tile_list);
 	wl_list_insert(shell->output_list.prev, &shell_output->link);
 }
 
@@ -6135,6 +6290,11 @@ shell_add_bindings(struct weston_compositor *ec, struct desktop_shell *shell)
 							  shell);
 	}
 
+	weston_compositor_add_key_binding(ec, KEY_1, mod,
+					  tile_layout_key_binding, NULL);
+	weston_compositor_add_key_binding(ec, KEY_2, mod,
+					  tile_layout_key_binding, NULL);
+
 	/* Debug bindings */
 	weston_compositor_add_key_binding(ec, KEY_SPACE, mod | MODIFIER_SHIFT,
 					  debug_binding, shell);
diff --git a/desktop-shell/shell.h b/desktop-shell/shell.h
index 6e63785..05fbad6 100644
--- a/desktop-shell/shell.h
+++ b/desktop-shell/shell.h
@@ -109,6 +109,7 @@ struct shell_output {
 	struct weston_output  *output;
 	struct exposay_output eoutput;
 	struct wl_listener    destroy_listener;
+	struct wl_list        tile_list;
 	struct wl_list        link;
 };
 
diff --git a/shared/cairo-util.c b/shared/cairo-util.c
index 2a33249..b2f924f 100644
--- a/shared/cairo-util.c
+++ b/shared/cairo-util.c
@@ -347,7 +347,7 @@ theme_create(void)
 		return NULL;
 
 	t->margin = 32;
-	t->width = 6;
+	t->width = 2;
 	t->titlebar_height = 27;
 	t->frame_radius = 3;
 	t->shadow = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 128, 128);
diff --git a/shared/frame.c b/shared/frame.c
index 35e6b65..75e1726 100644
--- a/shared/frame.c
+++ b/shared/frame.c
@@ -498,11 +498,11 @@ frame_refresh_geometry(struct frame *frame)
 
 	x_r = frame->width - t->width - frame->shadow_margin;
 	x_l = t->width + frame->shadow_margin;
-	y = t->width + frame->shadow_margin;
 	wl_list_for_each(button, &frame->buttons, link) {
 		const int button_padding = 4;
 		w = cairo_image_surface_get_width(button->icon);
 		h = cairo_image_surface_get_height(button->icon);
+		y = frame->shadow_margin + (titlebar_height - h) / 2;
 
 		if (button->flags & FRAME_BUTTON_DECORATED)
 			w += 10;
-- 
1.9.0



More information about the wayland-devel mailing list