diff --git a/include/sway/input/seat.h b/include/sway/input/seat.h
index 9dfb0714..5c404ecd 100644
--- a/include/sway/input/seat.h
+++ b/include/sway/input/seat.h
@@ -35,6 +35,14 @@ struct sway_drag_icon {
 	struct wl_listener destroy;
 };
 
+enum sway_seat_operation {
+	OP_NONE,
+	OP_DOWN,
+	OP_MOVE,
+	OP_RESIZE_FLOATING,
+	OP_RESIZE_TILING,
+};
+
 struct sway_seat {
 	struct wlr_seat *wlr_seat;
 	struct sway_cursor *cursor;
@@ -54,13 +62,7 @@ struct sway_seat {
 	double touch_x, touch_y;
 
 	// Operations (drag and resize)
-	enum {
-		OP_NONE,
-		OP_MOVE,
-		OP_RESIZE_FLOATING,
-		OP_RESIZE_TILING,
-	} operation;
-
+	enum sway_seat_operation operation;
 	struct sway_container *op_container;
 	enum wlr_edges op_resize_edge;
 	uint32_t op_button;
@@ -68,6 +70,7 @@ struct sway_seat {
 	double op_ref_lx, op_ref_ly;         // cursor's x/y at start of op
 	double op_ref_width, op_ref_height;  // container's size at start of op
 	double op_ref_con_lx, op_ref_con_ly; // container's x/y at start of op
+	bool op_moved;                       // if the mouse moved during a down op
 
 	uint32_t last_button;
 	uint32_t last_button_serial;
@@ -157,6 +160,9 @@ bool seat_is_input_allowed(struct sway_seat *seat, struct wlr_surface *surface);
 
 void drag_icon_update_position(struct sway_drag_icon *icon);
 
+void seat_begin_down(struct sway_seat *seat, struct sway_container *con,
+		uint32_t button, double sx, double sy);
+
 void seat_begin_move(struct sway_seat *seat, struct sway_container *con,
 		uint32_t button);
 
diff --git a/sway/input/cursor.c b/sway/input/cursor.c
index 6208ce32..762b8081 100644
--- a/sway/input/cursor.c
+++ b/sway/input/cursor.c
@@ -215,6 +215,19 @@ static enum wlr_edges find_resize_edge(struct sway_container *cont,
 	return edge;
 }
 
+static void handle_down_motion(struct sway_seat *seat,
+		struct sway_cursor *cursor, uint32_t time_msec) {
+	struct sway_container *con = seat->op_container;
+	if (seat_is_input_allowed(seat, con->sway_view->surface)) {
+		double moved_x = cursor->cursor->x - seat->op_ref_lx;
+		double moved_y = cursor->cursor->y - seat->op_ref_ly;
+		double sx = seat->op_ref_con_lx + moved_x;
+		double sy = seat->op_ref_con_ly + moved_y;
+		wlr_seat_pointer_notify_motion(seat->wlr_seat, time_msec, sx, sy);
+	}
+	seat->op_moved = true;
+}
+
 static void handle_move_motion(struct sway_seat *seat,
 		struct sway_cursor *cursor) {
 	struct sway_container *con = seat->op_container;
@@ -397,6 +410,9 @@ void cursor_send_pointer_motion(struct sway_cursor *cursor, uint32_t time_msec,
 
 	if (seat->operation != OP_NONE) {
 		switch (seat->operation) {
+		case OP_DOWN:
+			handle_down_motion(seat, cursor, time_msec);
+			break;
 		case OP_MOVE:
 			handle_move_motion(seat, cursor);
 			break;
@@ -743,6 +759,14 @@ void dispatch_cursor_button(struct sway_cursor *cursor,
 		}
 	}
 
+	// Handle mousedown on a container surface
+	if (surface && cont && state == WLR_BUTTON_PRESSED) {
+		seat_set_focus(seat, cont);
+		seat_pointer_notify_button(seat, time_msec, button, state);
+		seat_begin_down(seat, cont, button, sx, sy);
+		return;
+	}
+
 	// Handle clicking a container surface
 	if (cont) {
 		seat_set_focus(seat, cont);
diff --git a/sway/input/seat.c b/sway/input/seat.c
index fa41904a..9d46e760 100644
--- a/sway/input/seat.c
+++ b/sway/input/seat.c
@@ -954,6 +954,18 @@ struct seat_config *seat_get_config(struct sway_seat *seat) {
 	return NULL;
 }
 
+void seat_begin_down(struct sway_seat *seat, struct sway_container *con,
+		uint32_t button, double sx, double sy) {
+	seat->operation = OP_DOWN;
+	seat->op_container = con;
+	seat->op_button = button;
+	seat->op_ref_lx = seat->cursor->cursor->x;
+	seat->op_ref_ly = seat->cursor->cursor->y;
+	seat->op_ref_con_lx = sx;
+	seat->op_ref_con_ly = sy;
+	seat->op_moved = false;
+}
+
 void seat_begin_move(struct sway_seat *seat, struct sway_container *con,
 		uint32_t button) {
 	if (!seat->cursor) {
@@ -1007,6 +1019,7 @@ void seat_begin_resize_tiling(struct sway_seat *seat,
 }
 
 void seat_end_mouse_operation(struct sway_seat *seat) {
+	enum sway_seat_operation operation = seat->operation;
 	if (seat->operation == OP_MOVE) {
 		// We "move" the container to its own location so it discovers its
 		// output again.
@@ -1015,7 +1028,19 @@ void seat_end_mouse_operation(struct sway_seat *seat) {
 	}
 	seat->operation = OP_NONE;
 	seat->op_container = NULL;
-	cursor_set_image(seat->cursor, "left_ptr", NULL);
+	if (operation == OP_DOWN) {
+		// Set the cursor's previous coords to the x/y at the start of the
+		// operation, so the container change will be detected if using
+		// focus_follows_mouse and the cursor moved off the original container
+		// during the operation.
+		seat->cursor->previous.x = seat->op_ref_lx;
+		seat->cursor->previous.y = seat->op_ref_ly;
+		if (seat->op_moved) {
+			cursor_send_pointer_motion(seat->cursor, 0, true);
+		}
+	} else {
+		cursor_set_image(seat->cursor, "left_ptr", NULL);
+	}
 }
 
 void seat_pointer_notify_button(struct sway_seat *seat, uint32_t time_msec,