~nickbp/gridmgr

c27c3533f72e0721354c82e16472aa3ef7839bf7 — Nick Parker 10 years ago dfb79a4
make the position math steps more visible to the outside. when next position is going to be filling the screen, just maximize the window.
5 files changed, 457 insertions(+), 418 deletions(-)

M src/grid.cpp
M src/position.cpp
M src/position.h
M src/window.cpp
M src/window.h
M src/grid.cpp => src/grid.cpp +20 -2
@@ 21,6 21,8 @@
#include "position.h"
#include "window.h"

#include "config.h"

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


@@ 34,7 36,23 @@ bool grid::set_position(POS pos) {
	//using the requested position and current dimensions,
	//calculate and set new dimensions
	PositionCalc calc(viewport, cur_dim);
	

	State cur_state, next_state;
	if (!calc.CurState(cur_state) ||
			!calc.NextState(cur_state, pos, next_state)) {
		return false;
	}

	//if we're going to be filling the screen anyway, just maximize
	if (next_state.pos == grid::POS_CENTER &&
		next_state.mode == grid::MODE_THREE_COL_L) {
		if (win.Maximize()) {
			return true;
		}
		ERROR_DIR("Maximizing window failed, falling back to filling screen.");
	}

	Dimensions next_dim;
	return calc.NextPos(pos, next_dim) && win.MoveResize(next_dim);
	return calc.StateToDim(next_state, next_dim) &&
		win.MoveResize(next_dim);
}

M src/position.cpp => src/position.cpp +382 -403
@@ 20,21 20,15 @@
#include "config.h"

