~goleo/wio

ee68d6dfd647cb17ac5be1368fa496fde994113d — Leonid Bobrov 1 year, 5 months ago c8f2963
Resize like in Rio

Summary:
* remove duplicate code;
* resize views when dragging corner borders;
* width and height never become negative to avoid rendering bugs while reflecting,
  also recently wlroots crashes when width/height is <= 0;
* reflection behaves like in Rio;
* minimal size is enforced on red box like in Rio;
* change cursor for New and Resize;
* New and Resize do nothing if size is less than minimal like in Rio;
* New and Resize draw a grey surface in red box like in Rio;
* it's not accurate because it assumes scale is always 1.
6 files changed, 264 insertions(+), 329 deletions(-)

M include/colors.h
M include/server.h
M include/view.h
M input.c
M output.c
M view.c
M include/colors.h => include/colors.h +4 -0
@@ 29,4 29,8 @@ static const float menu_border[4] = {
	0x78 / 255.0f, 0xAD / 255.0f, 0x84 / 255.0f, 1.0f,
};

static const float surface[4] = {
	0xEE / 255.0f, 0xEE / 255.0f, 0xEE / 255.0f, 1.0f,
};

#endif

M include/server.h => include/server.h +1 -4
@@ 26,10 26,7 @@ enum wio_input_state {
	INPUT_STATE_RESIZE_SELECT,
	INPUT_STATE_RESIZE_START,
	INPUT_STATE_RESIZE_END,
	INPUT_STATE_BORDER_DRAG_TOP,
	INPUT_STATE_BORDER_DRAG_RIGHT,
	INPUT_STATE_BORDER_DRAG_BOTTOM,
	INPUT_STATE_BORDER_DRAG_LEFT,
	INPUT_STATE_BORDER_DRAG,
	INPUT_STATE_DELETE_SELECT,
	INPUT_STATE_HIDE_SELECT,
};

M include/view.h => include/view.h +13 -4
@@ 3,10 3,14 @@
#include <wlr/types/wlr_xdg_shell.h>
#include <wayland-server.h>

#define MINWIDTH 100
#define MINHEIGHT 100

struct wio_server;

struct wio_view {
	int x, y;
	int area;
	struct wlr_xdg_surface *xdg_surface;
	struct wio_server *server;
	struct wl_list link;


@@ 15,19 19,24 @@ struct wio_view {
};

enum wio_view_area {
	VIEW_AREA_SURFACE = 0,
	VIEW_AREA_BORDER_TOP_LEFT = 0,
	VIEW_AREA_BORDER_TOP,
	VIEW_AREA_BORDER_TOP_RIGHT,
	VIEW_AREA_BORDER_LEFT,
	VIEW_AREA_SURFACE,
	VIEW_AREA_BORDER_RIGHT,
	VIEW_AREA_BORDER_BOTTOM_LEFT,
	VIEW_AREA_BORDER_BOTTOM,
	VIEW_AREA_BORDER_LEFT,
	VIEW_AREA_BORDER_BOTTOM_RIGHT,
};

void server_new_xdg_surface(struct wl_listener *listener, void *data);

void wio_view_focus(struct wio_view *view, struct wlr_surface *surface);
struct wio_view *wio_view_at(struct wio_server *server, double lx, double ly,
		struct wlr_surface **surface, double *sx, double *sy,
		int *view_area);
		struct wlr_surface **surface, double *sx, double *sy);
void wio_view_move(struct wio_view *view, int x, int y);
struct wlr_box wio_which_box(struct wio_server *server);
struct wlr_box wio_canon_box(struct wio_server *server, struct wlr_box box);

#endif

M input.c => input.c +123 -246
@@ 14,6 14,13 @@
#include "server.h"
#include "view.h"

static char *corners[9] = {
	"top_left_corner", "top_side", "top_right_corner",
	"left_side", NULL, "right_side",
	"bottom_left_corner", "bottom_side", "bottom_right_corner",
};
static char *corner = NULL;

