[PATCH weston 1/2 v3] shell: Implement resize-from-center mode

Scott Moreau oreaus at gmail.com
Mon Feb 11 03:45:39 PST 2013


Toggles between normal and resize-from-center mode when shift is pressed.

---
v3: Compositor-side-only shell implementation.

Resize from center is a fairly simple algorithm but it becomes complicated
when switching between modes without releasing the grab. The majority of the
code handles placing the surface so that the cursor seems attached to the
surface (a.k.a. grabbed). There is also code to limit how far the surface can
move when it is adjusted. This is to work around a bug when resizing from a
corner and toggling the mode switch hotkey, but also to guarantee the surface
isn't repositioned unexpectedly during a resize.

The readjustments to switch back and forth between modes during a grab are not
perfect and a bit of a hack. An alternative could be to handle each resize mode
with separate bindings and avoid repositioning by not allowing mode toggle
during a resize grab.

 src/shell.c |  243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 235 insertions(+), 8 deletions(-)

diff --git a/src/shell.c b/src/shell.c
index 368fa5b..a5fc83a 100644
--- a/src/shell.c
+++ b/src/shell.c
@@ -150,6 +150,8 @@ struct ping_timer {
 	uint32_t serial;
 };
 
+struct weston_resize_grab;
+
 struct shell_surface {
 	struct wl_resource resource;
 
@@ -192,6 +194,7 @@ struct shell_surface {
 	} fullscreen;
 
 	struct ping_timer *ping_timer;
+	struct weston_resize_grab *resize;
 
 	struct weston_transform workspace_transform;
 
@@ -1077,6 +1080,15 @@ struct weston_resize_grab {
 	struct shell_grab base;
 	uint32_t edges;
 	int32_t width, height;
+	struct {
+		int active;
+		int32_t last_x, last_y;
+		int32_t last_width, last_height;
+		wl_fixed_t grab_offset_x, grab_offset_y;
+		int edge_offset_x, edge_offset_y;
+		/* Surface center in global coordinates */
+		wl_fixed_t x, y;
+	} from_center;
 };
 
 static void
@@ -1086,33 +1098,52 @@ resize_grab_motion(struct wl_pointer_grab *grab,
 	struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
 	struct wl_pointer *pointer = grab->pointer;
 	struct shell_surface *shsurf = resize->base.shsurf;
-	int32_t width, height;
+	int32_t width, height, offset;
+	wl_fixed_t grab_offset_x, grab_offset_y;
 	wl_fixed_t from_x, from_y;
 	wl_fixed_t to_x, to_y;
 
 	if (!shsurf)
 		return;
 
+	grab_offset_x = resize->from_center.grab_offset_x;
+	grab_offset_y = resize->from_center.grab_offset_y;
+
 	weston_surface_from_global_fixed(shsurf->surface,
-				         pointer->grab_x, pointer->grab_y,
+				         pointer->grab_x + grab_offset_x,
+				         pointer->grab_y + grab_offset_y,
 				         &from_x, &from_y);
 	weston_surface_from_global_fixed(shsurf->surface,
 				         pointer->x, pointer->y, &to_x, &to_y);
 
 	width = resize->width;
 	if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT) {
-		width += wl_fixed_to_int(from_x - to_x);
+		offset = wl_fixed_to_int(from_x - to_x);
 	} else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT) {
-		width += wl_fixed_to_int(to_x - from_x);
+		offset = wl_fixed_to_int(to_x - from_x);
+	} else {
+		offset = 0;
 	}
 
+	if (shsurf->resize->from_center.active)
+		offset *= 2;
+
+	width += offset;
+
 	height = resize->height;
 	if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP) {
-		height += wl_fixed_to_int(from_y - to_y);
+		offset = wl_fixed_to_int(from_y - to_y);
 	} else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) {
-		height += wl_fixed_to_int(to_y - from_y);
+		offset = wl_fixed_to_int(to_y - from_y);
+	} else {
+		offset = 0;
 	}
 
+	if (shsurf->resize->from_center.active)
+		offset *= 2;
+
+	height += offset;
+
 	shsurf->client->send_configure(shsurf->surface,
 				       resize->edges, width, height);
 }