namespace {
	enum MODE {
		MODE_UNKNOWN,
		MODE_TWO_COL,// 2x2 (not applicable for center col positions)
		MODE_THREE_COL_S,// 3x2 small: each position filling 1 column
		MODE_THREE_COL_L// 3x2 large: sides filling 2 columns and center filling full width
	};
	inline const char* mode_str(MODE mode) {
	inline const char* mode_str(grid::MODE mode) {
		switch (mode) {
		case MODE_UNKNOWN:
		case grid::MODE_UNKNOWN:
			return "UNKNOWN";
		case MODE_TWO_COL:
		case grid::MODE_TWO_COL:
			return "TWO_COL";
		case MODE_THREE_COL_S:
		case grid::MODE_THREE_COL_S:
			return "THREE_COL_S";
		case MODE_THREE_COL_L:
		case grid::MODE_THREE_COL_L:
			return "THREE_COL_L";
		default:
			break;


@@ 42,205 36,6 @@ namespace {
		return "???";
	}

	struct State {
		grid::POS pos;
		MODE mode;
	};

	// given window's state, calculate its dimensions.
	// could have some kind of fancy autogeneration here,
	// but there's a very finite number of possible positions (for now?)
	bool calculate_state(const Dimensions& viewport,
			const State& state, Dimensions& window_out) {
		bool ret = true;
		long rel_x = 0, rel_y = 0;//coordinates relative to viewport
		switch (state.mode) {
		case MODE_TWO_COL:
			switch (state.pos) {
			case grid::POS_TOP_LEFT:// top left quadrant
				rel_x = 0;
				rel_y = 0;
				window_out.width = viewport.width / 2.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_TOP_CENTER:// invalid, use mode THREE_COL_S/L
				return false;
			case grid::POS_TOP_RIGHT:// top right quadrant
				rel_x = viewport.width / 2.;
				rel_y = 0;
				window_out.width = viewport.width / 2.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_LEFT:// left half
				rel_x = 0;
				rel_y = 0;
				window_out.width = viewport.width / 2.;
				window_out.height = viewport.height;
				break;
			case grid::POS_CENTER:// invalid, use mode THREE_COL_S/L
				return false;
			case grid::POS_RIGHT:// right half
				rel_x = viewport.width / 2.;
				rel_y = 0;
				window_out.width = viewport.width / 2.;
				window_out.height = viewport.height;
				break;
			case grid::POS_BOT_LEFT:// bottom left quadrant
				rel_x = 0;
				rel_y = viewport.height / 2.;
				window_out.width = viewport.width / 2.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_BOT_CENTER:// invalid, use mode THREE_COL_S/L
				return false;
			case grid::POS_BOT_RIGHT:// bottom right quadrant
				rel_x = viewport.width / 2.;
				rel_y = viewport.height / 2.;
				window_out.width = viewport.width / 2.;
				window_out.height = viewport.height / 2.;
				break;
			default:
				ret = false;
				break;
			}
			break;
		case MODE_THREE_COL_S:
			switch (state.pos) {
			case grid::POS_TOP_LEFT:// top left col
				rel_x = 0;
				rel_y = 0;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_TOP_CENTER:// top center col
				rel_x = viewport.width / 3.;
				rel_y = 0;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_TOP_RIGHT:// top right col
				rel_x = 2 * viewport.width / 3.;
				rel_y = 0;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_LEFT:// left third
				rel_x = 0;
				rel_y = 0;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height;
				break;
			case grid::POS_CENTER:// center col
				rel_x = viewport.width / 3.;
				rel_y = 0;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height;
				break;
			case grid::POS_RIGHT:// right third
				rel_x = 2 * viewport.width / 3.;
				rel_y = 0;
				window_out.width = viewport.width / 3;
				window_out.height = viewport.height;
				break;
			case grid::POS_BOT_LEFT:// bottom left col
				rel_x = 0;
				rel_y = viewport.height / 2.;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_BOT_CENTER:// bottom center col
				rel_x = viewport.width / 3.;
				rel_y = viewport.height / 2.;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_BOT_RIGHT:// bottom right col
				rel_x = 2 * viewport.width / 3.;
				rel_y = viewport.height / 2.;
				window_out.width = viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			default:
				ret = false;
			}
			break;
		case MODE_THREE_COL_L:
			switch (state.pos) {
			case grid::POS_TOP_LEFT:// top left two cols
				rel_x = 0;
				rel_y = 0;
				window_out.width = 2 * viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_TOP_CENTER:// top half
				rel_x = 0;
				rel_y = 0;
				window_out.width = viewport.width;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_TOP_RIGHT:// top right two cols
				rel_x = viewport.width / 3.;
				rel_y = 0;
				window_out.width = 2 * viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_LEFT:// left two thirds
				rel_x = 0;
				rel_y = 0;
				window_out.width = 2 * viewport.width / 3.;
				window_out.height = viewport.height;
				break;
			case grid::POS_CENTER:// full screen
				rel_x = 0;
				rel_y = 0;
				window_out.width = viewport.width;
				window_out.height = viewport.height;
				break;
			case grid::POS_RIGHT:// right two thirds
				rel_x = viewport.width / 3.;
				rel_y = 0;
				window_out.width = 2 * viewport.width / 3.;
				window_out.height = viewport.height;
				break;
			case grid::POS_BOT_LEFT:// bottom left two cols
				rel_x = 0;
				rel_y = viewport.height / 2.;
				window_out.width = 2 * viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_BOT_CENTER:// bottom half
				rel_x = 0;
				rel_y = viewport.height / 2.;
				window_out.width = viewport.width;
				window_out.height = viewport.height / 2.;
				break;
			case grid::POS_BOT_RIGHT:// bottom right two cols
				rel_x = viewport.width / 3.;
				rel_y = viewport.height / 2.;
				window_out.width = 2 * viewport.width / 3.;
				window_out.height = viewport.height / 2.;
				break;
			default:
				ret = false;
			}
			break;
		default:
			ret = false;
		}
		if (ret) {
			//convert relative pos to absolute:
			window_out.x = rel_x + viewport.x;
			window_out.y = rel_y + viewport.y;
			DEBUG("pos=%s mode=%s -> %ldx %ldy %luw %luh",
					pos_str(state.pos), mode_str(state.mode),
					window_out.x, window_out.y,
					window_out.width, window_out.height);
		} else {
			ERROR("Bad pos=%s + mode=%s", pos_str(state.pos), mode_str(state.mode));
		}
		return ret;
	}

#define MAX(a,b) (((a) > (b)) ? (a) : (b))
	// whether two numbers are 'near' one another, according to a fudge factor.
	template <typename A, typename B>


@@ 254,223 49,407 @@ namespace {
		return ret;
	}
#define NEAR(a,b) _near(a, b, #a, #b)
}

	// given window's dimensions, estimate its state (or unknown+unknown)
	// (inverse of calculate_state)
	bool get_current_state(const Dimensions& window,
			const Dimensions& viewport, State& out) {
		//get window x/y relative to viewport x/y
		int rel_x = window.x - viewport.x,
			rel_y = window.y - viewport.y;
		out.pos = grid::POS_UNKNOWN;
		out.mode = MODE_UNKNOWN;
		if (NEAR(window.width, viewport.width / 2.)) {
			if (NEAR(window.height, viewport.height / 2.)) {
				if (NEAR(rel_x, 0)) {
					if (NEAR(rel_y, 0)) {
						// top left quadrant
						out.pos = grid::POS_TOP_LEFT;
						out.mode = MODE_TWO_COL;
					} else if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom left quadrant
						out.pos = grid::POS_BOT_LEFT;
						out.mode = 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.mode = MODE_TWO_COL;
					} else if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom right quadrant
						out.pos = grid::POS_BOT_RIGHT;
						out.mode = MODE_TWO_COL;
					}
/* given window's dimensions, estimate its state (or unknown+unknown)
   (inverse of StateToDim) */
bool PositionCalc::CurState(State& out) {
	//get window x/y relative to viewport x/y
	int rel_x = window.x - viewport.x,
		rel_y = window.y - viewport.y;
	out.pos = grid::POS_UNKNOWN;
	out.mode = grid::MODE_UNKNOWN;
	if (NEAR(window.width, viewport.width / 2.)) {
		if (NEAR(window.height, viewport.height / 2.)) {
			if (NEAR(rel_x, 0)) {
				if (NEAR(rel_y, 0)) {
					// top left quadrant
					out.pos = grid::POS_TOP_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.mode = grid::MODE_TWO_COL;
				}
			} else if (NEAR(window.height, viewport.height) &&
					NEAR(rel_y, 0)) {
				if (NEAR(rel_x, 0)) {
					// left half
					out.pos = grid::POS_LEFT;
					out.mode = MODE_TWO_COL;
				} else if (NEAR(rel_x, viewport.width / 2.)) {
					// right half
					out.pos = grid::POS_RIGHT;
					out.mode = 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.mode = grid::MODE_TWO_COL;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom right quadrant
					out.pos = grid::POS_BOT_RIGHT;
					out.mode = grid::MODE_TWO_COL;
				}
			}
		} else if (NEAR(window.width, viewport.width / 3.)) {
			if (NEAR(window.height, viewport.height / 2.)) {
				if (NEAR(rel_x, 0)) {
					if (NEAR(rel_y, 0)) {
						// top left col
						out.pos = grid::POS_TOP_LEFT;
						out.mode = MODE_THREE_COL_S;
					} if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom left col
						out.pos = grid::POS_BOT_LEFT;
						out.mode = 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.mode = MODE_THREE_COL_S;
					} else if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom center col
						out.pos = grid::POS_BOT_CENTER;
						out.mode = 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.mode = MODE_THREE_COL_S;
					} else if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom right col
						out.pos = grid::POS_BOT_RIGHT;
						out.mode = MODE_THREE_COL_S;
					}
				}
			} else if (NEAR(window.height, viewport.height) &&
					NEAR(rel_y, 0)) {
				if (NEAR(rel_x, 0)) {
					// left col
					out.pos = grid::POS_LEFT;
					out.mode = MODE_THREE_COL_S;
				} else if (NEAR(rel_x, viewport.width / 3.)) {
					// center col
					out.pos = grid::POS_CENTER;
					out.mode = MODE_THREE_COL_S;
				} else if (NEAR(rel_x, 2 * viewport.width / 3.)) {
					// right col
					out.pos = grid::POS_RIGHT;
					out.mode = MODE_THREE_COL_S;
				}
		} else if (NEAR(window.height, viewport.height) &&
				NEAR(rel_y, 0)) {
			if (NEAR(rel_x, 0)) {
				// left half
				out.pos = grid::POS_LEFT;
				out.mode = grid::MODE_TWO_COL;
			} else if (NEAR(rel_x, viewport.width / 2.)) {
				// right half
				out.pos = grid::POS_RIGHT;
				out.mode = grid::MODE_TWO_COL;
			}
		} else if (NEAR(window.width, 2 * viewport.width / 3.)) {
			if (NEAR(window.height, viewport.height / 2.)) {
				if (NEAR(rel_x, 0)) {
					if (NEAR(rel_y, 0)) {
						// top left two cols
						out.pos = grid::POS_TOP_LEFT;
						out.mode = MODE_THREE_COL_L;
					} else if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom left two cols
						out.pos = grid::POS_BOT_LEFT;
						out.mode = 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.mode = MODE_THREE_COL_L;
					} else if (NEAR(rel_y, viewport.height / 2.)) {
						// bottom right two cols
						out.pos = grid::POS_BOT_RIGHT;
						out.mode = MODE_THREE_COL_L;
					}
				}
			} else if (NEAR(window.height, viewport.height) &&
					NEAR(rel_y, 0)) {
				if (NEAR(rel_x, 0)) {
					// left two cols
					out.pos = grid::POS_LEFT;
					out.mode = MODE_THREE_COL_L;
				} else if (NEAR(rel_x, viewport.width / 3.)) {
					// right two cols
					out.pos = grid::POS_RIGHT;
					out.mode = MODE_THREE_COL_L;
		}
	} else if (NEAR(window.width, viewport.width / 3.)) {
		if (NEAR(window.height, viewport.height / 2.)) {
			if (NEAR(rel_x, 0)) {
				if (NEAR(rel_y, 0)) {
					// top left col
					out.pos = grid::POS_TOP_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.mode = grid::MODE_THREE_COL_S;
				}
			}
		} else if (NEAR(window.width, viewport.width) &&
				NEAR(rel_x, 0)) {
			if (NEAR(window.height, viewport.height / 2.)) {
			} else if (NEAR(rel_x, viewport.width / 3.)) {
				if (NEAR(rel_y, 0)) {
					// top half
					// top center col
					out.pos = grid::POS_TOP_CENTER;
					out.mode = MODE_THREE_COL_L;
					out.mode = grid::MODE_THREE_COL_S;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom half
					// bottom center col
					out.pos = grid::POS_BOT_CENTER;
					out.mode = MODE_THREE_COL_L;
					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.mode = grid::MODE_THREE_COL_S;
				} else if (NEAR(rel_y, viewport.height / 2.)) {
					// bottom right col
					out.pos = grid::POS_BOT_RIGHT;
					out.mode = grid::MODE_THREE_COL_S;
				}
			} else if (NEAR(window.height, viewport.height) &&
					NEAR(rel_y, 0)) {
				// full screen
			}
		} else if (NEAR(window.height, viewport.height) &&
				NEAR(rel_y, 0)) {
			if (NEAR(rel_x, 0)) {
				// left col
				out.pos = grid::POS_LEFT;
				out.mode = grid::MODE_THREE_COL_S;
			} else if (NEAR(rel_x, viewport.width / 3.)) {
				// center col
				out.pos = grid::POS_CENTER;
				out.mode = MODE_THREE_COL_L;
				out.mode = grid::MODE_THREE_COL_S;
			} else if (NEAR(rel_x, 2 * viewport.width / 3.)) {
				// right col
				out.pos = grid::POS_RIGHT;
				out.mode = grid::MODE_THREE_COL_S;
			}
		}

		DEBUG("%ldx %ldy %luw %luh -> pos=%s mode=%s",
				rel_x, rel_y, window.width, window.height,
				pos_str(out.pos), mode_str(out.mode));
		return true;
	} else if (NEAR(window.width, 2 * viewport.width / 3.)) {
		if (NEAR(window.height, viewport.height / 2.)) {
			if (NEAR(rel_x, 0)) {
				if (NEAR(rel_y, 0)) {
					// top left two cols
					out.pos = grid::POS_TOP_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.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.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.mode = grid::MODE_THREE_COL_L;
				}
			}
		} else if (NEAR(window.height, viewport.height) &&
				NEAR(rel_y, 0)) {
			if (NEAR(rel_x, 0)) {
				// left two cols
				out.pos = grid::POS_LEFT;
				out.mode = grid::MODE_THREE_COL_L;
			} else if (NEAR(rel_x, viewport.width / 3.)) {
				// right two cols
				out.pos = grid::POS_RIGHT;
				out.mode = grid::MODE_THREE_COL_L;
			}
		}
	} else if (NEAR(window.width, viewport.width) &&
			NEAR(rel_x, 0)) {
		if (NEAR(window.height, viewport.height / 2.)) {
			if (NEAR(rel_y, 0)) {
				// top half
				out.pos = grid::POS_TOP_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.mode = grid::MODE_THREE_COL_L;
			}
		} else if (NEAR(window.height, viewport.height) &&
				NEAR(rel_y, 0)) {
			// full screen
			out.pos = grid::POS_CENTER;
			out.mode = grid::MODE_THREE_COL_L;
		}
	}

	bool get_next_state(const State& cur, grid::POS req_pos, State& out) {
		if (req_pos == grid::POS_UNKNOWN) {
			ERROR("Position '%s' was requested. Internal error?", pos_str(req_pos));
			return false;// nice to have
		}
		if (cur.pos == req_pos) {
			// position is same, so rotate mode
			out.pos = cur.pos;
			switch (cur.pos) {
			case grid::POS_TOP_CENTER:
			case grid::POS_CENTER:
			case grid::POS_BOT_CENTER:
				// for center col: 3x2L -> 3x2S (-> 3x2L) (no 2x2)
				switch (cur.mode) {
				case MODE_THREE_COL_L:
					out.mode = MODE_THREE_COL_S;
					break;
				case MODE_THREE_COL_S:
				default:
					out.mode = MODE_THREE_COL_L;
				}
	DEBUG("%ldx %ldy %luw %luh -> pos=%s mode=%s",
			rel_x, rel_y, window.width, window.height,
			pos_str(out.pos), mode_str(out.mode));
	return true;
}

bool PositionCalc::NextState(const State& cur, grid::POS req_pos, State& out) {
	if (req_pos == grid::POS_UNKNOWN) {
		ERROR("Position '%s' was requested. Internal error?", pos_str(req_pos));
		return false;// nice to have
	}
	if (cur.pos == req_pos) {
		// position is same, so rotate mode
		out.pos = cur.pos;
		switch (cur.pos) {
		case grid::POS_TOP_CENTER:
		case grid::POS_CENTER:
		case grid::POS_BOT_CENTER:
			// for center col: 3x2L -> 3x2S (-> 3x2L) (no 2x2)
			switch (cur.mode) {
			case grid::MODE_THREE_COL_L:
				out.mode = grid::MODE_THREE_COL_S;
				break;
			case grid::MODE_THREE_COL_S:
			default:
				// for everything else: 2x2 -> 3x2L -> 3x2S (-> 2x2)
				switch (cur.mode) {
				case MODE_UNKNOWN:
				case MODE_THREE_COL_L:
					out.mode = MODE_THREE_COL_S;
					break;
				case MODE_TWO_COL:
					out.mode = MODE_THREE_COL_L;
					break;
				case MODE_THREE_COL_S:
				default:
					out.mode = MODE_TWO_COL;
				}
				out.mode = grid::MODE_THREE_COL_L;
			}
		} else {
			// new position, so start with initial mode
			out.pos = req_pos;
			switch (req_pos) {
			case grid::POS_TOP_CENTER:
			case grid::POS_CENTER:
			case grid::POS_BOT_CENTER:
				// for center col: start with 3x2L
				out.mode = MODE_THREE_COL_L;
			break;
		default:
			// for everything else: 2x2 -> 3x2L -> 3x2S (-> 2x2)
			switch (cur.mode) {
			case grid::MODE_UNKNOWN:
			case grid::MODE_THREE_COL_L:
				out.mode = grid::MODE_THREE_COL_S;
				break;
			case grid::MODE_TWO_COL:
				out.mode = grid::MODE_THREE_COL_L;
				break;
			case grid::MODE_THREE_COL_S:
			default:
				// for everything else: start with 2x2
				out.mode = MODE_TWO_COL;
				out.mode = grid::MODE_TWO_COL;
			}
		}
		DEBUG("curpos=%s curmode=%s + reqpos=%s -> pos=%s mode=%s",
				pos_str(cur.pos), mode_str(cur.mode), pos_str(req_pos),
				pos_str(out.pos), mode_str(out.mode));
		return true;
	} else {
		// new position, so start with initial mode
		out.pos = req_pos;
		switch (req_pos) {
		case grid::POS_TOP_CENTER:
		case grid::POS_CENTER:
		case grid::POS_BOT_CENTER:
			// for center col: start with 3x2L
			out.mode = grid::MODE_THREE_COL_L;
			break;
		default:
			// for everything else: start with 2x2
			out.mode = grid::MODE_TWO_COL;
		}
	}
	DEBUG("curpos=%s curmode=%s + reqpos=%s -> pos=%s mode=%s",
			pos_str(cur.pos), mode_str(cur.mode), pos_str(req_pos),
			pos_str(out.pos), mode_str(out.mode));
	return true;
}

bool PositionCalc::NextPos(grid::POS request, Dimensions& out) {
	State cur_state, next_state;
	return get_current_state(window, viewport, cur_state) &&// window + viewport -> cur_state
		get_next_state(cur_state, request, next_state) &&// cur_state + request -> next_state
		calculate_state(viewport, next_state, out);// next_state + viewport -> next_dim
/* given window's state, calculate its dimensions.
   could have some kind of fancy autogeneration here,
   but there's a very finite number of possible positions (for now?) */
bool PositionCalc::StateToDim(const State& state, Dimensions& out) {
	bool ret = true;
	long rel_x = 0, rel_y = 0;//coordinates relative to viewport
	switch (state.mode) {
	case grid::MODE_TWO_COL:
		switch (state.pos) {
		case grid::POS_TOP_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
			return false;
		case grid::POS_TOP_RIGHT:// top right quadrant
			rel_x = viewport.width / 2.;
			rel_y = 0;
			out.width = viewport.width / 2.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_LEFT:// left half
			rel_x = 0;
			rel_y = 0;
			out.width = viewport.width / 2.;
			out.height = viewport.height;
			break;
		case grid::POS_CENTER:// invalid, use mode THREE_COL_S/L
			return false;
		case grid::POS_RIGHT:// right half
			rel_x = viewport.width / 2.;
			rel_y = 0;
			out.width = viewport.width / 2.;
			out.height = viewport.height;
			break;
		case grid::POS_BOT_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
			return false;
		case grid::POS_BOT_RIGHT:// bottom right quadrant
			rel_x = viewport.width / 2.;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 2.;
			out.height = viewport.height / 2.;
			break;
		default:
			ret = false;
			break;
		}
		break;
	case grid::MODE_THREE_COL_S:
		switch (state.pos) {
		case grid::POS_TOP_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
			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
			rel_x = 2 * viewport.width / 3.;
			rel_y = 0;
			out.width = viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_LEFT:// left third
			rel_x = 0;
			rel_y = 0;
			out.width = viewport.width / 3.;
			out.height = viewport.height;
			break;
		case grid::POS_CENTER:// center col
			rel_x = viewport.width / 3.;
			rel_y = 0;
			out.width = viewport.width / 3.;
			out.height = viewport.height;
			break;
		case grid::POS_RIGHT:// right third
			rel_x = 2 * viewport.width / 3.;
			rel_y = 0;
			out.width = viewport.width / 3;
			out.height = viewport.height;
			break;
		case grid::POS_BOT_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
			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
			rel_x = 2 * viewport.width / 3.;
			rel_y = viewport.height / 2.;
			out.width = viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		default:
			ret = false;
		}
		break;
	case grid::MODE_THREE_COL_L:
		switch (state.pos) {
		case grid::POS_TOP_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
			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
			rel_x = viewport.width / 3.;
			rel_y = 0;
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		case grid::POS_LEFT:// left two thirds
			rel_x = 0;
			rel_y = 0;
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height;
			break;
		case grid::POS_CENTER:// full screen
			rel_x = 0;
			rel_y = 0;
			out.width = viewport.width;
			out.height = viewport.height;
			break;
		case grid::POS_RIGHT:// right two thirds
			rel_x = viewport.width / 3.;
			rel_y = 0;
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height;
			break;
		case grid::POS_BOT_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
			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
			rel_x = viewport.width / 3.;
			rel_y = viewport.height / 2.;
			out.width = 2 * viewport.width / 3.;
			out.height = viewport.height / 2.;
			break;
		default:
			ret = false;
		}
		break;
	default:
		ret = false;
	}
	if (ret) {
		//convert relative pos to absolute:
		out.x = rel_x + viewport.x;
		out.y = rel_y + viewport.y;
		DEBUG("pos=%s mode=%s -> %ldx %ldy %luw %luh",
				pos_str(state.pos), mode_str(state.mode),
				out.x, out.y, out.width, out.height);
	} else {
		ERROR("Bad pos=%s + mode=%s", pos_str(state.pos), mode_str(state.mode));
	}
	return ret;
}

M src/position.h => src/position.h +26 -1
@@ 22,13 22,38 @@
#include "pos.h"
#include "dimensions.h"

namespace grid {
	enum MODE {
		MODE_UNKNOWN,
		MODE_TWO_COL,// 2x2 (not applicable for center col positions)
		MODE_THREE_COL_S,// 3x2 small: each position filling 1 column
		MODE_THREE_COL_L// 3x2 large: sides filling 2 columns and center filling full width
	};
}

struct State {
	grid::POS pos;
	grid::MODE mode;
};

class PositionCalc {
 public:
	PositionCalc(const Dimensions& viewport,
		 const Dimensions& window)
	 : viewport(viewport), window(window) { }

	bool NextPos(grid::POS request, Dimensions& out);
	/* Produces an autodetected state of this window using its current
	 * coordinates. Returns true on success, else false. */
	bool CurState(State& cur_state);

	/* Given a current state and requested position for the window, calculates
	 * its next state. Returns true on success, else false. */
	bool NextState(const State& cur_state, grid::POS req_pos,
			State& next_state);

	/* Given a state for the window, calculates the dimensions of that position.
	 * Returns true on success, else false. */
	bool StateToDim(const State& state, Dimensions& out);

 private:
	const Dimensions viewport, window;

M src/window.cpp => src/window.cpp +28 -12
@@ 81,7 81,7 @@ namespace {

			if (!(types = (Atom*)x11_util::get_property(disp, win,
									XA_ATOM, "_NET_WM_WINDOW_TYPE", &count))) {
				ERROR_DIR("couldnt get window types");
				ERROR_DIR("couldn't get window types");
				//assume window types are allowed, keep going
			} else {
				for (size_t i = 0; i < count; ++i) {


@@ 110,7 110,7 @@ namespace {

			if (!(states = (Atom*)x11_util::get_property(disp, win,
									XA_ATOM, "_NET_WM_STATE", &count))) {
				ERROR_DIR("couldnt get window states");
				ERROR_DIR("couldn't get window states");
				//assume window states are allowed, keep going
			} else {
				bool has_skip_pager = false, has_skip_taskbar = false;


@@ 228,25 228,30 @@ namespace {
						XInternAtom(disp, "_NET_WM_STATE_SHADED", False),
						XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False),
						SOURCE_INDICATION, 0)) {
			ERROR_DIR("couldnt unshade/defullscreen");
			ERROR_DIR("couldn't unshade/defullscreen");
			return false;
		}
		return true;
	}

	bool demaximize_window(Display* disp, Window win) {
	bool maximize_window(Display* disp, Window win, bool maximize) {
		/*
		  this disagrees with docs, which say that we should be using a
		  _NET_WM_STATE_DISABLE atom in data[0]. but that apparently doesn't
		  work in practice, but '0' does. (and '1' works for ENABLE)
		  _NET_WM_STATE_DISABLE/_ENABLE atom in data[0]. That apparently doesn't
		  work in practice, but '0'/'1' do.
		*/

		int val = (maximize) ? 1 : 0;//just to be explicit
		if (!client_msg(disp, win, "_NET_WM_STATE",
						0,//1 = enable state(s), 0 = disable state(s)
						val,//1 = enable state(s), 0 = disable state(s)
						XInternAtom(disp, "_NET_WM_STATE_MAXIMIZED_VERT", False),
						XInternAtom(disp, "_NET_WM_STATE_MAXIMIZED_HORZ", False),
						SOURCE_INDICATION, 0)) {
			ERROR_DIR("couldnt demaximize");
			if (maximize) {
				ERROR_DIR("couldn't maximize");
			} else {
				ERROR_DIR("couldn't demaximize");
			}
			return false;
		}



@@ 300,7 305,7 @@ bool ActiveWindow::Sizes(Dimensions& viewport, Dimensions& activewin) const {
	}

	if (!get_window_size(disp, *win, &activewin, NULL, NULL)) {
		ERROR_DIR("couldnt get window size");
		ERROR_DIR("couldn't get window size");
		return false;
	}



@@ 333,7 338,7 @@ bool ActiveWindow::MoveResize(const Dimensions& activewin) {
	}

	//demaximize the window before attempting to move it
	demaximize_window(disp, *win);//disregard failure
	maximize_window(disp, *win, false);//disregard failure

	unsigned long new_interior_width = activewin.width - margin_width,
		new_interior_height = activewin.height - margin_height;


@@ 344,6 349,17 @@ bool ActiveWindow::MoveResize(const Dimensions& activewin) {
			margin_width, margin_height,
			activewin.x, activewin.y, new_interior_width, new_interior_height);

	return XMoveResizeWindow(disp, *win, activewin.x, activewin.y,
			new_interior_width, new_interior_height) == 0;
	if (XMoveResizeWindow(disp, *win, activewin.x, activewin.y,
					new_interior_width, new_interior_height) == 0) {
		ERROR("MoveResize to %ldx %ldy %luw %luh failed.",
				activewin.x, activewin.y, new_interior_width, new_interior_height);
		return false;
	}
	return true;
}

bool ActiveWindow::Maximize() {
	CHECK_STATE();

	return maximize_window(disp, *win, true);
}

M src/window.h => src/window.h +1 -0
@@ 36,6 36,7 @@ class ActiveWindow {

	bool Sizes(Dimensions& viewport, Dimensions& activewin) const;
	bool MoveResize(const Dimensions& activewin);
	bool Maximize();

 private:
	Display* disp;