static void keyboard_handle_modifiers(
		struct wl_listener *listener, void *data) {
	struct wio_keyboard *keyboard =


@@ 112,57 119,47 @@ void server_new_input(struct wl_listener *listener, void *data) {

static void process_cursor_motion(struct wio_server *server, uint32_t time) {
	double sx, sy;
	int view_area;
	struct wlr_seat *seat = server->seat;
	struct wlr_surface *surface = NULL;
	struct wio_view *view = wio_view_at(
			server, server->cursor->x, server->cursor->y, &surface, &sx, &sy,
			&view_area);
	if (!view) {
		switch (server->input_state) {
		case INPUT_STATE_MOVE_SELECT:
		case INPUT_STATE_RESIZE_SELECT:
		case INPUT_STATE_DELETE_SELECT:
		case INPUT_STATE_HIDE_SELECT:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"hand1", server->cursor);
			break;
		case INPUT_STATE_MOVE:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"grabbing", server->cursor);
			break;
		case INPUT_STATE_RESIZE_START:
		case INPUT_STATE_NEW_START:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"top_left_corner", server->cursor);
			break;
		case INPUT_STATE_BORDER_DRAG_TOP:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"top_side", server->cursor);
			break;
		case INPUT_STATE_BORDER_DRAG_RIGHT:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"right_side", server->cursor);
			break;
		case INPUT_STATE_BORDER_DRAG_BOTTOM:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"bottom_side", server->cursor);
			break;
		case INPUT_STATE_BORDER_DRAG_LEFT:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"left_side", server->cursor);
			break;
		case INPUT_STATE_RESIZE_END:
		case INPUT_STATE_NEW_END:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"bottom_right_corner", server->cursor);
			break;
		default:
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					"left_ptr", server->cursor);
			break;
		}
	struct wio_view *view = NULL;
	if (server->input_state == INPUT_STATE_NONE) {
		view = wio_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
	}
	if (view) {
		goto End;
	}
	switch (server->input_state) {
	case INPUT_STATE_MOVE_SELECT:
	case INPUT_STATE_RESIZE_SELECT:
	case INPUT_STATE_DELETE_SELECT:
	case INPUT_STATE_HIDE_SELECT:
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				"hand1", server->cursor);
		break;
	case INPUT_STATE_MOVE:
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				"grabbing", server->cursor);
		break;
	case INPUT_STATE_BORDER_DRAG:
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				corner, server->cursor);
		break;
	case INPUT_STATE_RESIZE_START:
	case INPUT_STATE_NEW_START:
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				"grabbing", server->cursor);
		break;
	case INPUT_STATE_RESIZE_END:
	case INPUT_STATE_NEW_END:
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				"grabbing", server->cursor);
		break;
	default:
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				"left_ptr", server->cursor);
		break;
	}
End:
	if (surface) {
		bool focus_changed = seat->pointer_state.focused_surface != surface;
		wlr_seat_pointer_notify_enter(seat, surface, sx, sy);


@@ 170,6 167,10 @@ static void process_cursor_motion(struct wio_server *server, uint32_t time) {
			wlr_seat_pointer_notify_motion(seat, time, sx, sy);
		}
	} else {
		if (view) {
			wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
					corners[view->area], server->cursor);
		}
		wlr_seat_pointer_clear_focus(seat);
	}
}


@@ 199,7 200,7 @@ static void menu_handle_button(
	case 0:
		server->input_state = INPUT_STATE_NEW_START;
		wlr_xcursor_manager_set_cursor_image(server->cursor_mgr,
				"top_left_corner", server->cursor);
				"grabbing", server->cursor);
		break;
	case 1:
		server->input_state = INPUT_STATE_RESIZE_SELECT;


@@ 243,29 244,12 @@ static void view_end_interactive(struct wio_server *server) {
}

