[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