~nickbp/gridmgr

61741257071f1f14475288d0d2bb8c0e272076a0 — Nick Parker 10 years ago 64f1345
Add commands for window selection and cross-monitor movement.

- New command set for selecting windows adjacent to the active window.
- New command set for moving windows to adjacent monitors.
- The usual cleanup and simplification of current code.
M src/CMakeLists.txt => src/CMakeLists.txt +4 -8
@@ 23,18 23,14 @@ if(CMAKE_COMPILER_IS_GNUCXX)
endif()

SET(SRCS
  config.in.h
  config.cpp
  grid.h
  grid.cpp
  main.cpp
  position.h
  neighbor.cpp
  position.cpp
  viewport.h
  viewport-ewmh.cpp
  window.h
  viewport.cpp
  viewport-imp-ewmh.cpp
  window.cpp
  x11-util.h
  x11-util.cpp
  )
configure_file (


@@ 54,7 50,7 @@ if(USE_XINERAMA)
  message(STATUS "Xinerama multi-monitor support enabled.")
  list(APPEND INCLUDES "${X11_Xinerama_INCLUDE_PATH}")
  list(APPEND LIBS "${X11_Xinerama_LIB}")
  list(APPEND SRCS viewport-xinerama.cpp)
  list(APPEND SRCS viewport-imp-xinerama.cpp)

else()


M src/config.cpp => src/config.cpp +1 -1
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by

M src/config.in.h => src/config.in.h +5 -3
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 57,12 57,14 @@ namespace config {
	static const char VERSION_STRING[] = "@gridmgr_VERSION_MAJOR@.@gridmgr_VERSION_MINOR@.@gridmgr_VERSION_PATCH@";
	static const char BUILD_DATE[] = __TIMESTAMP__;

	extern bool debug_enabled;
	/* DONT USE THESE, use DEBUG()/LOG()/ERROR() instead: */

	extern FILE *fout;
	extern FILE *ferr;

	/* DONT USE THESE, use DEBUG()/LOG()/ERROR() instead. */
	extern bool debug_enabled;
	void _debug(const char* format, ...);

	void _log(const char* format, ...);
	void _error(const char* format, ...);
}

M src/grid.cpp => src/grid.cpp +53 -22
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 16,43 16,74 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"
#include "grid.h"

#include "position.h"
#include "viewport.h"
#include "window.h"

#include "config.h"
bool grid::set_window(DIR window) {
	return window::select_activate(window);
}

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

	//get current window's dimensions
	Dimensions viewport, cur_dim;
	if (!win.Sizes(viewport, cur_dim)) {
	// get current window's dimensions
	Dimensions cur_window;
	if (!win.Size(cur_window)) {
		return false;
	}

	//using the requested position and current dimensions,
	//calculate and set new dimensions
	PositionCalc calc(viewport, cur_dim);
	/* special case: if the active window is fullscreen, the desktop workarea is
	   wrong (ie the obscured taskbar extents aren't included). get around this
	   by unfullscreening the window first (if applicable). but return the
	   window's dimensions from when it was fullscreened/shaded. */
	win.DeFullscreen();// disregard failure

	Dimensions cur_viewport, next_viewport;
	{
		ViewportCalc vcalc(cur_window);
		// cur_window + monitor -> cur_viewport + next_viewport
		if (!vcalc.Viewports(monitor, cur_viewport, next_viewport)) {
			return false;
		}
	}

	/* using the requested position and current dimensions,
	   calculate and set new dimensions */
	PositionCalc pcalc(cur_window);

	State cur_state, next_state;
	if (!calc.CurState(cur_state) ||
			!calc.NextState(cur_state, pos, next_state)) {
	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)) {
		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;
	Dimensions next_dim;
	if (next_state.pos == POS_UNKNOWN && next_state.mode == MODE_UNKNOWN) {
		// window doesnt currently have a state, and user didn't specify one
		pcalc.ViewportToDim(cur_viewport, next_viewport, next_dim);
	} else {
		// next_viewport + next_state -> next_dim
		if (!pcalc.StateToDim(next_viewport, next_state, next_dim)) {
			return false;
		}
		ERROR_DIR("Maximizing window failed, falling back to filling screen.");
	}

	Dimensions next_dim;
	return calc.StateToDim(next_state, next_dim) &&
		win.MoveResize(next_dim);
	// move the window to next_dim
	if (!win.DeShade() || !win.MoveResize(next_dim)) {
		return false;
	}

	if (next_state.pos == POS_CENTER &&
		next_state.mode == MODE_THREE_COL_L) {
		/* if we're going to be filling the screen anyway, just maximize
		   (do this after MoveResize so that viewport changes are respected) */
		win.Maximize();// disregard failure
	}
	return true;
}

M src/grid.h => src/grid.h +8 -4
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 22,10 22,14 @@
#include "pos.h"

namespace grid {
	/* Selects and makes active the window in the specified direction relative
	 * to the currently active window. */
	bool set_window(DIR window);

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

#endif

M src/main.cpp => src/main.cpp +131 -39
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 17,6 17,7 @@
*/

#include <getopt.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>


@@ 26,16 27,46 @@

#define TIMESTR_MAX 128 // arbitrarily large

void syntax(char* appname) {
static void syntax(char* appname) {
	ERROR_RAWDIR("");
	ERROR_RAWDIR("gridmgr v%s (built %s)",
		  config::VERSION_STRING,
		  config::BUILD_DATE);
	ERROR_RAWDIR("Moves/sizes windows to match 2x2/3x2 grid layouts.");
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Usage: %s [options] <position>", appname);
	ERROR_RAWDIR("Performs one or more of the following, in this order:");
	ERROR_RAWDIR("- Activate a window adjacent to the current window.");
#ifdef USE_XINERAMA
	ERROR_RAWDIR("- Move active window to an adjacent monitor.");
#endif
	ERROR_RAWDIR("- Position active window on a grid layout.");
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Positions:");
#ifdef USE_XINERAMA
	ERROR_RAWDIR("Usage: %s [options] <window/monitor/position> [w/m/p] [w/m/p]", appname);
#else
	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("          -------           ");
#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("          -------           ");
#endif
	ERROR_RAWDIR("");
	ERROR_RAWDIR("Positions (position window on grid):");
	ERROR_RAWDIR("  -----------------------------  ");
	ERROR_RAWDIR(" | topleft |  top   | topright | ");
	ERROR_RAWDIR(" | ------- + ------ + -------- | ");


@@ 51,13 82,69 @@ 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;
	} else if (strcmp(arg, "left") == 0) {
		out = grid::POS_LEFT;
	} else if (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;
}

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;
	}
	return true;
}

namespace {
	enum CMD { CMD_UNKNOWN, CMD_HELP, CMD_POSITION };
	CMD run_cmd = CMD_UNKNOWN;
	grid::POS position = grid::POS_UNKNOWN;
	grid::POS position = grid::POS_CURRENT;
	grid::DIR monitor = grid::DIR_CURRENT, window = grid::DIR_CURRENT;
}

bool parse_config(int argc, char* argv[]) {
static bool parse_config(int argc, char* argv[]) {
	if (argc == 1) {
		syntax(argv[0]);
		return false;


@@ 82,32 169,32 @@ bool parse_config(int argc, char* argv[]) {
			//getopt refuses to continue, so handle position manually:
			for (int i = optind; i < argc; ++i) {
				const char* arg = argv[i];
				//debug("%d %d %s", argc, i, arg);
				if (strcmp(arg, "topleft") == 0) {
					position = grid::POS_TOP_LEFT;
				} else if (strcmp(arg, "top") == 0) {
					position = grid::POS_TOP_CENTER;
				} else if (strcmp(arg, "topright") == 0) {
					position = grid::POS_TOP_RIGHT;
				} else if (strcmp(arg, "left") == 0) {
					position = grid::POS_LEFT;
				} else if (strcmp(arg, "center") == 0) {
					position = grid::POS_CENTER;
				} else if (strcmp(arg, "right") == 0) {
					position = grid::POS_RIGHT;
				} else if (strcmp(arg, "botleft") == 0) {
					position = grid::POS_BOT_LEFT;
				} else if (strcmp(arg, "bottom") == 0 || strcmp(arg, "bot") == 0) {
					position = grid::POS_BOT_CENTER;
				} else if (strcmp(arg, "botright") == 0) {
					position = grid::POS_BOT_RIGHT;
				//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) {
						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) {
						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) {
						ERROR("%s: Multiple monitors specified: '%s'", argv[0], argv[i]);
						syntax(argv[0]);
						return false;
					}
					monitor = tmp_dir;
				} else {
					ERROR("%s: unknown argument: '%s'", argv[0], argv[i]);
					syntax(argv[0]);
					return false;
				}
				if (run_cmd != CMD_UNKNOWN) {
					ERROR("%s: misplaced argument: '%s'", argv[0], argv[i]);
					ERROR("%s: Unknown argument: '%s'", argv[0], argv[i]);
					syntax(argv[0]);
					return false;
				}


@@ 163,21 250,26 @@ bool parse_config(int argc, char* argv[]) {

int main(int argc, char* argv[]) {
	if (!parse_config(argc, argv)) {
		return 1;
		return EXIT_FAILURE;
	}
	switch (run_cmd) {
	case CMD_HELP:
		syntax(argv[0]);
		return 0;
		return EXIT_SUCCESS;
	case CMD_POSITION:
		if (position == grid::POS_UNKNOWN) {
			ERROR_DIR("INTERNAL ERROR: position command, but position not set!");
			return 1;
		// activate window (if specified)
		if (window != grid::DIR_CURRENT && !grid::set_window(window)) {
			return EXIT_FAILURE;
		}
		// move window (if specified)
		if (position != grid::POS_CURRENT || monitor != grid::DIR_CURRENT) {
			return grid::set_position(position, monitor) ?
				EXIT_SUCCESS : EXIT_FAILURE;
		}
		return grid::set_position(position) ? 0 : 1;
		return EXIT_SUCCESS;
	default:
		ERROR("%s: no command specified", argv[0]);
		syntax(argv[0]);
		return 1;
		return EXIT_FAILURE;
	}
}

A src/neighbor.cpp => src/neighbor.cpp +207 -0
@@ 0,0 1,207 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <math.h>

#include "config.h"
#include "neighbor.h"

#define MIDPOINT(min, size) ((size / 2.) + min)
#define DISTANCE(a,b) ((a > b) ? (a - b) : (b - a))

namespace {
	class point {
	public:
		point() : x(0), y(0) { }
		point(const Dimensions& d)
			: x(MIDPOINT(d.x, d.width)),
			  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 {
			// note: M_PI_4 == pi/4, from math.h.
			if (y == p.y && x == p.x) { return false; } // p is on top of us
			//DEBUG("%s: %f vs %f", grid::dir_str(dir), abs_atan(p), (M_PI_4));
			switch (dir) {
			case grid::DIR_UP:
				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:
				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:
				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:
				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:
				break;
			}
			return false;//???
		}

		double distance(const point& p) const {
			long w = p.x - x, h = p.y - y;
			return sqrt((w * w) + (h * h));
		}

		void shift_pos(grid::DIR dir, long max_x, long max_y) {
			switch (dir) {
			case grid::DIR_UP:
				// 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);
				break;

			case grid::DIR_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);
				break;

			case grid::DIR_UNKNOWN:
			case grid::DIR_CURRENT:
				break;//???
			}
		}

		long x;
		long y;

	private:
		inline double abs_atan(const point& p2) const {
			long denom = DISTANCE(x, p2.x);
			if (denom == 0) {
				return M_PI_2;// avoid div0 (pi/2, from math.h)
			} else {
				return atan(DISTANCE(y, p2.y) / (double)denom);
			}
		}
	};

	/* 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,
			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);
		for (size_t i = 0; i < pts.size(); ++i) {
			if (i == active) {
				DEBUG("skip: %ld,%ld", pts[i].x, pts[i].y);
				continue;
			}

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

		if (nearest_i >= 0) {
			// found!
			select = nearest_i;
			return true;
		}
		return false;
	}

	/* 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) {
		// 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:
			break;
		}
		return grid::DIR_UP;//???
	}
}

#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) {
	if (all.size() <= 1) {
		select = 0;
		return;
	}
	if (dir == grid::DIR_CURRENT) {
		select = active;
		return;
	}

	// dimension -> center point and max window dimensions
	size_t bound_x = MAX_X(all[0]), bound_y = MAX_Y(all[0]);
	std::vector<point> pts;
	pts.reserve(all.size());
	for (dim_list_t::const_iterator iter = all.begin();
		 iter != all.end(); ++iter) {
		const Dimensions& d = *iter;
		size_t d_x = MAX_X(d), d_y = MAX_Y(d);
		if (d_x > bound_x) { bound_x = d_x; }
		if (d_y > bound_y) { bound_y = d_y; }
		pts.push_back(point(d));
	}

	for (size_t i = 0; i < 4; ++i) {//try each of the four directions
		if (select_nearest_in_direction(dir, pts, active, select)) {
			return;
		}
		// not found. try shifting active pt for a wraparound search
		pts[active].shift_pos(dir, bound_x, bound_y);
		if (select_nearest_in_direction(dir, pts, active, select)) {
			return;
		}
		// still not found. undo shift and try next direction
		pts[active].shift_pos(dir, -1 * bound_x, -1 * bound_y);
		dir = fallback_direction(dir);
	}

	// STILL not found. just give up!
	select = active;
}

A src/neighbor.h => src/neighbor.h +33 -0
@@ 0,0 1,33 @@
#ifndef GRIDMGR_NEIGHBOR_H
#define GRIDMGR_NEIGHBOR_H

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <vector>

#include "dimensions.h"
#include "pos.h"

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);
}

#endif

M src/pos.h => src/pos.h +30 -5
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 21,9 21,9 @@

namespace grid {
	/* These are the various available positions which may be set.
	   UNKNOWN is considered an error state and should not be requested. */
	 * CURRENT is "use the current position", useful when only switching monitors */
	enum POS {
		POS_UNKNOWN,
		POS_UNKNOWN, POS_CURRENT,
		POS_TOP_LEFT, POS_TOP_CENTER, POS_TOP_RIGHT,
		POS_LEFT, POS_CENTER, POS_RIGHT,
		POS_BOT_LEFT, POS_BOT_CENTER, POS_BOT_RIGHT


@@ 33,6 33,8 @@ namespace grid {
		switch (pos) {
		case POS_UNKNOWN:
			return "UNKNOWN";
		case POS_CURRENT:
			return "CURRENT";
		case POS_TOP_LEFT:
			return "TOP_LEFT";
		case POS_TOP_CENTER:


@@ 51,8 53,31 @@ namespace grid {
			return "BOT_CENTER";
		case POS_BOT_RIGHT:
			return "BOT_RIGHT";
		default:
			break;
		}
		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";
		}
		return "???";
	}

M src/position.cpp => src/position.cpp +30 -8
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 16,8 16,10 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "position.h"
#include <math.h> // round()

#include "config.h"
#include "position.h"

namespace {
	inline const char* mode_str(grid::MODE mode) {


@@ 53,7 55,7 @@ namespace {

/* given window's dimensions, estimate its state (or unknown+unknown)
   (inverse of StateToDim) */
bool PositionCalc::CurState(State& out) {
bool PositionCalc::CurState(const Dimensions& viewport, State& out) const {
	//get window x/y relative to viewport x/y
	int rel_x = window.x - viewport.x,
		rel_y = window.y - viewport.y;


@@ 204,12 206,14 @@ bool PositionCalc::CurState(State& out) {
	return true;
}

bool PositionCalc::NextState(const State& cur, grid::POS req_pos, State& out) {
	if (req_pos == grid::POS_UNKNOWN) {
bool PositionCalc::NextState(const State& cur, grid::POS req_pos, State& out) const {
	if (req_pos == grid::POS_UNKNOWN) {// nice to have
		ERROR("Position '%s' was requested. Internal error?", pos_str(req_pos));
		return false;// nice to have
		return false;
	}
	if (cur.pos == req_pos) {
	if (req_pos == grid::POS_CURRENT) {
		out = cur;
	} else if (cur.pos == req_pos) {
		// position is same, so rotate mode
		out.pos = cur.pos;
		switch (cur.pos) {


@@ 265,7 269,8 @@ bool PositionCalc::NextState(const State& cur, grid::POS req_pos, State& out) {
/* 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 PositionCalc::StateToDim(const Dimensions& viewport, const State& state,
		Dimensions& out) const {
	bool ret = true;
	long rel_x = 0, rel_y = 0;//coordinates relative to viewport
	switch (state.mode) {


@@ 453,3 458,20 @@ bool PositionCalc::StateToDim(const State& state, Dimensions& out) {
	}
	return ret;
}

void PositionCalc::ViewportToDim(const Dimensions& cur_viewport,
		const Dimensions& next_viewport, Dimensions& out) const {
	// just do an exact scaling to the new viewport
	if (cur_viewport.width == 0 || cur_viewport.height == 0) {//nice to have, avoid div0
		out = next_viewport;//just throw something together and get out
		return;
	}
	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:
	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);
	out.height = round(window.height * ratio_y);
}

M src/position.h => src/position.h +18 -10
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 32,31 32,39 @@ namespace grid {
}

struct State {
	State() : pos(grid::POS_UNKNOWN), mode(grid::MODE_UNKNOWN) { }

	grid::POS pos;
	grid::MODE mode;
};

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

	/* Produces an autodetected state of this window using its current
	 * coordinates. Returns true on success, else false. */
	bool CurState(State& cur_state);
	bool CurState(const Dimensions& viewport, State& cur_state) const;

	/* 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);
			State& next_state) const;

	/* 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);
	bool StateToDim(const Dimensions& viewport, const State& state,
			Dimensions& out) const;

	/* Special case of StateToDim: Given the current viewport and next viewport
	 * for a window with no autodetected state, calculates a reasonable location
	 * in the new viewport. */
	void ViewportToDim(const Dimensions& cur_viewport,
			const Dimensions& next_viewport, Dimensions& out) const;

 private:
	const Dimensions viewport, window;
private:
	const Dimensions window;
};

#endif

R src/viewport-ewmh.cpp => src/viewport-imp-ewmh.cpp +20 -11
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 16,18 16,19 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "viewport.h"
#include "x11-util.h"
#include "config.h"
#include "viewport-imp-ewmh.h"
#include "x11-util.h"

bool viewport::get_viewport_ewmh(Display* disp,
		Dimensions& viewport_out) {
bool viewport::ewmh::get_viewports(Display* disp, const Dimensions& /*activewin*/,
		dim_list_t& viewports_out, size_t& active_out) {
	//get current workspace
	unsigned long cur_workspace;
	{
		unsigned long* cur_ptr;
		static Atom curdesk_msg = XInternAtom(disp, "_NET_CURRENT_DESKTOP", False);
		if (!(cur_ptr = (unsigned long *)x11_util::get_property(disp, DefaultRootWindow(disp),
								XA_CARDINAL, "_NET_CURRENT_DESKTOP", NULL))) {
								XA_CARDINAL, curdesk_msg, NULL))) {
			ERROR_DIR("unable to retrieve current desktop");
			return false;
		}


@@ 37,8 38,9 @@ bool viewport::get_viewport_ewmh(Display* disp,

	unsigned long* area;
	size_t area_count = 0;//number of areas returned, one per workspace. each area contains 4 ulongs.
	static Atom workarea_msg = XInternAtom(disp, "_NET_WORKAREA", False);
	if (!(area = (unsigned long*)x11_util::get_property(disp, DefaultRootWindow(disp),
							XA_CARDINAL, "_NET_WORKAREA", &area_count))) {
							XA_CARDINAL, workarea_msg, &area_count))) {
		ERROR_DIR("unable to retrieve spanning workarea");
		return false;
	}


@@ 53,6 55,7 @@ bool viewport::get_viewport_ewmh(Display* disp,
		x11_util::free_property(area);
		return false;
	}

	if (config::debug_enabled) {
		for (size_t i = 0; i < area_count/4; ++i) {
			if (i == cur_workspace) {


@@ 68,10 71,16 @@ bool viewport::get_viewport_ewmh(Display* disp,
	}

	//set current workspace as viewport
	viewport_out.x = area[cur_workspace*4];
	viewport_out.y = area[(cur_workspace*4)+1];
	viewport_out.width = area[(cur_workspace*4)+2];
	viewport_out.height = area[(cur_workspace*4)+3];
	viewports_out.clear();//nice to have
	viewports_out.push_back(Dimensions());
	active_out = 0;

	Dimensions& v = viewports_out.back();
	v.x = area[cur_workspace*4];
	v.y = area[(cur_workspace*4)+1];
	v.width = area[(cur_workspace*4)+2];
	v.height = area[(cur_workspace*4)+3];

	x11_util::free_property(area);
	return true;
}

A src/viewport-imp-ewmh.h => src/viewport-imp-ewmh.h +36 -0
@@ 0,0 1,36 @@
#ifndef GRIDMGR_VIEWPORT_IMP_EWMH_H
#define GRIDMGR_VIEWPORT_IMP_EWMH_H

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <vector>
#include <X11/Xlib.h>

#include "dimensions.h"

typedef std::vector<Dimensions> dim_list_t;

namespace viewport {
	namespace ewmh {
		bool get_viewports(Display* disp, const Dimensions& activewin,
				dim_list_t& viewports_out, size_t& active_out);
	}
}

#endif

A src/viewport-imp-xinerama.cpp => src/viewport-imp-xinerama.cpp +241 -0
@@ 0,0 1,241 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <X11/extensions/Xinerama.h>

#include "config.h"
#include "viewport-imp-xinerama.h"
#include "x11-util.h"

#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))

static int INTERSECTION(int a1, int a2, int b1, int b2) {
	int ret = (a2 < b1 || b2 < a1) ? 0 :
		(a1 >= b1) ?
		((a2 >= b2) ? (b2 - a1) : (a2 - a1)) :
		((a2 >= b2) ? (b2 - b1) : (a2 - b1));
	DEBUG("%d-%d x %d-%d = %d", a1, a2, b1, b2, ret);
	return ret;
}

namespace {
	bool get_screens(Display* disp, const Dimensions& activewin,
			Dimensions& bounding_box, dim_list_t& viewports,
			size_t& active_viewport) {
		int screen_count = 0;
		XineramaScreenInfo* screens = XineramaQueryScreens(disp, &screen_count);
		if (screens == NULL || screen_count == 0) {
			DEBUG_DIR("xinerama not loaded or unavailable");
			if (screens != NULL) {
				XFree(screens);
			}
			return false;
		}

		//initialize bounding box to something
		bounding_box.x = screens[0].x_org;
		bounding_box.y = screens[0].y_org;
		int bound_max_x = screens[0].x_org + screens[0].width,
			bound_max_y = screens[0].y_org + screens[0].height;

		// search for largest overlap between active window and xinerama screen.
		// the screen with the most overlap is the 'active screen'
		int active_overlap = 0;

		for (int i = 0; i < screen_count; ++i) {
			const XineramaScreenInfo& screen = screens[i];

			// grow bounding box
			bounding_box.x = MIN(bounding_box.x, screen.x_org);
			bounding_box.y = MIN(bounding_box.y, screen.y_org);
			bound_max_x = MAX(bound_max_x, screen.x_org + screen.width);
			bound_max_y = MAX(bound_max_y, screen.y_org + screen.height);

			// add viewport
			viewports.push_back(Dimensions());
			Dimensions& v = viewports.back();
			v.x = screen.x_org;
			v.y = screen.y_org;
			v.width = screen.width;
			v.height = screen.height;

			// check active overlap
			int overlap =
				INTERSECTION(screen.x_org, screen.x_org + screen.width,
						activewin.x, activewin.x + activewin.width) *
				INTERSECTION(screen.y_org, screen.y_org + screen.height,
						activewin.y, activewin.y + activewin.height);

			DEBUG("screen %d of %d: %dx %dy %dw %dh (overlap %d)",
					i+1, screen_count,
					screen.x_org, screen.y_org, screen.width, screen.height,
					overlap);

			if (overlap > active_overlap) {
				// overlap is bigger, update active counters
				active_overlap = overlap;
				active_viewport = i;
			}
		};

		DEBUG("active screen is %lu of %d", active_viewport+1, screen_count);

		bounding_box.width = bound_max_x - bounding_box.x;
		bounding_box.height = bound_max_y - bounding_box.y;
		DEBUG("desktop bounding box: %ldx %ldy %ldw %ldh",
				bounding_box.x, bounding_box.y,
				bounding_box.width, bounding_box.height);

		XFree(screens);
		return true;
	}

	struct strut {
		enum TYPE {
			LEFT, RIGHT, TOP, BOTTOM
		};

		strut(TYPE type, size_t width, size_t min, size_t max)
			: type(type), width(width), min(min), max(max) { }

		TYPE type;
		size_t width, min, max;
	};

	bool get_struts(Display* disp, std::vector<strut>& out) {
		Window* clients;
		size_t client_count = 0;
		static Atom clientlist_msg = XInternAtom(disp, "_NET_CLIENT_LIST", False);
		if (!(clients = (Window*)x11_util::get_property(disp, DefaultRootWindow(disp),
								XA_WINDOW, clientlist_msg, &client_count))) {
			ERROR_DIR("unable to retrieve list of clients");
			return false;
		}
		for (size_t i = 0; i < client_count; ++i) {
			unsigned long* xstrut;
			size_t xstrut_count = 0;//number of strut values for this client (should always be 12)
			static Atom strut_msg = XInternAtom(disp, "_NET_WM_STRUT_PARTIAL", False);
			if (!(xstrut = (unsigned long*)x11_util::get_property(disp, clients[i],
									XA_CARDINAL, strut_msg, &xstrut_count))) {
				//DEBUG("client %lu of %lu lacks struts", i+1, client_count);
				continue;
			}
			if (xstrut_count != 12) {//nice to have
				ERROR_DIR("incorrect number of strut values: got %lu, expected 12", xstrut_count);
				x11_util::free_property(clients);
				x11_util::free_property(xstrut);
				return false;
			}

			DEBUG("client %lu of %lu struts: left:%lu@%lu-%lu right:%lu@%lu-%lu top:%lu@%lu-%lu bot:%lu@%lu-%lu",
					i+1, client_count,
					xstrut[0], xstrut[4], xstrut[5],
					xstrut[1], xstrut[6], xstrut[7],
					xstrut[2], xstrut[8], xstrut[9],
					xstrut[3], xstrut[10], xstrut[11]);

			//left
			if (xstrut[0] > 0) {
				out.push_back(strut(strut::LEFT, xstrut[0], xstrut[4], xstrut[5]));
			}
			//right
			if (xstrut[1] > 0) {
				out.push_back(strut(strut::RIGHT, xstrut[1], xstrut[6], xstrut[7]));
			}
			//top
			if (xstrut[2] > 0) {
				out.push_back(strut(strut::TOP, xstrut[2], xstrut[8], xstrut[9]));
			}
			//bot
			if (xstrut[3] > 0) {
				out.push_back(strut(strut::BOTTOM, xstrut[3], xstrut[10], xstrut[11]));
			}

			x11_util::free_property(xstrut);
		}
		x11_util::free_property(clients);
		return true;
	}

	void trim_screen(const Dimensions& bound, const std::vector<strut>& struts,
			Dimensions& screen) {
		//for simpler math, operate on things in terms of min/max
		long screen_max_x = screen.x + screen.width,
			screen_max_y = screen.y + screen.height;

		for (std::vector<strut>::const_iterator iter = struts.begin();
			 iter != struts.end(); ++iter) {
			switch (iter->type) {
			case strut::LEFT:
				// first check if it intersects our screen's min/max y
				if (INTERSECTION(screen.y, screen_max_y, iter->min, iter->max) != 0) {
					//then check if the strut (relative to the bounding box) actually exceeds our min x
					screen.x = MAX(screen.x, (long)iter->width - bound.x);
				}
				break;
			case strut::RIGHT:
				if (INTERSECTION(screen.y, screen_max_y, iter->min, iter->max) != 0) {
					long bound_max_x = bound.x + bound.width;
					screen_max_x = MIN(screen_max_x, bound_max_x - iter->width);
				}
				break;
			case strut::TOP:
				if (INTERSECTION(screen.x, screen_max_x, iter->min, iter->max) != 0) {
					screen.y = MAX(screen.y, (long)iter->width - bound.y);
				}
				break;
			case strut::BOTTOM:
				if (INTERSECTION(screen.x, screen_max_x, iter->min, iter->max) != 0) {
					long bound_max_y = bound.y + bound.height;
					screen_max_y = MIN(screen_max_y, bound_max_y - iter->width);
				}
				break;
			}
		}

		screen.width = screen_max_x - screen.x;
		screen.height = screen_max_y - screen.y;

		DEBUG("trimmed: %ldx %ldy %ldw %ldh",
				screen.x, screen.y, screen.width, screen.height);
	}
}

bool viewport::xinerama::get_viewports(Display* disp, const Dimensions& activewin,
		dim_list_t& viewports_out, size_t& active_out) {
	viewports_out.clear();

	Dimensions bounding_box;
	if (!get_screens(disp, activewin, bounding_box, viewports_out, active_out)) {
		return false;
	}

	std::vector<strut> struts;
	if (!get_struts(disp, struts)) {
		return false;
	}

	// trim struts from viewports
	for (dim_list_t::iterator iter = viewports_out.begin();
		 iter != viewports_out.end(); ++iter) {
		trim_screen(bounding_box, struts, *iter);
	}

	return true;
}

A src/viewport-imp-xinerama.h => src/viewport-imp-xinerama.h +42 -0
@@ 0,0 1,42 @@
#ifndef GRIDMGR_VIEWPORT_IMP_XINERAMA_H
#define GRIDMGR_VIEWPORT_IMP_XINERAMA_H

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <vector>
#include <X11/Xlib.h>

#include "config.h"
#include "dimensions.h"

#ifndef USE_XINERAMA
#error "Build configuration error:"
#error " Shouldn't be building this file if USE_XINERAMA is disabled."
#endif

typedef std::vector<Dimensions> dim_list_t;

namespace viewport {
	namespace xinerama {
		bool get_viewports(Display* disp, const Dimensions& activewin,
				dim_list_t& viewports_out, size_t& active_out);
	}
}

#endif

D src/viewport-xinerama.cpp => src/viewport-xinerama.cpp +0 -176
@@ 1,176 0,0 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "viewport.h"
#include "x11-util.h"
#include "config.h"

#include <X11/extensions/Xinerama.h>

#ifndef USE_XINERAMA
#error "Build configuration error:"
#error " Shouldn't be building this file if USE_XINERAMA is disabled."
#endif

#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
static int INTERSECTION(int a1, int a2, int b1, int b2) {
	int ret = (a2 < b1 || b2 < a1) ? 0 :
		(a1 >= b1) ?
		((a2 >= b2) ? (b2 - a1) : (a2 - a1)) :
		((a2 >= b2) ? (b2 - b1) : (a2 - b1));
	DEBUG("%d-%d x %d-%d = %d", a1, a2, b1, b2, ret);
	return ret;
}

bool viewport::get_viewport_xinerama(Display* disp,
		const Dimensions& activewin,
		Dimensions& viewport_out) {
	//pick the xinerama screen which the active window 'belongs' to (= 'active screen')
	//also calculate a bounding box across all screens (needed for strut math)
	long bound_xmin = 0, bound_xmax = 0, bound_ymin = 0, bound_ymax = 0,//bounding box of all screens
		active_xmin = 0, active_xmax = 0, active_ymin = 0, active_ymax = 0;//copy of the active screen
	{
		int screen_count = 0;
		XineramaScreenInfo* screens = XineramaQueryScreens(disp, &screen_count);
		if (screens == NULL || screen_count == 0) {
			DEBUG_DIR("xinerama disabled");
			if (screens != NULL) {
				XFree(screens);
			}
			return false;
		} else {
			//initialize bounding box to something
			bound_xmin = screens[0].x_org;
			bound_xmax = screens[0].x_org + screens[0].width;
			bound_ymin = screens[0].y_org;
			bound_ymax = screens[0].y_org + screens[0].height;

			//search for largest overlap between active window and xinerama screen.
			//the screen with the most overlap is the 'active screen'
			int active_i = 0, active_overlap = 0;

			for (int i = 0; i < screen_count; ++i) {
				XineramaScreenInfo& screen = screens[i];

				//grow bounding box
				bound_xmin = MIN(bound_xmin, screen.x_org);
				bound_xmax = MAX(bound_xmax, screen.x_org + screen.width);
				bound_ymin = MIN(bound_ymin, screen.y_org);
				bound_ymax = MAX(bound_ymax, screen.y_org + screen.height);

				//check overlap, update counters if this overlap is bigger
				int overlap =
					INTERSECTION(screen.x_org, screen.x_org+screen.width,
							activewin.x, activewin.x+activewin.width) *
					INTERSECTION(screen.y_org, screen.y_org+screen.height,
							activewin.y, activewin.y+activewin.height);

				DEBUG("screen %d of %d (overlap %d): %dx %dy %dw %dh",
						i+1, screen_count, overlap,
						screens[i].x_org, screens[i].y_org,
						screens[i].width, screens[i].height);

				if (overlap > active_overlap) {
					active_overlap = overlap;
					active_i = i;
				}
			}

			//set active screen's dimensions
			active_xmin = screens[active_i].x_org;
			active_xmax = active_xmin + screens[active_i].width;
			active_ymin = screens[active_i].y_org;
			active_ymax = active_ymin + screens[active_i].height;
		}
		XFree(screens);
	}
	DEBUG("desktop bounding box: %ld-%ldx %ld-%ldy",
			bound_xmin, bound_xmax, bound_ymin, bound_ymax);
	DEBUG("active screen: %ld-%ldx %ld-%ldy",
			active_xmin, active_xmax, active_ymin, active_ymax);

	//now that we've got the active screen and the bounding box,
	//iterate over all struts, shrinking the active screen's viewport as necessary

	//make copy of active_*, operate on things in terms of min/max until the end
	long viewport_xmin = active_xmin, viewport_xmax = active_xmax,
		viewport_ymin = active_ymin, viewport_ymax = active_ymax;

	size_t client_count = 0;
	Window* clients;
	if (!(clients = (Window*)x11_util::get_property(disp, DefaultRootWindow(disp),
							XA_WINDOW, "_NET_CLIENT_LIST", &client_count))) {
		ERROR_DIR("unable to retrieve list of clients");
		return false;
	}
	for (size_t i = 0; i < client_count; ++i) {
		unsigned long* strut;
		size_t strut_count = 0;//number of strut values for this client (should always be 12)
		if (!(strut = (unsigned long*)x11_util::get_property(disp, clients[i],
								XA_CARDINAL, "_NET_WM_STRUT_PARTIAL", &strut_count))) {
			//DEBUG("client %lu of %lu lacks struts", i+1, client_count);
			continue;
		}
		if (strut_count != 12) {//nice to have
			ERROR_DIR("incorrect number of strut values: got %lu, expected 12", strut_count);
			x11_util::free_property(strut);
			return false;
		}
		DEBUG("client %lu of %lu struts: left:%lu@%lu-%lu right:%lu@%lu-%lu top:%lu@%lu-%lu bot:%lu@%lu-%lu",
				i+1, client_count,
				strut[0], strut[4], strut[5],
				strut[1], strut[6], strut[7],
				strut[2], strut[8], strut[9],
				strut[3], strut[10], strut[11]);

		//check whether the strut (which is given relative to the bounding box) is within
		//the active screen, then update/shrink the active screen's viewport if it is.

		//left strut: first check if it intersects our screen's min/max y
		if (strut[0] > 0 && INTERSECTION(active_ymin, active_ymax, strut[4], strut[5]) != 0) {
			//then check if the strut (relative to the bounding box) actually exceeds our min x
			viewport_xmin = MAX(viewport_xmin, (long)strut[0] - bound_xmin);
		}
		//right strut
		if (strut[1] > 0 && INTERSECTION(active_ymin, active_ymax, strut[6], strut[7]) != 0) {
			viewport_xmax = MIN(viewport_xmax, bound_xmax - strut[1]);
		}
		//top strut
		if (strut[2] > 0 && INTERSECTION(active_xmin, active_xmax, strut[8], strut[9]) != 0) {
			viewport_ymin = MAX(viewport_ymin, (long)strut[2] - bound_ymin);
		}
		//bottom strut
		if (strut[3] > 0 && INTERSECTION(active_xmin, active_xmax, strut[10], strut[11]) != 0) {
			viewport_ymax = MIN(viewport_ymax, bound_ymax - strut[3]);
		}
		x11_util::free_property(strut);
	}
	x11_util::free_property(clients);

	viewport_out.x = viewport_xmin;
	viewport_out.y = viewport_ymin;
	viewport_out.width = viewport_xmax - viewport_xmin;
	viewport_out.height = viewport_ymax - viewport_ymin;

	DEBUG("trimmed active screen: %ld-%ldx %ld-%ldy -> %ldx %ldy %ldw %ldh",
			viewport_xmin, viewport_xmax, viewport_ymin, viewport_ymax,
			viewport_out.x, viewport_out.y, viewport_out.width, viewport_out.height);

	return true;
}

A src/viewport.cpp => src/viewport.cpp +79 -0
@@ 0,0 1,79 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <cstddef>

#include "config.h"
#include "neighbor.h"
#include "viewport.h"

#include "viewport-imp-ewmh.h"
#ifdef USE_XINERAMA
#include "viewport-imp-xinerama.h"
#endif

namespace {
	bool get_all(const Dimensions& activewin,
			dim_list_t& viewports, size_t& active) {
		Display* disp = XOpenDisplay(NULL);
		if (disp == NULL) {
			ERROR_DIR("unable to get display");
			return false;
		}

#ifdef USE_XINERAMA
		//try xinerama, fall back to ewmh if xinerama is unavailable
		bool ok = viewport::xinerama::get_viewports(disp, activewin, viewports, active) ||
			viewport::ewmh::get_viewports(disp, activewin, viewports, active);
#else
		//xinerama disabled; just do ewmh
		bool ok = viewport::ewmh::get_viewports(disp, activewin, viewports, active);
#endif

		if (config::debug_enabled) {
			for (size_t i = 0; i < viewports.size(); ++i) {
				const Dimensions& v = viewports[i];
				DEBUG("viewport %lu: %dx %dy %luw %luh",
						i, v.x, v.y, v.width, v.height);
			}
		}

		XCloseDisplay(disp);
		return ok;
	}
}

bool ViewportCalc::Viewports(grid::DIR monitor,
		Dimensions& cur_viewport, Dimensions& next_viewport) const {
	dim_list_t viewports;
	size_t active, neighbor;
	if (!get_all(activewin, viewports, active)) {
		return false;
	}

	neighbor::select(monitor, viewports, active, neighbor);

	cur_viewport = viewports[active];
	next_viewport = viewports[neighbor];

	DEBUG("cur viewport: %dx %dy %dw %dh",
			cur_viewport.x, cur_viewport.y, cur_viewport.width, cur_viewport.height);
	DEBUG("next viewport: %dx %dy %dw %dh",
			next_viewport.x, next_viewport.y, next_viewport.width, next_viewport.height);
	return true;
}

M src/viewport.h => src/viewport.h +11 -14
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 19,22 19,19 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <X11/Xlib.h>

#include "dimensions.h"
#include "config.h"

namespace viewport {
#include "pos.h"

	bool get_viewport_ewmh(Display* disp,
			Dimensions& viewport_out);
class ViewportCalc {
public:
	ViewportCalc(const Dimensions& activewin)
		: activewin(activewin) { }

#ifdef USE_XINERAMA
	bool get_viewport_xinerama(Display* disp,
			const Dimensions& activewin,
			Dimensions& viewport_out);
#endif
	bool Viewports(grid::DIR monitor,
			Dimensions& cur_viewport, Dimensions& next_viewport) const;

}
private:
	const Dimensions activewin;
};

#endif

M src/window.cpp => src/window.cpp +190 -131
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 16,15 16,15 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "config.h"
#include "neighbor.h"
#include "window.h"
#include "viewport.h"
#include "x11-util.h"
#include "config.h"

#define SOURCE_INDICATION 2 //say that we're a pager or taskbar

namespace {
	int client_msg(Display* disp, Window win, const char *msg,
	int _client_msg(Display* disp, Window win, Atom msg,
			unsigned long data0, unsigned long data1,
			unsigned long data2, unsigned long data3,
			unsigned long data4) {


@@ 34,7 34,7 @@ namespace {
		event.xclient.type = ClientMessage;
		event.xclient.serial = 0;
		event.xclient.send_event = True;
		event.xclient.message_type = XInternAtom(disp, msg, False);
		event.xclient.message_type = msg;
		event.xclient.window = win;
		event.xclient.format = 32;
		event.xclient.data.l[0] = data0;


@@ 43,8 43,8 @@ namespace {
		event.xclient.data.l[3] = data3;
		event.xclient.data.l[4] = data4;

		DEBUG("send message_type=%lu, data=(%lu,%lu,%lu,%lu,%lu)",
				event.xclient.message_type, data0, data1, data2, data3, data4);
		DEBUG("send message_type=%s, data=(%lu,%lu,%lu,%lu,%lu)",
				XGetAtomName(disp, msg), data0, data1, data2, data3, data4);

		if (XSendEvent(disp, DefaultRootWindow(disp), False, mask, &event)) {
			return true;


@@ 54,7 54,7 @@ namespace {
		}
	}

	void print_window(Display* disp, Window win) {
	inline void print_window(Display* disp, Window win) {
		if (!config::debug_enabled) { return; }
		Window root;
		int x, y;


@@ 67,71 67,71 @@ namespace {
		DEBUG("  %dx %dy %uw %uh %ub", x, y, width, height, border);
	}

	bool check_window_allowed(Display* disp, Window win) {
		bool ret = true;
		{
			/*
			  disallow moving this window if it has type DESKTOP or DOCK.
			  (avoid messing with the user's desktop components)
			*/
			size_t count = 0;
			Atom* types;
	Window* get_active_window(Display* disp) {
		static Atom actwin_msg = XInternAtom(disp, "_NET_ACTIVE_WINDOW", False);
		Window* ret = (Window*)x11_util::get_property(disp, DefaultRootWindow(disp),
				XA_WINDOW, actwin_msg, NULL);
		if (ret == NULL) {
			ERROR_DIR("unable to get active window");
		}
		return ret;
	}

	bool is_dock_window(Display* disp, Window win) {
		/* disallow moving/selecting this window if it has type DESKTOP or DOCK.
		   (avoid messing with the user's desktop components) */
		bool ret = false;
		size_t count = 0;
		static Atom wintype_msg = XInternAtom(disp, "_NET_WM_WINDOW_TYPE", False);
		Atom* types = (Atom*)x11_util::get_property(disp, win, XA_ATOM, wintype_msg, &count);
		if (types == NULL) {
			ERROR_DIR("couldn't get window types");
			//assume window types are fine, keep going
		} else {
			static Atom desktop_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DESKTOP", False),
				dock_type = XInternAtom(disp, "_NET_WM_WINDOW_TYPE_DOCK", False);

			if (!(types = (Atom*)x11_util::get_property(disp, win,
									XA_ATOM, "_NET_WM_WINDOW_TYPE", &count))) {
				ERROR_DIR("couldn't get window types");
				//assume window types are allowed, keep going
			} else {
				for (size_t i = 0; i < count; ++i) {
					DEBUG("type %lu: %d %s",
							i, types[i], XGetAtomName(disp, types[i]));
					if (types[i] == desktop_type || types[i] == dock_type) {
						ret = false;
						if (!config::debug_enabled) {
							break;
						}
			for (size_t i = 0; i < count; ++i) {
				DEBUG("%d type %lu: %d %s",
						win, i, types[i], XGetAtomName(disp, types[i]));
				if (types[i] == desktop_type || types[i] == dock_type) {
					ret = true;
					if (!config::debug_enabled) {
						break;
					}
				}
				x11_util::free_property(types);
			}
			x11_util::free_property(types);
		}
		return ret;
	}

		{
			/*
			  also disallow moving this window if it has BOTH the SKIP_PAGER and SKIP_TASKBAR.
			  (avoid messing with auxiliary panels and menus)
			*/
			size_t count = 0;
			Atom* states;
	bool is_menu_window(Display* disp, Window win) {
		/* also disallow moving/selecting this window if it has BOTH SKIP_PAGER and SKIP_TASKBAR.
		   (avoid messing with auxiliary panels and menus) */
		bool ret = false;
		size_t count = 0;
		static Atom state_msg = XInternAtom(disp, "_NET_WM_STATE", False);
		Atom* states = (Atom*)x11_util::get_property(disp, win, XA_ATOM, state_msg, &count);
		if (states == NULL) {
			ERROR_DIR("couldn't get window states");
			//assume window states are fine, keep going
		} else {
			static Atom skip_pager = XInternAtom(disp, "_NET_WM_STATE_SKIP_PAGER", False),
				skip_taskbar = XInternAtom(disp, "_NET_WM_STATE_SKIP_TASKBAR", False);

			if (!(states = (Atom*)x11_util::get_property(disp, win,
									XA_ATOM, "_NET_WM_STATE", &count))) {
				ERROR_DIR("couldn't get window states");
				//assume window states are allowed, keep going
			} else {
				bool has_skip_pager = false, has_skip_taskbar = false;
				for (size_t i = 0; i < count; ++i) {
					DEBUG("state %lu: %d %s",
							i, states[i], XGetAtomName(disp, states[i]));
					if (states[i] == skip_pager) {
						has_skip_pager = true;
					} else if (states[i] == skip_taskbar) {
						has_skip_taskbar = true;
					}
				}
				x11_util::free_property(states);
				if (has_skip_pager && has_skip_taskbar) {
					ret = false;
			bool has_skip_pager = false, has_skip_taskbar = false;
			for (size_t i = 0; i < count; ++i) {
				DEBUG("%d state %lu: %d %s",
						win, i, states[i], XGetAtomName(disp, states[i]));
				if (states[i] == skip_pager) {
					has_skip_pager = true;
				} else if (states[i] == skip_taskbar) {
					has_skip_taskbar = true;
				}
			}
		}

		if (!ret) {
			LOG_DIR("Active window is a desktop or dock. Ignoring move request.");
			x11_util::free_property(states);
			if (has_skip_pager && has_skip_taskbar) {
				ret = true;
			}
		}
		return ret;
	}


@@ 216,71 216,93 @@ namespace {
		return true;
	}

	bool defullscreen_deshade_window(Display* disp, Window win) {
		/*
		  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)
		*/

		if (!client_msg(disp, win, "_NET_WM_STATE",
						0,//1 = enable state(s), 0 = disable state(s)
						XInternAtom(disp, "_NET_WM_STATE_SHADED", False),
						XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False),
						SOURCE_INDICATION, 0)) {
			ERROR_DIR("couldn't unshade/defullscreen");
	bool activate_window(Display* disp, Window curactive, Window newactive) {
		static Atom active_msg = XInternAtom(disp, "_NET_ACTIVE_WINDOW", False);
		if (!_client_msg(disp, newactive, active_msg,
						SOURCE_INDICATION, CurrentTime, curactive, 0, 0)) {
			ERROR_DIR("couldn't activate");
			return false;
		}
		return true;
	}

	bool maximize_window(Display* disp, Window win, bool maximize) {
	bool set_window_state(Display* disp, Window win, Atom state1, Atom state2, bool enable) {
		/*
		  this disagrees with docs, which say that we should be using a
		  _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",
						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)) {
			if (maximize) {
				ERROR_DIR("couldn't maximize");
			} else {
				ERROR_DIR("couldn't demaximize");
			}
			return false;
		}

		return true;
		int val = (enable) ? 1 : 0;// just to be explicit
		static Atom state_msg = XInternAtom(disp, "_NET_WM_STATE", False);
		return _client_msg(disp, win, state_msg,
				val, state1, state2, SOURCE_INDICATION, 0);
	}

	bool get_viewport(Display* disp, const Dimensions& activewin,
			Dimensions& viewport_out) {
#ifdef USE_XINERAMA
		//try xinerama, fall back to ewmh if xinerama is unavailable
		return viewport::get_viewport_xinerama(disp, activewin, viewport_out) ||
			viewport::get_viewport_ewmh(disp, viewport_out);
#else
		//xinerama disabled; just do ewmh
		return viewport::get_viewport_ewmh(disp, viewport_out);
#endif
	bool maximize_window(Display* disp, Window win, bool enable) {
		static Atom max_vert = XInternAtom(disp, "_NET_WM_STATE_MAXIMIZED_VERT", False),
			max_horiz = XInternAtom(disp, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
		return set_window_state(disp, win, max_vert, max_horiz, enable);
	}
}

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

	if (!(win = (Window*)x11_util::get_property(disp, DefaultRootWindow(disp),
							XA_WINDOW, "_NET_ACTIVE_WINDOW", NULL))) {
		ERROR_DIR("unable to get active window");
	std::vector<Window> wins;

	{
		size_t win_count = 0;
		static Atom clientlist_msg = XInternAtom(disp, "_NET_CLIENT_LIST", False);
		Window* all_wins = (Window*)x11_util::get_property(disp, DefaultRootWindow(disp),
				XA_WINDOW, clientlist_msg, &win_count);
		if (all_wins == NULL || win_count == 0) {
			ERROR_DIR("unable to get list of windows");
			if (all_wins != NULL) {
				x11_util::free_property(all_wins);
			}
			XCloseDisplay(disp);
			return false;
		}
		// only select normal windows, ignore docks and menus
		for (size_t i = 0; i < win_count; ++i) {
			if (!is_dock_window(disp, all_wins[i]) && !is_menu_window(disp, all_wins[i])) {
				wins.push_back(all_wins[i]);
			}
		}
		x11_util::free_property(all_wins);
	}

	size_t active_window = 0;
	dim_list_t all_windows;
	{
		Window* active = get_active_window(disp);
		if (active == NULL) {
			XCloseDisplay(disp);
			return false;
		}
		for (size_t i = 0; i < wins.size(); ++i) {
			if (wins[i] == *active) {
				active_window = i;
				DEBUG_DIR("ACTIVE:");
			}
			all_windows.push_back(Dimensions());
			get_window_size(disp, wins[i], &all_windows.back(), NULL, NULL);
		}
		x11_util::free_property(active);
	}

	size_t next_window;
	neighbor::select(dir, all_windows, active_window, next_window);

	bool ok = activate_window(disp, wins[active_window], wins[next_window]);

	XCloseDisplay(disp);
	return ok;
}

ActiveWindow::~ActiveWindow() {


@@ 292,45 314,49 @@ ActiveWindow::~ActiveWindow() {
	}
}

#define CHECK_STATE() if (disp == NULL || win == NULL) { \
		ERROR_DIR("unable to initialize active window"); \
		return false; \
bool ActiveWindow::init() {
	if (disp == NULL) {
		disp = XOpenDisplay(NULL);
		if (disp == NULL) {
			ERROR_DIR("unable to get display");
			return false;
		}
	}

bool ActiveWindow::Sizes(Dimensions& viewport, Dimensions& activewin) const {
	CHECK_STATE();
	if (win == NULL) {
		win = get_active_window(disp);
		if (win == NULL) {
			return false;
		}
	}
	return true;
}

	if (!check_window_allowed(disp, *win)) {
bool ActiveWindow::Size(Dimensions& activewin) {
	if (!init()) {
		return false;
	}

	if (!get_window_size(disp, *win, &activewin, NULL, NULL)) {
		ERROR_DIR("couldn't get window size");
	if (is_dock_window(disp, *win) || is_menu_window(disp, *win)) {
		LOG_DIR("Active window is a desktop or dock. Ignoring move request.");
		return false;
	}

	/*
	  special case: if the active window is fullscreen, the desktop workarea is
	  wrong (ie the obscured taskbar extents aren't included). get around this
	  by unfullscreening the window first (if applicable). but return the
	  window's dimensions from when it was fullscreened/shaded.
	*/
	defullscreen_deshade_window(disp, *win);//disregard failure

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

	DEBUG("viewport %dx %dy %luw %luh, activewin %dx %dy %luw %luh",
			viewport.x, viewport.y, viewport.width, viewport.height,
	DEBUG("activewin %dx %dy %luw %luh",
			activewin.x, activewin.y, activewin.width, activewin.height);

	return true;
}

bool ActiveWindow::MoveResize(const Dimensions& activewin) {
	CHECK_STATE();
	if (!init()) {
		return false;
	}

	unsigned int margin_width, margin_height;
	if (!get_window_size(disp, *win, NULL, &margin_width, &margin_height)) {


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

	//demaximize the window before attempting to move it
	maximize_window(disp, *win, false);//disregard failure
	if (!maximize_window(disp, *win, false)) {
		ERROR_DIR("couldn't demaximize");
		//disregard failure
	}

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


@@ 359,7 388,37 @@ bool ActiveWindow::MoveResize(const Dimensions& activewin) {
}

bool ActiveWindow::Maximize() {
	CHECK_STATE();
	if (!init()) {
		return false;
	}

	if (!maximize_window(disp, *win, true)) {
		ERROR_DIR("couldn't maximize");
		return false;
	}
	return true;
}
bool ActiveWindow::DeFullscreen() {
	if (!init()) {
		return false;
	}

	return maximize_window(disp, *win, true);
	static Atom fs = XInternAtom(disp, "_NET_WM_STATE_FULLSCREEN", False);
	if (!set_window_state(disp, *win, fs, 0, false)) {
		ERROR_DIR("couldn't defullscreen");
		return false;
	}
	return true;
}
bool ActiveWindow::DeShade() {
	if (!init()) {
		return false;
	}

	static Atom shade = XInternAtom(disp, "_NET_WM_STATE_SHADED", False);
	if (!set_window_state(disp, *win, shade, 0, false)) {
		ERROR_DIR("couldn't deshade");
		return false;
	}
	return true;
}

M src/window.h => src/window.h +16 -10
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 21,24 21,30 @@

#include <X11/Xlib.h>

#include "pos.h"
#include "dimensions.h"

/*
  NOTE: This class internally assumes that a new active window won't be
  selected for the lifetime of the constructed object. Any changes in
  active windows will NOT be automatically detected!
*/
namespace window {
	/* Finds the nearest window in the given direction and activates it. */
	bool select_activate(grid::DIR dir);
}

class ActiveWindow {
 public:
	ActiveWindow();
public:
	ActiveWindow() : disp(NULL), win(NULL) { }
	virtual ~ActiveWindow();

	bool Sizes(Dimensions& viewport, Dimensions& activewin) const;
	bool Size(Dimensions& activewin);

	bool MoveResize(const Dimensions& activewin);

	bool Maximize();
	bool DeFullscreen();
	bool DeShade();

private:
	bool init();

 private:
	Display* disp;
	Window* win;
};

M src/x11-util.cpp => src/x11-util.cpp +8 -11
@@ 1,6 1,6 @@
/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 16,18 16,13 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "x11-util.h"
#include "config.h"
#include "x11-util.h"

#define MAX_PROPERTY_VALUE_LEN 4096

unsigned char* x11_util::get_property(Display *disp, Window win,
		Atom xa_prop_type, const char* prop_name, size_t* out_count) {
	Atom xa_prop_name = XInternAtom(disp, prop_name, false);
	if (xa_prop_name == None) {
		ERROR("Atom not found for %s", prop_name);
		return NULL;
	}
		Atom xa_prop_type, Atom xa_prop_name, size_t* out_count) {
	Atom xa_ret_type;
	int ret_format;
	unsigned long ret_nitems, ret_bytes_after;


@@ 41,8 36,10 @@ unsigned char* x11_util::get_property(Display *disp, Window win,
	if (XGetWindowProperty(disp, win, xa_prop_name, 0, MAX_PROPERTY_VALUE_LEN / 4, false,
					xa_prop_type, &xa_ret_type, &ret_format,
					&ret_nitems, &ret_bytes_after, &ret_prop) != Success) {
		ERROR("Cannot get %s property.", prop_name);
		ERROR("Cannot get property %d/%s.", xa_prop_name, XGetAtomName(disp, xa_prop_name));
		return NULL;
	} else {
		DEBUG("Property %d/%s -> %lu items", xa_prop_name, XGetAtomName(disp, xa_prop_name), ret_nitems);
	}

	if (xa_ret_type != xa_prop_type) {


@@ 55,8 52,8 @@ unsigned char* x11_util::get_property(Display *disp, Window win,
		} else {
			char *req = XGetAtomName(disp, xa_prop_type),
				*got = XGetAtomName(disp, xa_ret_type);
			ERROR("Invalid type of %s property: req %s, got %s",
					prop_name, req, got);
			ERROR("Invalid type of property %d/%s: req %s, got %s",
					xa_prop_name, XGetAtomName(disp, xa_prop_name), req, got);
			XFree(req);
			XFree(got);
		}

M src/x11-util.h => src/x11-util.h +4 -3
@@ 3,7 3,7 @@

/*
  gridmgr - Organizes windows according to a grid.
  Copyright (C) 2011  Nicholas Parker
  Copyright (C) 2011-2012  Nicholas Parker

  This program is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by


@@ 19,12 19,13 @@
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <stdint.h>

namespace x11_util {
	unsigned char* get_property(Display *disp, Window win,
			Atom xa_prop_type, const char* prop_name, size_t* out_count);
			Atom xa_prop_type, Atom xa_prop_name, size_t* out_count);
	void free_property(void* prop);
}