static void new_view(struct wio_server *server) {
	int x1 = server->interactive.sx, x2 = server->cursor->x;
	int y1 = server->interactive.sy, y2 = server->cursor->y;
	if (x2 < x1) {
		int _ = x1;
		x1 = x2;
		x2 = _;
	}
	if (y2 < y1) {
		int _ = y1;
		y1 = y2;
		y2 = _;
	struct wlr_box box = wio_which_box(server);
	if (box.width < MINWIDTH || box.height < MINHEIGHT) {
		return;
	}
	struct wio_new_view *view = calloc(1, sizeof(struct wio_new_view));
	view->box.x = x1;
	view->box.y = y1;
	view->box.width = x2 - x1;
	view->box.height = y2 - y1;
	if (view->box.width < 100){
		view->box.width = 100;
	}
	if (view->box.height < 100){
		view->box.height = 100;
	}
	view->box = box;
	int fd[2];
	if (pipe(fd) != 0) {
		wlr_log(WLR_ERROR, "Unable to create pipe for fork");


@@ 322,8 306,10 @@ static void handle_button_internal(
		.x = server->menu.x, .y = server->menu.y,
		.width = server->menu.width, .height = server->menu.height,
	};
	int x1, x2, y1, y2;
	uint32_t width, height;
	struct wlr_box box;
	double sx, sy;
	struct wlr_surface *surface = NULL;
	struct wio_view *view;
	switch (server->input_state) {
	case INPUT_STATE_NONE:
		if (event->state == WLR_BUTTON_PRESSED && event->button == BTN_RIGHT) {


@@ 345,157 331,63 @@ static void handle_button_internal(
		}
		break;
	case INPUT_STATE_NEW_START:
		if (event->state == WLR_BUTTON_PRESSED) {
			server->interactive.sx = server->cursor->x;
			server->interactive.sy = server->cursor->y;
			server->input_state = INPUT_STATE_NEW_END;
		if (event->state != WLR_BUTTON_PRESSED) {
			break;
		}
		server->interactive.sx = server->cursor->x;
		server->interactive.sy = server->cursor->y;
		server->input_state = INPUT_STATE_NEW_END;
		break;
	case INPUT_STATE_NEW_END:
		new_view(server);
		view_end_interactive(server);
		break;
	case INPUT_STATE_RESIZE_SELECT:
		if (event->state == WLR_BUTTON_PRESSED) {
			double sx, sy;
			int view_area;
			struct wlr_surface *surface = NULL;
			struct wio_view *view = wio_view_at(server,
					server->cursor->x, server->cursor->y, &surface, &sx, &sy,
					&view_area);
			if (view != NULL) {
				view_begin_interactive(view, surface, sx, sy,
						"bottom_right_corner", INPUT_STATE_RESIZE_START);
			} else {
				view_end_interactive(server);
			}
		}
		break;
	case INPUT_STATE_RESIZE_START:
		if (event->state == WLR_BUTTON_PRESSED) {
			server->interactive.sx = server->cursor->x;
			server->interactive.sy = server->cursor->y;
			server->input_state = INPUT_STATE_RESIZE_END;
		}
		break;
	case INPUT_STATE_BORDER_DRAG_TOP:
		y1 = server->interactive.view->y + server->interactive.view->xdg_surface->surface->current.height;
		y2 = server->cursor->y;
		x1 = server->interactive.view->x;
		if (y2 < y1) {
			int _ = y1;
			y1 = y2;
			y2 = _;
		}
		wio_view_move(server->interactive.view,
				x1, y1);
		width = server->interactive.view->xdg_surface->surface->current.width;
		height = y2 - y1;
		if (height < 100) {
			height = 100;
		}
		wlr_xdg_toplevel_set_size(
				server->interactive.view->xdg_surface, width, height);
		view_end_interactive(server);
		break;
	case INPUT_STATE_BORDER_DRAG_LEFT:
		x1 = server->interactive.view->x + server->interactive.view->xdg_surface->surface->current.width;
		x2 = server->cursor->x;
		y1 = server->interactive.view->y;
		if (x2 < x1) {
			int _ = x1;
			x1 = x2;
			x2 = _;
		}
		wio_view_move(server->interactive.view,
				x1, y1);
		width = x2 - x1;
		height = server->interactive.view->xdg_surface->surface->current.height;
		if (width < 100) {
			width = 100;
		}
		wlr_xdg_toplevel_set_size(
				server->interactive.view->xdg_surface, width, height);
		view_end_interactive(server);
		break;
	case INPUT_STATE_BORDER_DRAG_BOTTOM:
		x1 = server->interactive.view->x;
		y1 = server->interactive.view->y, y2 = server->cursor->y;
		if (y2 < y1) {
			int _ = y1;
			y1 = y2;
			y2 = _;
		if (event->state != WLR_BUTTON_PRESSED) {
			break;
		}
		wio_view_move(server->interactive.view,
				x1, y1);
		width = server->interactive.view->xdg_surface->surface->current.width;
		height = y2 - y1;
		if (width < 100) {
			width = 100;
		view = wio_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
		if (view != NULL) {
			view_begin_interactive(view, surface, sx, sy,
					"grabbing", INPUT_STATE_RESIZE_START);
		} else {
			view_end_interactive(server);
		}
		wlr_xdg_toplevel_set_size(
				server->interactive.view->xdg_surface, width, height);
		view_end_interactive(server);
		break;
	case INPUT_STATE_BORDER_DRAG_RIGHT:
		x1 = server->interactive.view->x, x2 = server->cursor->x;
		y1 = server->interactive.view->y;
		if (x2 < x1) {
			int _ = x1;
			x1 = x2;
			x2 = _;
		}
		wio_view_move(server->interactive.view,
				x1, y1);
		width = x2 - x1;
		height = server->interactive.view->xdg_surface->surface->current.height;
		if (width < 100) {
			width = 100;
	case INPUT_STATE_RESIZE_START:
		if (event->state != WLR_BUTTON_PRESSED) {
			break;
		}
		wlr_xdg_toplevel_set_size(
				server->interactive.view->xdg_surface, width, height);
		view_end_interactive(server);
		server->interactive.sx = server->cursor->x;
		server->interactive.sy = server->cursor->y;
		server->interactive.view->area = VIEW_AREA_BORDER_BOTTOM_RIGHT;
		server->input_state = INPUT_STATE_RESIZE_END;
		break;
	case INPUT_STATE_BORDER_DRAG:
		box = wio_which_box(server);
		box = wio_canon_box(server, box);
		goto Done;
	case INPUT_STATE_RESIZE_END:
		x1 = server->interactive.sx, x2 = server->cursor->x;
		y1 = server->interactive.sy, y2 = server->cursor->y;
		if (x2 < x1) {
			int _ = x1;
			x1 = x2;
			x2 = _;
		}
		if (y2 < y1) {
			int _ = y1;
			y1 = y2;
			y2 = _;
		}
		wio_view_move(server->interactive.view,
				x1, y1);
		width = x2 - x1, height = y2 - y1;
		if (width < 100) {
			width = 100;
		}
		if (height < 100) {
			height = 100;
		box = wio_which_box(server);
		if (box.width < MINWIDTH || box.height < MINHEIGHT) {
			view_end_interactive(server);
			break;
		}
		wlr_xdg_toplevel_set_size(
				server->interactive.view->xdg_surface, width, height);
	Done:
		wio_view_move(server->interactive.view, box.x, box.y);
		wlr_xdg_toplevel_set_size(server->interactive.view->xdg_surface, box.width, box.height);
		view_end_interactive(server);
		break;
	case INPUT_STATE_MOVE_SELECT:
		if (event->state == WLR_BUTTON_PRESSED) {
			double sx, sy;
			int view_area;
			struct wlr_surface *surface = NULL;
			struct wio_view *view = wio_view_at(server,
					server->cursor->x, server->cursor->y, &surface, &sx, &sy,
					&view_area);
			if (view != NULL) {
				view_begin_interactive(view, surface, sx, sy,
						"grabbing", INPUT_STATE_MOVE);
			} else {
				view_end_interactive(server);
			}
		if (event->state != WLR_BUTTON_PRESSED) {
			break;
		}
		view = wio_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
		if (view != NULL) {
			view_begin_interactive(view, surface, sx, sy,
					"grabbing", INPUT_STATE_MOVE);
		} else {
			view_end_interactive(server);
		}
		break;
	case INPUT_STATE_MOVE:


@@ 505,18 397,14 @@ static void handle_button_internal(
		view_end_interactive(server);
		break;
	case INPUT_STATE_DELETE_SELECT:
		if (event->state == WLR_BUTTON_PRESSED) {
			double sx, sy;
			int view_area;
			struct wlr_surface *surface = NULL;
			struct wio_view *view = wio_view_at(server,
					server->cursor->x, server->cursor->y, &surface, &sx, &sy,
					&view_area);
			if (view != NULL) {
				wlr_xdg_toplevel_send_close(view->xdg_surface);
			}
			view_end_interactive(server);
		if (event->state != WLR_BUTTON_PRESSED) {
			break;
		}
		view = wio_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
		if (view != NULL) {
			wlr_xdg_toplevel_send_close(view->xdg_surface);
		}
		view_end_interactive(server);
		break;
	default:
		// TODO


@@ 530,36 418,25 @@ void server_cursor_button(struct wl_listener *listener, void *data) {
	struct wlr_event_pointer_button *event = data;
	double sx, sy;
	struct wlr_surface *surface = NULL;
	int view_area;
	struct wio_view *view = wio_view_at(
			server, server->cursor->x, server->cursor->y, &surface, &sx, &sy,
			&view_area);
	if (server->input_state == INPUT_STATE_NONE && view) {
		wio_view_focus(view, surface);
		switch (view_area) {
		case VIEW_AREA_SURFACE:
			wlr_seat_pointer_notify_button(server->seat,
					event->time_msec, event->button, event->state);
			break;
		case VIEW_AREA_BORDER_TOP:
			view_begin_interactive(view, surface, view->x, view->y,
					"top_side", INPUT_STATE_BORDER_DRAG_TOP);
			break;
		case VIEW_AREA_BORDER_RIGHT:
			view_begin_interactive(view, surface, view->x, view->y,
					"right_side", INPUT_STATE_BORDER_DRAG_RIGHT);
			break;
		case VIEW_AREA_BORDER_BOTTOM:
			view_begin_interactive(view, surface, view->x, view->y,
					"bottom_side", INPUT_STATE_BORDER_DRAG_BOTTOM);
			break;
		case VIEW_AREA_BORDER_LEFT:
			view_begin_interactive(view, surface, view->x, view->y,
					"left_side", INPUT_STATE_BORDER_DRAG_LEFT);
			break;
		}
	} else {
	struct wio_view *view = NULL;
	if (server->input_state == INPUT_STATE_NONE) {
		view = wio_view_at(server, server->cursor->x, server->cursor->y, &surface, &sx, &sy);
	}
	if (!view) {
		handle_button_internal(server, event);
		return;
	}
	wio_view_focus(view, surface);
	switch (view->area) {
	case VIEW_AREA_SURFACE:
		wlr_seat_pointer_notify_button(server->seat,
				event->time_msec, event->button, event->state);
		break;
	default:
		corner = corners[view->area];
		view_begin_interactive(view, surface, view->x, view->y,
				corner, INPUT_STATE_BORDER_DRAG);
		break;
	}
}


M output.c => output.c +12 -36
@@ 272,6 272,8 @@ static void output_frame(struct wl_listener *listener, void *data) {
	struct wio_output *output = wl_container_of(listener, output, frame);
	struct wio_server *server = output->server;
	struct wlr_renderer *renderer = server->renderer;
	struct wlr_box box;
	float color[4];

	struct timespec now;
	clock_gettime(CLOCK_MONOTONIC, &now);


@@ 308,37 310,10 @@ static void output_frame(struct wl_listener *listener, void *data) {
	}
	view = server->interactive.view;
	switch (server->input_state) {
	case INPUT_STATE_BORDER_DRAG_TOP:
		render_view_border(renderer, output, view,
			view->x,
			server->cursor->y,
			view->xdg_surface->surface->current.width,
			view->xdg_surface->surface->current.height - (server->cursor->y - server->interactive.sy),
			1);
		break;
	case INPUT_STATE_BORDER_DRAG_LEFT:
		render_view_border(renderer, output, view,
			server->cursor->x,
			view->y,
			view->xdg_surface->surface->current.width - (server->cursor->x - server->interactive.sx),
			view->xdg_surface->surface->current.height,
			1);
		break;
	case INPUT_STATE_BORDER_DRAG_BOTTOM:
		render_view_border(renderer, output, view,
			view->x,
			view->y,
			view->xdg_surface->surface->current.width,
			server->cursor->y - server->interactive.sy,
			1);
		break;
	case INPUT_STATE_BORDER_DRAG_RIGHT:
		render_view_border(renderer, output, view,
			view->x,
			view->y,
			server->cursor->x - server->interactive.sx,
			view->xdg_surface->surface->current.height,
			1);
	case INPUT_STATE_BORDER_DRAG:
		box = wio_which_box(server);
		box = wio_canon_box(server, box);
		render_view_border(renderer, output, NULL, box.x, box.y, box.width, box.height, 1);
		break;
	case INPUT_STATE_MOVE:
		render_view_border(renderer, output, view,


@@ 350,11 325,12 @@ static void output_frame(struct wl_listener *listener, void *data) {
		break;
	case INPUT_STATE_NEW_END:
	case INPUT_STATE_RESIZE_END:
		render_view_border(renderer, output, NULL,
			server->interactive.sx, server->interactive.sy,
			server->cursor->x - server->interactive.sx,
			server->cursor->y - server->interactive.sy,
			1);
		box = wio_which_box(server);
		if (box.width > 0 && box.height > 0) {
			memcpy(color, surface, sizeof(color));
			wlr_render_rect(renderer, &box, color, output->wlr_output->transform_matrix);
		}
		render_view_border(renderer, output, NULL, box.x, box.y, box.width, box.height, 1);
		break;
	default:
		break;

M view.c => view.c +111 -39
@@ 5,6 5,10 @@
#include "server.h"
#include "view.h"

// TODO: scale
#define less_swap1(A, B) { if (A < B) { int C = A; A = B; B = C + window_border * 2; } }
#define less_swap2(A, B) { if (A < B) { int C = A; A = B - window_border * 2; B = C; } }

static void xdg_surface_map(struct wl_listener *listener, void *data) {
	struct wio_view *view = wl_container_of(listener, view, map);
	struct wio_server *server = view->server;


@@ 122,9 126,27 @@ static bool view_at(struct wio_view *view,
	return false;
}

static int portion(int x, int lo, int width) {
	x -= lo;
	if (x < 20) {
		return 0;
	}
	if (x > width-20) {
		return 2;
	}
	return 1;
}

static int which_corner(struct wlr_box *box, int x, int y) {
	int i, j;

	i = portion(x, box->x, box->width);
	j = portion(y, box->y, box->height);
	return 3*j+i;
}

struct wio_view *wio_view_at(struct wio_server *server, double lx, double ly,
		struct wlr_surface **surface, double *sx, double *sy,
		int *view_area) {
		struct wlr_surface **surface, double *sx, double *sy) {
	struct wlr_box border_box = {
		.x = 0, .y = 0,
		.width = 0, .height = 0,


@@ 133,46 155,18 @@ struct wio_view *wio_view_at(struct wio_server *server, double lx, double ly,
	wl_list_for_each(view, &server->views, link) {
		// Surface
		if (view_at(view, lx, ly, surface, sx, sy)) {
			*view_area = VIEW_AREA_SURFACE;
			return view;
		}
		// Top border
		border_box.height = window_border;
		border_box.width = view->xdg_surface->surface->current.width;
		border_box.x = view->x;
		border_box.y = view->y - window_border;
		if (wlr_box_contains_point(&border_box, server->cursor->x, server->cursor->y)) {
			*view_area = VIEW_AREA_BORDER_TOP;
			return view;
		}

		// Right border
		border_box.height = view->xdg_surface->surface->current.height;
		border_box.width = window_border;
		border_box.x = view->x + view->xdg_surface->surface->current.width;
		border_box.y = view->y;
		if (wlr_box_contains_point(&border_box, server->cursor->x, server->cursor->y)) {
			*view_area = VIEW_AREA_BORDER_RIGHT;
			view->area = VIEW_AREA_SURFACE;
			return view;
		}

		// Bottom border
		border_box.height = window_border;
		border_box.width = view->xdg_surface->surface->current.width;
		border_box.x = view->x;
		border_box.y = view->y + view->xdg_surface->surface->current.height;
		if (wlr_box_contains_point(&border_box, server->cursor->x, server->cursor->y)) {
			*view_area = VIEW_AREA_BORDER_BOTTOM;
			return view;
		}

		// Left border
		border_box.height = view->xdg_surface->surface->current.height;
		border_box.width = window_border;
		// Border
		border_box.x = view->x - window_border;
		border_box.y = view->y;
		if (wlr_box_contains_point(&border_box, server->cursor->x, server->cursor->y)) {
			*view_area = VIEW_AREA_BORDER_LEFT;
		border_box.y = view->y - window_border;
		border_box.width = view->xdg_surface->surface->current.width + window_border * 2;
		border_box.height = view->xdg_surface->surface->current.height + window_border * 2;
		if (wlr_box_contains_point(&border_box, lx, ly)) {
			view->area = which_corner(&border_box, lx, ly);
			*sx = lx - view->x;
			*sy = ly - view->y;
			return view;
		}
	}


@@ 189,3 183,81 @@ void wio_view_move(struct wio_view *view, int x, int y) {
		wlr_surface_send_enter(view->xdg_surface->surface, output->wlr_output);
	}
}

struct wlr_box wio_which_box(struct wio_server *server) {
	struct wlr_box box;
	int x1, x2, y1, y2;

	if (server->interactive.view == NULL) {
		goto End;
	}
	x2 = server->interactive.sx + server->interactive.view->xdg_surface->surface->current.width;
	y2 = server->interactive.sy + server->interactive.view->xdg_surface->surface->current.height;
	switch (server->interactive.view->area) {
	case VIEW_AREA_BORDER_TOP_LEFT:
		y1 = server->cursor->y;
		x1 = server->cursor->x;
		less_swap1(y2, y1);
		less_swap1(x2, x1);
		break;
	case VIEW_AREA_BORDER_TOP:
		y1 = server->cursor->y;
		x1 = server->interactive.sx;
		less_swap1(y2, y1);
		break;
	case VIEW_AREA_BORDER_TOP_RIGHT:
		y1 = server->cursor->y;
		x1 = server->interactive.sx;
		x2 = server->cursor->x;
		less_swap1(y2, y1);
		less_swap2(x2, x1);
		break;
	case VIEW_AREA_BORDER_LEFT:
		x1 = server->cursor->x;
		y1 = server->interactive.sy;
		less_swap1(x2, x1);
		break;
	case VIEW_AREA_BORDER_RIGHT:
		x1 = server->interactive.sx;
		x2 = server->cursor->x;
		y1 = server->interactive.sy;
		less_swap2(x2, x1);
		break;
	case VIEW_AREA_BORDER_BOTTOM_LEFT:
		x1 = server->cursor->x;
		y1 = server->interactive.sy;
		y2 = server->cursor->y;
		less_swap1(x2, x1);
		less_swap2(y2, y1);
		break;
	case VIEW_AREA_BORDER_BOTTOM:
		x1 = server->interactive.sx;
		y1 = server->interactive.sy;
		y2 = server->cursor->y;
		less_swap2(y2, y1);
		box.height = y2 - y1;
		break;
	case VIEW_AREA_BORDER_BOTTOM_RIGHT:
	End:
		x1 = server->interactive.sx;
		x2 = server->cursor->x;
		y1 = server->interactive.sy;
		y2 = server->cursor->y;
		less_swap2(x2, x1);
		less_swap2(y2, y1);
		break;
	}
	box.x = x1;
	box.y = y1;
	box.width = x2 - x1;
	box.height = y2 - y1;
	return box;
}

struct wlr_box wio_canon_box(struct wio_server *server, struct wlr_box box) {
	static struct wlr_box cache;
	if (box.width < MINWIDTH || box.height < MINHEIGHT) {
		return cache;
	}
	return cache = box;
}