@@ -1136,11 +1167,14 @@ resize_grab_button(struct wl_pointer_grab *grab,
 		   uint32_t time, uint32_t button, uint32_t state_w)
 {
 	struct weston_resize_grab *resize = (struct weston_resize_grab *) grab;
+	struct shell_grab *shell_grab = (struct shell_grab *) grab;
 	struct wl_pointer *pointer = grab->pointer;
 	enum wl_pointer_button_state state = state_w;
 
 	if (pointer->button_count == 0 &&
 	    state == WL_POINTER_BUTTON_STATE_RELEASED) {
+		shell_grab->shsurf->resize->from_center.active = 0;
+		shell_grab->shsurf->resize = NULL;
 		shell_grab_end(&resize->base);
 		free(grab);
 	}
@@ -1157,6 +1191,9 @@ surface_resize(struct shell_surface *shsurf,
 	       struct weston_seat *ws, uint32_t edges)
 {
 	struct weston_resize_grab *resize;
+	struct wl_pointer *pointer = ws->seat.pointer;
+	int *edge_offset_x, *edge_offset_y;
+	wl_fixed_t grab_x, grab_y;
 
 	if (shsurf->type == SHELL_SURFACE_FULLSCREEN)
 		return 0;
@@ -1172,9 +1209,42 @@ surface_resize(struct shell_surface *shsurf,
 	resize->edges = edges;
 	resize->width = shsurf->surface->geometry.width;
 	resize->height = shsurf->surface->geometry.height;
+	resize->from_center.active = 0;
+	resize->from_center.grab_offset_x = 0;
+	resize->from_center.grab_offset_y = 0;
+	resize->from_center.last_x = pointer->x;
+	resize->from_center.last_y = pointer->y;
+	resize->from_center.last_width = resize->width;
+	resize->from_center.last_height = resize->height;
+
+	shsurf->resize = resize;
 
 	shell_grab_start(&resize->base, &resize_grab_interface, shsurf,
-			 ws->seat.pointer, edges);
+			 pointer, edges);
+
+	weston_surface_from_global_fixed(shsurf->surface,
+				         pointer->grab_x,
+				         pointer->grab_y,
+				         &grab_x, &grab_y);
+
+	edge_offset_x = &resize->from_center.edge_offset_x;
+	edge_offset_y = &resize->from_center.edge_offset_y;
+
+	if (edges & WL_SHELL_SURFACE_RESIZE_LEFT) {
+		*edge_offset_x = wl_fixed_to_int(grab_x);
+	} else if (edges & WL_SHELL_SURFACE_RESIZE_RIGHT) {
+		*edge_offset_x = resize->width - wl_fixed_to_int(grab_x);
+	} else {
+		*edge_offset_x = 0;
+	}
+
+	if (edges & WL_SHELL_SURFACE_RESIZE_TOP) {
+		*edge_offset_y = wl_fixed_to_int(grab_y);
+	} else if (edges & WL_SHELL_SURFACE_RESIZE_BOTTOM) {
+		*edge_offset_y = resize->height - wl_fixed_to_int(grab_y);
+	} else {
+		*edge_offset_y = 0;
+	}
 
 	return 0;
 }
@@ -2976,6 +3046,154 @@ map(struct desktop_shell *shell, struct weston_surface *surface,
 }
 
 static void
+resize_mode_switch(struct weston_resize_grab *resize,
+				int32_t width, int32_t height,
+				wl_fixed_t grab_x, wl_fixed_t grab_y,
+				int resize_from_center)
+{
+	struct shell_surface *shsurf;
+	struct weston_surface *surface;
+	struct wl_pointer *pointer;
+	int offset_x, offset_y;
+	int edge_offset_x, edge_offset_y;
+	wl_fixed_t new_grab_x, new_grab_y;
+	float from_x, from_y;
+	float to_x, to_y;
+	const int max_offset = 50;
+
+	shsurf = resize->base.shsurf;
+	surface = shsurf->surface;
+	pointer = resize->base.grab.pointer;
+
+	resize->from_center.active = resize_from_center;
+	resize->width = width;
+	resize->height = height;
+	resize->from_center.grab_offset_x = pointer->x - pointer->grab_x;
+	resize->from_center.grab_offset_y = pointer->y - pointer->grab_y;
+
+	if (resize->from_center.active) {
+		weston_surface_to_global_fixed(shsurf->surface,
+			wl_fixed_from_int(resize->width) / 2,
+			wl_fixed_from_int(resize->height) / 2,
+			&shsurf->resize->from_center.x,
+			&shsurf->resize->from_center.y);
+	} else {
+		shsurf->resize->from_center.x = 0;
+		shsurf->resize->from_center.y = 0;
+	}
+
+	/* Adjust position relative to cursor */
+	edge_offset_x = resize->from_center.edge_offset_x;
+	edge_offset_y = resize->from_center.edge_offset_y;
+
+	if (resize->edges & WL_SHELL_SURFACE_RESIZE_LEFT)
+		offset_x = wl_fixed_to_int(grab_x) - edge_offset_x;
+	else if (resize->edges & WL_SHELL_SURFACE_RESIZE_RIGHT)
+		offset_x = wl_fixed_to_int(grab_x) - (width - edge_offset_x);
+	else
+		offset_x = 0;
+
+	if (resize->edges & WL_SHELL_SURFACE_RESIZE_TOP)
+		offset_y = wl_fixed_to_int(grab_y) - edge_offset_y;
+	else if (resize->edges & WL_SHELL_SURFACE_RESIZE_BOTTOM)
+		offset_y = wl_fixed_to_int(grab_y) - (height - edge_offset_y);
+	else
+		offset_y = 0;
+
+	/* There might be cases where the computed offset has an unexpected
+	 * x or y value, for example when resizing from a surface's corner.
+	 * If the offset is too large, discard it completely and recompute
+	 * the grab offset to reflect the new grab coordinates. */
+
+	if (abs(offset_x) > max_offset) {
+		offset_x = 0;
+		weston_surface_to_global_fixed(surface,
+						resize->from_center.last_x,
+						grab_y,
+						&new_grab_x, &new_grab_y);
+		resize->from_center.grab_offset_x = new_grab_x - pointer->grab_x;
+		resize->from_center.grab_offset_y = new_grab_y - pointer->grab_y;
+	}
+
+	if (abs(offset_y) > max_offset) {
+		offset_y = 0;
+		weston_surface_to_global_fixed(surface,
+						grab_x,
+						resize->from_center.last_y,
+						&new_grab_x, &new_grab_y);
+		resize->from_center.grab_offset_x = new_grab_x - pointer->grab_x;
+		resize->from_center.grab_offset_y = new_grab_y - pointer->grab_y;
+	}
+
+	weston_surface_to_global_float(surface, 0, 0, &from_x, &from_y);
+	weston_surface_to_global_float(surface, offset_x, offset_y, &to_x, &to_y);
+	surface->geometry.x += to_x - from_x;
+	surface->geometry.y += to_y - from_y;
+	surface->geometry.dirty = 1;
+}
+
+static void
+resize_update_data(struct shell_surface *shsurf)
+{
+	struct weston_resize_grab *resize;
+	struct wl_pointer *pointer;
+	struct weston_seat *ws;
+	int32_t width, height;
+	wl_fixed_t grab_x, grab_y;
+
+	if (!shsurf)
+		return;
+
+	resize = shsurf->resize;
+
+	if (!resize)
+		return;
+
+	pointer = resize->base.grab.pointer;
+	ws = (struct weston_seat *) pointer->seat;
+
+	width = shsurf->surface->geometry.width;
+	height = shsurf->surface->geometry.height;
+
+	weston_surface_from_global_fixed(shsurf->surface,
+					pointer->x,
+					pointer->y,
+					&grab_x, &grab_y);
+
+	if ((!resize->from_center.active &&
+	    (ws->modifier_state & MODIFIER_SHIFT)) ||
+	    (resize->from_center.active &&
+	    (ws->modifier_state & MODIFIER_SHIFT) == 0))
+		resize_mode_switch(resize, width, height, grab_x, grab_y,
+				(ws->modifier_state & MODIFIER_SHIFT) ? 1 : 0);
+
+	if (resize->from_center.last_width != width)
+		resize->from_center.last_x = grab_x;
+	if (resize->from_center.last_height != height)
+		resize->from_center.last_y = grab_y;
+	resize->from_center.last_width = width;
+	resize->from_center.last_height = height;
+}
+
+static void
+resize_pin_center(struct weston_resize_grab *resize,
+				struct weston_surface *surface,
+				float *fsx, float *fsy,
+				wl_fixed_t width, wl_fixed_t height)
+{
+	wl_fixed_t center_x, center_y;
+
+	weston_surface_from_global_fixed(surface,
+					resize->from_center.x,
+					resize->from_center.y,
+					&center_x,
+					&center_y);
+
+	*fsx = wl_fixed_to_double(center_x - width / 2);
+	*fsy = wl_fixed_to_double(center_y - height / 2);
+}
+
+static void
 configure(struct desktop_shell *shell, struct weston_surface *surface,
 	  float x, float y, int32_t width, int32_t height)
 {
@@ -3016,6 +3234,8 @@ configure(struct desktop_shell *shell, struct weston_surface *surface,
 		if (surface_type == SHELL_SURFACE_MAXIMIZED)
 			surface->output = shsurf->output;
 	}
+
+	resize_update_data(shsurf);
 }
 
 static void
@@ -3040,9 +3260,16 @@ shell_surface_configure(struct weston_surface *es, int32_t sx, int32_t sy)
 		   es->geometry.height != height) {
 		float from_x, from_y;
 		float to_x, to_y;
+		float fsx = sx;
+		float fsy = sy;
+
+		if (shsurf->resize && shsurf->resize->from_center.active)
+			resize_pin_center(shsurf->resize, es, &fsx, &fsy,
+						wl_fixed_from_int(width),
+						wl_fixed_from_int(height));
 
 		weston_surface_to_global_float(es, 0, 0, &from_x, &from_y);
-		weston_surface_to_global_float(es, sx, sy, &to_x, &to_y);
+		weston_surface_to_global_float(es, fsx, fsy, &to_x, &to_y);
 		configure(shell, es,
 			  es->geometry.x + to_x - from_x,
 			  es->geometry.y + to_y - from_y,
-- 
1.7.10.4



More information about the wayland-devel mailing list