~nickbp/gridmgr

eaeb1063f501cc46b1d3d00877aa2ef9466d1fff — Nick Parker 10 years ago 6174125
Rename commands, add window/monitor switching diagonals.

- All commands (incl grid commands) have been renamed for consistency
  across all actions.
- New commands now have diagonal equivalents.
M src/grid.cpp => src/grid.cpp +3 -3
@@ 22,11 22,11 @@
#include "viewport.h"
#include "window.h"

bool grid::set_window(DIR window) {
bool grid::set_active(POS window) {
	return window::select_activate(window);
}

bool grid::set_position(POS pos, DIR monitor) {
bool grid::set_position(POS gridpos, POS monitor) {
	// initializes to the currently active window
	ActiveWindow win;



@@ 59,7 59,7 @@ bool grid::set_position(POS pos, DIR monitor) {
	if (/* cur_viewport + cur_window -> cur_state */
			!pcalc.CurState(cur_viewport, cur_state) ||
			/* cur_state + pos -> next_state */
			!pcalc.NextState(cur_state, pos, next_state)) {
			!pcalc.NextState(cur_state, gridpos, next_state)) {
		return false;
	}


M src/grid.h => src/grid.h +2 -2
@@ 24,12 24,12 @@
namespace grid {
	/* Selects and makes active the window in the specified direction relative
	 * to the currently active window. */
	bool set_window(DIR window);
	bool set_active(POS window);

	/* Selects the active window and moves/resizes it to the requested
	 * position/monitor, according to its current state.
	 * Returns true if successful, false otherwise. */
	bool set_position(POS position, DIR monitor);
	bool set_position(POS gridpos, POS monitor);
}

#endif

M src/main.cpp => src/main.cpp +53 -81
@@ 46,34 46,34 @@ static void syntax(char* appname) {
	ERROR_RAWDIR("Usage: %s [options] <window/position> [w/p] [w/p]", appname);
#endif
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Windows (activate adjacent window):");
	ERROR_RAWDIR("          -------           ");
	ERROR_RAWDIR("         |  wup  |          ");
	ERROR_RAWDIR("  ------ + ----- + -------  ");
	ERROR_RAWDIR(" | wleft |       | wright | ");
	ERROR_RAWDIR("  ------ + ----- + -------  ");
	ERROR_RAWDIR("         | wdown |          ");
	ERROR_RAWDIR("          -------           ");
	ERROR_RAWDIR("Windows (activate adjacent (w)indow):");
	ERROR_RAWDIR("  -------- --------- ---------  ");
	ERROR_RAWDIR(" | wuleft |   wup   | wuright | ");
	ERROR_RAWDIR(" |------- + ------- + --------| ");
	ERROR_RAWDIR(" | wleft  |         |  wright | ");
	ERROR_RAWDIR(" |------- + ------- + --------| ");
	ERROR_RAWDIR(" | wdleft |  wdown  | wdright | ");
	ERROR_RAWDIR("  -------- --------- ---------  ");
#ifdef USE_XINERAMA
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Monitors (move window to adjacent monitor):");
	ERROR_RAWDIR("          -------           ");
	ERROR_RAWDIR("         |  mup  |          ");
	ERROR_RAWDIR("  ------ + ----- + -------  ");
	ERROR_RAWDIR(" | mleft |       | mright | ");
	ERROR_RAWDIR("  ------ + ----- + -------  ");
	ERROR_RAWDIR("         | mdown |          ");
	ERROR_RAWDIR("          -------           ");
	ERROR_RAWDIR("Monitors (move window to adjacent (m)onitor):");
	ERROR_RAWDIR("  -------- --------- ---------  ");
	ERROR_RAWDIR(" | muleft |   mup   | muright | ");
	ERROR_RAWDIR(" |------- + ------- + --------| ");
	ERROR_RAWDIR(" | mleft  |         |  mright | ");
	ERROR_RAWDIR(" |------- + ------- + --------| ");
	ERROR_RAWDIR(" | mdleft |  mdown  | mdright | ");
	ERROR_RAWDIR("  -------- --------- ---------  ");
#endif
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Positions (position window on grid):");
	ERROR_RAWDIR("  -----------------------------  ");
	ERROR_RAWDIR(" | topleft |  top   | topright | ");
	ERROR_RAWDIR(" | ------- + ------ + -------- | ");
	ERROR_RAWDIR(" |  left   | center |   right  | ");
	ERROR_RAWDIR(" | ------- + ------ + -------- | ");
	ERROR_RAWDIR(" | botleft | bottom | botright | ");
	ERROR_RAWDIR("  -----------------------------  ");
	ERROR_RAWDIR("Positions (position window on (g)rid):");
	ERROR_RAWDIR("  -------- --------- ---------  ");
	ERROR_RAWDIR(" | guleft |   gup   | guright | ");
	ERROR_RAWDIR(" |------- + ------- + --------| ");
	ERROR_RAWDIR(" | gleft  | gcenter |  gright | ");
	ERROR_RAWDIR(" |------- + ------- + --------| ");
	ERROR_RAWDIR(" | gdleft |  gdown  | gdright | ");
	ERROR_RAWDIR("  -------- --------- ---------  ");
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Options:");
	ERROR_RAWDIR("  -h/--help        This help text.");


@@ 82,55 82,28 @@ static void syntax(char* appname) {
	ERROR_RAWDIR("");
}

static bool str_to_pos(const char* arg, grid::POS& out) {
	if (strcmp(arg, "topleft") == 0) {
		out = grid::POS_TOP_LEFT;
	} else if (strcmp(arg, "top") == 0) {
		out = grid::POS_TOP_CENTER;
	} else if (strcmp(arg, "topright") == 0) {
		out = grid::POS_TOP_RIGHT;
static bool strsub_to_pos(const char* arg, grid::POS& out, bool center_is_valid) {
	if (strcmp(arg, "uleft") == 0) {
		out = grid::POS_UP_LEFT;
	} else if (strcmp(arg, "up") == 0) {
		out = grid::POS_UP_CENTER;
	} else if (strcmp(arg, "uright") == 0) {
		out = grid::POS_UP_RIGHT;

	} else if (strcmp(arg, "left") == 0) {
		out = grid::POS_LEFT;
	} else if (strcmp(arg, "center") == 0) {
	} else if (center_is_valid && strcmp(arg, "center") == 0) {
		out = grid::POS_CENTER;
	} else if (strcmp(arg, "right") == 0) {
		out = grid::POS_RIGHT;
	} else if (strcmp(arg, "botleft") == 0) {
		out = grid::POS_BOT_LEFT;
	} else if (strcmp(arg, "bottom") == 0 || strcmp(arg, "bot") == 0) {
		out = grid::POS_BOT_CENTER;
	} else if (strcmp(arg, "botright") == 0) {
		out = grid::POS_BOT_RIGHT;
	} else {
		return false;
	}
	return true;
}

static bool str_to_win(const char* arg, grid::DIR& out) {
	if (strcmp(arg, "wup") == 0) {
		out = grid::DIR_UP;
	} else if (strcmp(arg, "wleft") == 0) {
		out = grid::DIR_LEFT;
	} else if (strcmp(arg, "wright") == 0) {
		out = grid::DIR_RIGHT;
	} else if (strcmp(arg, "wdown") == 0) {
		out = grid::DIR_DOWN;
	} else {
		return false;
	}
	return true;
}
	} else if (strcmp(arg, "dleft") == 0) {
		out = grid::POS_DOWN_LEFT;
	} else if (strcmp(arg, "down") == 0) {
		out = grid::POS_DOWN_CENTER;
	} else if (strcmp(arg, "dright") == 0) {
		out = grid::POS_DOWN_RIGHT;

static bool str_to_mon(const char* arg, grid::DIR& out) {
	if (strcmp(arg, "mup") == 0) {
		out = grid::DIR_UP;
	} else if (strcmp(arg, "mleft") == 0) {
		out = grid::DIR_LEFT;
	} else if (strcmp(arg, "mright") == 0) {
		out = grid::DIR_RIGHT;
	} else if (strcmp(arg, "mdown") == 0) {
		out = grid::DIR_DOWN;
	} else {
		return false;
	}


@@ 140,8 113,8 @@ static bool str_to_mon(const char* arg, grid::DIR& out) {
namespace {
	enum CMD { CMD_UNKNOWN, CMD_HELP, CMD_POSITION };
	CMD run_cmd = CMD_UNKNOWN;
	grid::POS position = grid::POS_CURRENT;
	grid::DIR monitor = grid::DIR_CURRENT, window = grid::DIR_CURRENT;
	grid::POS gridpos = grid::POS_CURRENT,
		monitor = grid::POS_CURRENT, window = grid::POS_CURRENT;
}

static bool parse_config(int argc, char* argv[]) {


@@ 171,28 144,27 @@ static bool parse_config(int argc, char* argv[]) {
				const char* arg = argv[i];
				//DEBUG("%d %d %s", argc, i, arg);
				grid::POS tmp_pos;
				grid::DIR tmp_dir;
				if (str_to_pos(arg, tmp_pos)) {
					if (position != grid::POS_CURRENT) {
				if (arg[0] == 'g' && strsub_to_pos(arg+1, tmp_pos, true)) {
					if (gridpos != grid::POS_CURRENT) {
						ERROR("%s: Multiple positions specified: '%s'", argv[0], argv[i]);
						syntax(argv[0]);
						return false;
					}
					position = tmp_pos;
				} else if (str_to_win(arg, tmp_dir)) {
					if (window != grid::DIR_CURRENT) {
					gridpos = tmp_pos;
				} else if (arg[0] == 'w' && strsub_to_pos(arg+1, tmp_pos, false)) {
					if (window != grid::POS_CURRENT) {
						ERROR("%s: Multiple windows specified: '%s'", argv[0], argv[i]);
						syntax(argv[0]);
						return false;
					}
					window = tmp_dir;
				} else if (str_to_mon(arg, tmp_dir)) {
					if (monitor != grid::DIR_CURRENT) {
					window = tmp_pos;
				} else if (arg[0] == 'm' && strsub_to_pos(arg+1, tmp_pos, false)) {
					if (monitor != grid::POS_CURRENT) {
						ERROR("%s: Multiple monitors specified: '%s'", argv[0], argv[i]);
						syntax(argv[0]);
						return false;
					}
					monitor = tmp_dir;
					monitor = tmp_pos;
				} else {
					ERROR("%s: Unknown argument: '%s'", argv[0], argv[i]);
					syntax(argv[0]);


@@ 258,12 230,12 @@ int main(int argc, char* argv[]) {
		return EXIT_SUCCESS;
	case CMD_POSITION:
		// activate window (if specified)
		if (window != grid::DIR_CURRENT && !grid::set_window(window)) {
		if (window != grid::POS_CURRENT && !grid::set_active(window)) {
			return EXIT_FAILURE;
		}
		// move window (if specified)
		if (position != grid::POS_CURRENT || monitor != grid::DIR_CURRENT) {
			return grid::set_position(position, monitor) ?
		if (gridpos != grid::POS_CURRENT || monitor != grid::POS_CURRENT) {
			return grid::set_position(gridpos, monitor) ?
				EXIT_SUCCESS : EXIT_FAILURE;
		}
		return EXIT_SUCCESS;

M src/neighbor.cpp => src/neighbor.cpp +113 -40
@@ 33,60 33,122 @@ namespace {
			  y(MIDPOINT(d.y, d.height)) { }

		/* Returns whether 'p' is in the specified direction relative to this
		   point. Eg DIR_UP -> "is p above this?" */
		bool direction(grid::DIR dir, const point& p) const {
		   point. Eg POS_UP -> "is p above this?" */
		bool direction(grid::POS dir, const point& p) const {
			// note: M_PI_4 == pi/4, from math.h.
			if (y == p.y && x == p.x) { return false; } // p is on top of us
			if (y == p.y && x == p.x) {
				// p is on top of us, avoid getting stuck
				return false;
			}
			//DEBUG("%s: %f vs %f", grid::dir_str(dir), abs_atan(p), (M_PI_4));
			switch (dir) {
			case grid::DIR_UP:
			case grid::POS_UP_LEFT:
				return (y > p.y && x > p.x);
			case grid::POS_UP_RIGHT:
				return (y > p.y && x < p.x);
			case grid::POS_DOWN_LEFT:
				return (y < p.y && x > p.x);
			case grid::POS_DOWN_RIGHT:
				return (y < p.y && x < p.x);

			case grid::POS_UP_CENTER:
				if (y < p.y) { return false; } // p is below us
				return abs_atan(p) >= M_PI_4; // p is too far right/left of us
			case grid::DIR_DOWN:
			case grid::POS_DOWN_CENTER:
				if (y > p.y) { return false; } // p is above us
				return abs_atan(p) >= M_PI_4; // p is too far right/left of us

			case grid::DIR_LEFT:
			case grid::POS_LEFT:
				if (x < p.x) { return false; } // p is right of us
				return abs_atan(p) <= M_PI_4; // p is too far above/below us
			case grid::DIR_RIGHT:
			case grid::POS_RIGHT:
				if (x > p.x) { return false; } // p is left of us
				return abs_atan(p) <= M_PI_4; // p is too far above/below us

			case grid::DIR_UNKNOWN:
			case grid::DIR_CURRENT:
			case grid::POS_CENTER:
			case grid::POS_UNKNOWN:
			case grid::POS_CURRENT:
				ERROR("Internal error: invalid dir %s", grid::pos_str(dir));
				break;
			}
			return false;//???
		}

		double distance(const point& p) const {
			long w = p.x - x, h = p.y - y;
			return sqrt((w * w) + (h * h));
		/* Not a true 'distance', more of a weighting where smaller is better. */
		double distance(grid::POS dir, const point& p) const {
			long w = DISTANCE(p.x, x), h = DISTANCE(p.y, y);
			switch (dir) {
			case grid::POS_UP_LEFT:
			case grid::POS_UP_RIGHT:
			case grid::POS_DOWN_LEFT:
			case grid::POS_DOWN_RIGHT:
				//use a weighting that favors diagonals (where w == h)
			{
				double dist = sqrt((w * w) + (h * h));
				//sqrt again to further decrease dist's importance
				return sqrt(dist) * DISTANCE(w, h);
			}

			case grid::POS_UP_CENTER:
			case grid::POS_DOWN_CENTER:
			case grid::POS_RIGHT:
			case grid::POS_LEFT:
				//use actual distance, diagonals are automatically farther
				return sqrt((w * w) + (h * h));

			case grid::POS_CENTER:
			case grid::POS_UNKNOWN:
			case grid::POS_CURRENT:
				ERROR("Internal error: invalid dir %s", grid::pos_str(dir));
				break;
			}
			return 0;//???
		}

		void shift_pos(grid::DIR dir, long max_x, long max_y) {
		void shift_pos(grid::POS dir, long max_x, long max_y) {
			switch (dir) {
			case grid::DIR_UP:
			case grid::POS_UP_LEFT:
				// we're looking up left, so move the point down right
				y += max_y;
				x += max_x;
				break;
			case grid::POS_DOWN_RIGHT:
				// opposite of UP_LEFT
				shift_pos(grid::POS_UP_LEFT, -1 * max_x, -1 * max_y);
				break;

			case grid::POS_UP_RIGHT:
				// we're looking up right, so move the point down left
				y += max_y;
				x -= max_x;
				break;
			case grid::POS_DOWN_LEFT:
				// opposite of UP_RIGHT
				shift_pos(grid::POS_UP_RIGHT, -1 * max_x, -1 * max_y);
				break;

			case grid::POS_UP_CENTER:
				// we're looking up, so move the point down
				y += max_y;
				break;
			case grid::DIR_DOWN:
				// opposite of DIR_UP
				shift_pos(grid::DIR_UP, -1 * max_x, -1 * max_y);
			case grid::POS_DOWN_CENTER:
				// opposite of UP_CENTER
				shift_pos(grid::POS_UP_CENTER, -1 * max_x, -1 * max_y);
				break;

			case grid::DIR_LEFT:
			case grid::POS_LEFT:
				// we're looking left, so move the point right
				x += max_x;
				break;
			case grid::DIR_RIGHT:
				// opposite of DIR_LEFT
				shift_pos(grid::DIR_LEFT, -1 * max_x, -1 * max_y);
			case grid::POS_RIGHT:
				// opposite of LEFT
				shift_pos(grid::POS_LEFT, -1 * max_x, -1 * max_y);
				break;

			case grid::DIR_UNKNOWN:
			case grid::DIR_CURRENT:
			case grid::POS_CENTER:
			case grid::POS_UNKNOWN:
			case grid::POS_CURRENT:
				ERROR("Internal error: invalid dir %s", grid::pos_str(dir));
				break;//???
			}
		}


@@ 107,13 169,14 @@ namespace {

	/* Finds and selects the nearest non-active point in the given direction and
	   returns true, or returns false if none was found. */
	bool select_nearest_in_direction(grid::DIR dir, const std::vector<point>& pts,
	bool select_nearest_in_direction(grid::POS dir, const std::vector<point>& pts,
			size_t active, size_t& select) {
		// find nearst point that matches the given direction
		double nearest_dist = 0;
		long nearest_i = -1;
		const point& active_pt = pts[active];
		DEBUG("search %lu for points %s of %ld,%ld:", pts.size()-1, grid::dir_str(dir), active_pt.x, active_pt.y);
		DEBUG("search %lu for points %s of %ld,%ld:",
				pts.size()-1, grid::pos_str(dir), active_pt.x, active_pt.y);
		for (size_t i = 0; i < pts.size(); ++i) {
			if (i == active) {
				DEBUG("skip: %ld,%ld", pts[i].x, pts[i].y);


@@ 122,7 185,7 @@ namespace {

			const point& pt = pts[i];
			if (active_pt.direction(dir, pt)) {
				double dist = active_pt.distance(pt);
				double dist = active_pt.distance(dir, pt);
				DEBUG("match!: %ld,%ld (dist %.02f)", pts[i].x, pts[i].y, dist);
				if (nearest_i < 0 || dist < nearest_dist) {
					nearest_i = i;


@@ 143,34 206,44 @@ namespace {

	/* When nothing is found in a given direction, this function determines what
	   the fallback direction ordering should be */
	grid::DIR fallback_direction(grid::DIR dir) {
	grid::POS fallback_direction(grid::POS dir) {
		// go clockwise
		switch (dir) {
		case grid::DIR_UP:
			return grid::DIR_RIGHT;
		case grid::DIR_RIGHT:
			return grid::DIR_DOWN;
		case grid::DIR_DOWN:
			return grid::DIR_LEFT;
		case grid::DIR_LEFT:
			return grid::DIR_UP;
		case grid::DIR_UNKNOWN:
		case grid::DIR_CURRENT:
		case grid::POS_UP_CENTER:
			return grid::POS_UP_RIGHT;
		case grid::POS_UP_RIGHT:
			return grid::POS_RIGHT;
		case grid::POS_RIGHT:
			return grid::POS_DOWN_RIGHT;
		case grid::POS_DOWN_RIGHT:
			return grid::POS_DOWN_CENTER;
		case grid::POS_DOWN_CENTER:
			return grid::POS_DOWN_LEFT;
		case grid::POS_DOWN_LEFT:
			return grid::POS_LEFT;
		case grid::POS_LEFT:
			return grid::POS_UP_LEFT;
		case grid::POS_UP_LEFT:
			return grid::POS_UP_CENTER;
		case grid::POS_CENTER:
		case grid::POS_UNKNOWN:
		case grid::POS_CURRENT:
			ERROR("Internal error: invalid dir %s", grid::pos_str(dir));
			break;
		}
		return grid::DIR_UP;//???
		return grid::POS_UP_CENTER;//???
	}
}

#define MAX_X(dim) (dim.x + dim.width)
#define MAX_Y(dim) (dim.y + dim.height)

void neighbor::select(grid::DIR dir, const dim_list_t& all, size_t active, size_t& select) {
void neighbor::select(grid::POS dir, const dim_list_t& all, size_t active, size_t& select) {
	if (all.size() <= 1) {
		select = 0;
		return;
	}
	if (dir == grid::DIR_CURRENT) {
	if (dir == grid::POS_CURRENT) {
		select = active;
		return;
	}

M src/neighbor.h => src/neighbor.h +1 -1
@@ 27,7 27,7 @@
typedef std::vector<Dimensions> dim_list_t;

namespace neighbor {
	void select(grid::DIR dir, const dim_list_t& all, size_t active, size_t& select);
	void select(grid::POS dir, const dim_list_t& all, size_t active, size_t& select);
}

#endif

M src/pos.h => src/pos.h +14 -39
@@ 24,9 24,9 @@ namespace grid {
	 * CURRENT is "use the current position", useful when only switching monitors */
	enum POS {
		POS_UNKNOWN, POS_CURRENT,
		POS_TOP_LEFT, POS_TOP_CENTER, POS_TOP_RIGHT,
		POS_UP_LEFT, POS_UP_CENTER, POS_UP_RIGHT,
		POS_LEFT, POS_CENTER, POS_RIGHT,
		POS_BOT_LEFT, POS_BOT_CENTER, POS_BOT_RIGHT
		POS_DOWN_LEFT, POS_DOWN_CENTER, POS_DOWN_RIGHT
	};

	inline const char* pos_str(POS pos) {


@@ 35,49 35,24 @@ namespace grid {
			return "UNKNOWN";
		case POS_CURRENT:
			return "CURRENT";
		case POS_TOP_LEFT:
			return "TOP_LEFT";
		case POS_TOP_CENTER:
			return "TOP_CENTER";
		case POS_TOP_RIGHT:
			return "TOP_RIGHT";
		case POS_UP_LEFT:
			return "UP_LEFT";
		case POS_UP_CENTER:
			return "UP_CENTER";
		case POS_UP_RIGHT:
			return "UP_RIGHT";
		case POS_LEFT:
			return "LEFT";
		case POS_CENTER:
			return "CENTER";
		case POS_RIGHT:
			return "RIGHT";
		case POS_BOT_LEFT:
			return "BOT_LEFT";
		case POS_BOT_CENTER:
			return "BOT_CENTER";
		case POS_BOT_RIGHT:
			return "BOT_RIGHT";
		}
		return "???";
	}

	/* These are the available directions for movement between monitors and/or
	 * windows. CURRENT is "use the current monitor/window". */
	enum DIR {
		DIR_UNKNOWN, DIR_CURRENT,
		DIR_UP, DIR_RIGHT, DIR_DOWN, DIR_LEFT
	};

	inline const char* dir_str(DIR dir) {
		switch (dir) {
		case DIR_UNKNOWN:
			return "UNKNOWN";
		case DIR_CURRENT:
			return "CURRENT";
		case DIR_UP:
			return "UP";
		case DIR_RIGHT:
			return "RIGHT";
		case DIR_DOWN:
			return "DOWN";
		case DIR_LEFT:
			return "LEFT";
		case POS_DOWN_LEFT:
			return "DOWN_LEFT";
		case POS_DOWN_CENTER:
			return "DOWN_CENTER";
		case POS_DOWN_RIGHT:
			return "DOWN_RIGHT";
		}
		return "???";
	}

M src/position.cpp => src/position.cpp +39 -39
@@ 66,21 66,21 @@ bool PositionCalc::CurState(const Dimensions& viewport, State& out) const {
			if (NEAR(rel_x, 0)) {
				if (NEAR(rel_y, 0)) {
					// top left quadrant
					out.pos = grid::POS_TOP_LEFT;
					out.pos = grid::POS_UP_LEFT;
					out.mode = grid::MODE_TWO_COL;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom left quadrant
					out.pos = grid::POS_BOT_LEFT;
					out.pos = grid::POS_DOWN_LEFT;
					out.mode = grid::MODE_TWO_COL;
				}
			} else if (NEAR(rel_x, viewport.width / 2.)) {
				if (NEAR(rel_y, 0)) {
					// top right quadrant
					out.pos = grid::POS_TOP_RIGHT;
					out.pos = grid::POS_UP_RIGHT;
					out.mode = grid::MODE_TWO_COL;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom right quadrant
					out.pos = grid::POS_BOT_RIGHT;
					out.pos = grid::POS_DOWN_RIGHT;
					out.mode = grid::MODE_TWO_COL;
				}
			}


@@ 101,31 101,31 @@ bool PositionCalc::CurState(const Dimensions& viewport, State& out) const {
			if (NEAR(rel_x, 0)) {
				if (NEAR(rel_y, 0)) {
					// top left col
					out.pos = grid::POS_TOP_LEFT;
					out.pos = grid::POS_UP_LEFT;
					out.mode = grid::MODE_THREE_COL_S;
				} if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom left col
					out.pos = grid::POS_BOT_LEFT;
					out.pos = grid::POS_DOWN_LEFT;
					out.mode = grid::MODE_THREE_COL_S;
				}
			} else if (NEAR(rel_x, viewport.width / 3.)) {
				if (NEAR(rel_y, 0)) {
					// top center col
					out.pos = grid::POS_TOP_CENTER;
					out.pos = grid::POS_UP_CENTER;
					out.mode = grid::MODE_THREE_COL_S;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom center col
					out.pos = grid::POS_BOT_CENTER;
					out.pos = grid::POS_DOWN_CENTER;
					out.mode = grid::MODE_THREE_COL_S;
				}
			} else if (NEAR(rel_x, 2 * viewport.width / 3.)) {
				if (NEAR(rel_y, 0)) {
					// top right col
					out.pos = grid::POS_TOP_RIGHT;
					out.pos = grid::POS_UP_RIGHT;
					out.mode = grid::MODE_THREE_COL_S;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom right col
					out.pos = grid::POS_BOT_RIGHT;
					out.pos = grid::POS_DOWN_RIGHT;
					out.mode = grid::MODE_THREE_COL_S;
				}
			}


@@ 150,21 150,21 @@ bool PositionCalc::CurState(const Dimensions& viewport, State& out) const {
			if (NEAR(rel_x, 0)) {
				if (NEAR(rel_y, 0)) {
					// top left two cols
					out.pos = grid::POS_TOP_LEFT;
					out.pos = grid::POS_UP_LEFT;
					out.mode = grid::MODE_THREE_COL_L;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom left two cols
					out.pos = grid::POS_BOT_LEFT;
					out.pos = grid::POS_DOWN_LEFT;
					out.mode = grid::MODE_THREE_COL_L;
				}
			} else if (NEAR(rel_x, viewport.width / 3.)) {
				if (NEAR(rel_y, 0)) {
					// top right two cols
					out.pos = grid::POS_TOP_RIGHT;
					out.pos = grid::POS_UP_RIGHT;
					out.mode = grid::MODE_THREE_COL_L;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom right two cols
					out.pos = grid::POS_BOT_RIGHT;
					out.pos = grid::POS_DOWN_RIGHT;
					out.mode = grid::MODE_THREE_COL_L;
				}
			}


@@ 185,11 185,11 @@ bool PositionCalc::CurState(const Dimensions& viewport, State& out) const {
		if (NEAR(window.height, viewport.height / 2.)) {
			if (NEAR(rel_y, 0)) {
				// top half
				out.pos = grid::POS_TOP_CENTER;
				out.pos = grid::POS_UP_CENTER;
				out.mode = grid::MODE_THREE_COL_L;
			} else if (NEAR(rel_y, viewport.height / 2.)) {
				// bottom half
				out.pos = grid::POS_BOT_CENTER;
				out.pos = grid::POS_DOWN_CENTER;
				out.mode = grid::MODE_THREE_COL_L;
			}
		} else if (NEAR(window.height, viewport.height) &&


@@ 217,9 217,9 @@ bool PositionCalc::NextState(const State& cur, grid::POS req_pos, State& out) co
		// position is same, so rotate mode
		out.pos = cur.pos;
		switch (cur.pos) {
		case grid::POS_TOP_CENTER:
		case grid::POS_UP_CENTER:
		case grid::POS_CENTER:
		case grid::POS_BOT_CENTER:
		case grid::POS_DOWN_CENTER:
			// for center col: 3x2L -> 3x2S (-> 3x2L) (no 2x2)
			switch (cur.mode) {
			case grid::MODE_THREE_COL_L:


@@ 249,9 249,9 @@ bool PositionCalc::NextState(const State& cur, grid::POS req_pos, State& out) co
		// new position, so start with initial mode
		out.pos = req_pos;
		switch (req_pos) {
		case grid::POS_TOP_CENTER:
		case grid::POS_UP_CENTER:
		case grid::POS_CENTER:
		case grid::POS_BOT_CENTER:
		case grid::POS_DOWN_CENTER:
			// for center col: start with 3x2L
			out.mode = grid::MODE_THREE_COL_L;
			break;


@@ 276,15 276,15 @@ bool PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
	switch (state.mode) {
	case grid::MODE_TWO_COL:
		switch (state.pos) {
		case grid::POS_TOP_LEFT:// top left quadrant
		case grid::POS_UP_LEFT:// top left quadrant
			rel_x = 0;
			rel_y = 0;
			out.width = viewport.width / 2.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_TOP_CENTER:// invalid, use mode THREE_COL_S/L
		case grid::POS_UP_CENTER:// invalid, use mode THREE_COL_S/L
			return false;
		case grid::POS_TOP_RIGHT:// top right quadrant
		case grid::POS_UP_RIGHT:// top right quadrant
			rel_x = viewport.width / 2.;
			rel_y = 0;
			out.width = viewport.width / 2.;


@@ 304,15 304,15 @@ bool PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
			out.width = viewport.width / 2.;
			out.height = viewport.height;
			break;
		case grid::POS_BOT_LEFT:// bottom left quadrant
		case grid::POS_DOWN_LEFT:// bottom left quadrant
			rel_x = 0;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 2.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_BOT_CENTER:// invalid, use mode THREE_COL_S/L
		case grid::POS_DOWN_CENTER:// invalid, use mode THREE_COL_S/L
			return false;
		case grid::POS_BOT_RIGHT:// bottom right quadrant
		case grid::POS_DOWN_RIGHT:// bottom right quadrant
			rel_x = viewport.width / 2.;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 2.;


@@ 325,19 325,19 @@ bool PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
		break;
	case grid::MODE_THREE_COL_S:
		switch (state.pos) {
		case grid::POS_TOP_LEFT:// top left col
		case grid::POS_UP_LEFT:// top left col
			rel_x = 0;
			rel_y = 0;
			out.width = viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_TOP_CENTER:// top center col
		case grid::POS_UP_CENTER:// top center col
			rel_x = viewport.width / 3.;
			rel_y = 0;
			out.width = viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_TOP_RIGHT:// top right col
		case grid::POS_UP_RIGHT:// top right col
			rel_x = 2 * viewport.width / 3.;
			rel_y = 0;
			out.width = viewport.width / 3.;


@@ 361,19 361,19 @@ bool PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
			out.width = viewport.width / 3;
			out.height = viewport.height;
			break;
		case grid::POS_BOT_LEFT:// bottom left col
		case grid::POS_DOWN_LEFT:// bottom left col
			rel_x = 0;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_BOT_CENTER:// bottom center col
		case grid::POS_DOWN_CENTER:// bottom center col
			rel_x = viewport.width / 3.;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_BOT_RIGHT:// bottom right col
		case grid::POS_DOWN_RIGHT:// bottom right col
			rel_x = 2 * viewport.width / 3.;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 3.;


@@ 385,19 385,19 @@ bool PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
		break;
	case grid::MODE_THREE_COL_L:
		switch (state.pos) {
		case grid::POS_TOP_LEFT:// top left two cols
		case grid::POS_UP_LEFT:// top left two cols
			rel_x = 0;
			rel_y = 0;
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_TOP_CENTER:// top half
		case grid::POS_UP_CENTER:// top half
			rel_x = 0;
			rel_y = 0;
			out.width = viewport.width;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_TOP_RIGHT:// top right two cols
		case grid::POS_UP_RIGHT:// top right two cols
			rel_x = viewport.width / 3.;
			rel_y = 0;
			out.width = 2 * viewport.width / 3.;


@@ 421,19 421,19 @@ bool PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height;
			break;
		case grid::POS_BOT_LEFT:// bottom left two cols
		case grid::POS_DOWN_LEFT:// bottom left two cols
			rel_x = 0;
			rel_y = viewport.height / 2.;
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_BOT_CENTER:// bottom half
		case grid::POS_DOWN_CENTER:// bottom half
			rel_x = 0;
			rel_y = viewport.height / 2.;
			out.width = viewport.width;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_BOT_RIGHT:// bottom right two cols
		case grid::POS_DOWN_RIGHT:// bottom right two cols
			rel_x = viewport.width / 3.;
			rel_y = viewport.height / 2.;
			out.width = 2 * viewport.width / 3.;


@@ 469,7 469,7 @@ void PositionCalc::ViewportToDim(const Dimensions& cur_viewport,
	const double ratio_x = next_viewport.width / (double)cur_viewport.width,
		ratio_y = next_viewport.height / (double)cur_viewport.height;

	// avoid implicit floor to avoid some rounding error:
	// avoid implicit floor to avoid cumulative rounding error:
	out.x = round((window.x - cur_viewport.x) * ratio_x + next_viewport.x);
	out.y = round((window.y - cur_viewport.y) * ratio_y + next_viewport.y);
	out.width = round(window.width * ratio_x);

M src/viewport.cpp => src/viewport.cpp +1 -1
@@ 58,7 58,7 @@ namespace {
	}
}

bool ViewportCalc::Viewports(grid::DIR monitor,
bool ViewportCalc::Viewports(grid::POS monitor,
		Dimensions& cur_viewport, Dimensions& next_viewport) const {
	dim_list_t viewports;
	size_t active, neighbor;

M src/viewport.h => src/viewport.h +1 -1
@@ 27,7 27,7 @@ public:
	ViewportCalc(const Dimensions& activewin)
		: activewin(activewin) { }

	bool Viewports(grid::DIR monitor,
	bool Viewports(grid::POS monitor,
			Dimensions& cur_viewport, Dimensions& next_viewport) const;

private:

M src/window.cpp => src/window.cpp +1 -1
@@ 246,7 246,7 @@ namespace {
	}
}

bool window::select_activate(grid::DIR dir) {
bool window::select_activate(grid::POS dir) {
	Display* disp = XOpenDisplay(NULL);
	if (disp == NULL) {
		ERROR_DIR("unable to get display");

M src/window.h => src/window.h +1 -1
@@ 26,7 26,7 @@

namespace window {
	/* Finds the nearest window in the given direction and activates it. */
	bool select_activate(grid::DIR dir);
	bool select_activate(grid::POS dir);
}

class ActiveWindow {