/* Copyright (c) 2000-2004 Lars Bernhardsson, see README for license details */
#include <stdio.h>
#include <stdlib.h>

#include <X11/X.h>
#include <X11/Xos.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/shape.h>

#include "dat.h"
#include "fns.h"

/*
 * This file was added to David Hogan's 9wm source distribution.
 * It is written by Lars Bernhardsson, with inspiration from
 * such things as acme, wily, oberon and 100's of misplaced windows
 * over the years. :)
 */

/* only tile visible top level windows on the current desktop. */
#define DO_TILE(x,s) (((x)->screen == (s)) && (((x)->desktop == (s)->desktop) || (x)->issticky) && ((x)->trans == None) && (!(x)->isnotile) && (!(x)->istool) && (normal(x)))

/* windows to raise when Raise Untiled is selected. */
#define DO_RAISE_NOTILE(x,s) (((x)->screen == (s)) && (((x)->desktop == (s)->desktop) || (x)->issticky) && ((x)->isnotile) && (normal(x)))

/* global variables for this module */
static int track_x[64][2];
static int track_width[64][2];

#define MAX_RC 1024
static Client *rc[MAX_RC];
static int nrc;

/* which tracks to reserve tool space in. */
static int res_track = 0;


void
getmouse (int *x, int *y, ScreenInfo * s)
{
  Window dw1, dw2;
  int t1, t2;
  unsigned int t3;

  XQueryPointer (dpy, s->root, &dw1, &dw2, x, y, &t1, &t2, &t3);
}

void
setmouse (int x, int y, ScreenInfo * s)
{
  XWarpPointer (dpy, None, s->root, None, None, None, None, x, y);
}

void
rev_clients (void)
{
  Client *c;

  nrc = 0;

  for (c = clients; c && (nrc < MAX_RC); c = c->next)
    rc[nrc++] = c;
}

int
is_float (Client * c)
{
  int i;

  if (!c->class)
    return 0;

  for (i = 0; i < MAXFLOATCLASS; i++)
    {
      char *inst;

      if (!prefs.floatclass[i])
	continue;

      if (strcmp (c->class, prefs.floatclass[i]) == 0)
	return 1;

      if (!c->instance)
	return 0;

      if (strcmp (c->instance, prefs.floatclass[i]) == 0)
	return 1;

      if ((inst = strchr (prefs.floatclass[i], '~')))
	{
	  inst++;
	  if (strncmp (c->class, prefs.floatclass[i], strlen (c->class)) == 0)
	    if (strncmp (c->instance, inst, strlen (c->instance)) == 0)
	      return 1;
	}
    }

  return 0;
}

int
is_sticky (Client * c)
{
  int i;

  if (!c->class)
    return 0;

  for (i = 0; i < MAXSTICKYCLASS; i++)
    {
      char *inst;

      if (!prefs.stickyclass[i])
	continue;

      if (strcmp (c->class, prefs.stickyclass[i]) == 0)
	return 1;

      if (!c->instance)
	return 0;

      if (strcmp (c->instance, prefs.stickyclass[i]) == 0)
	return 1;

      if ((inst = strchr (prefs.stickyclass[i], '~')))
	{
	  inst++;
	  if (strncmp (c->class, prefs.stickyclass[i], strlen (c->class)) ==
	      0)
	    if (strncmp (c->instance, inst, strlen (c->instance)) == 0)
	      return 1;
	}
    }

  return 0;
}

int
is_tool (Client * c)
{
  int i;

  if (!c->class)
    return 0;

  for (i = 0; i < MAXTOOLCLASS; i++)
    {
      char *inst;

      if (!prefs.toolclass[i])
	continue;

      if (strcmp (c->class, prefs.toolclass[i]) == 0)
	return 1;

      if (!c->instance)
	return 0;

      if (strcmp (c->instance, prefs.toolclass[i]) == 0)
	return 1;

      if ((inst = strchr (prefs.toolclass[i], '~')))
	{
	  inst++;
	  if (strncmp (c->class, prefs.toolclass[i], strlen (c->class)) == 0)
	    if (strncmp (c->instance, inst, strlen (c->instance)) == 0)
	      return 1;
	}
    }

  return 0;
}

int
is_notile (Client * c)
{
  int i;

  if (!c->class)
    return 1;

  for (i = 0; i < MAXNOTILECLASS; i++)
    {
      char *inst;

      if (!prefs.notileclass[i])
	continue;

      if (strcmp (c->class, prefs.notileclass[i]) == 0)
	return 0;

      if (!c->instance)
	return 1;

      if (strcmp (c->instance, prefs.notileclass[i]) == 0)
	return 0;

      if ((inst = strchr (prefs.notileclass[i], '~')))
	{
	  inst++;
	  if (strncmp (c->class, prefs.notileclass[i], strlen (c->class)) ==
	      0)
	    if (strncmp (c->instance, inst, strlen (c->instance)) == 0)
	      return 0;
	}
    }

  return 1;
}

int
is_dt (Client * c)
{
  int i;

  if (!c->class)
    return -1;

  for (i = 0; i < MAXDTCLASS; i++)
    {
      char *inst;

      if (!prefs.dtclass[i])
	continue;

      if (strcmp (c->class, prefs.dtclass[i]) == 0)
	return prefs.dtnum[i];

      if (!c->instance)
	return -1;

      if (strcmp (c->instance, prefs.dtclass[i]) == 0)
	return prefs.dtnum[i];

      if ((inst = strchr (prefs.dtclass[i], '~')))
	{
	  inst++;
	  if (strncmp (c->class, prefs.dtclass[i], strlen (c->class)) == 0)
	    if (strncmp (c->instance, inst, strlen (c->instance)) == 0)
	      return prefs.dtnum[i];
	}
    }

  return -1;
}

void
toggle_isfloat (Client * c)
{
  if (c)
    c->isfloat = !c->isfloat;

  update_tbar ();
}

void
toggle_istool (Client * c)
{
  if (c)
    c->istool = !c->istool;

  update_tbar ();
}

void
toggle_issticky (Client * c)
{
  if (c)
    c->issticky = !c->issticky;

  update_tbar ();
}

void
toggle_inc (ScreenInfo * s)
{
  if (s)
    s->bigmr[s->desktop] = !s->bigmr[s->desktop];

  update_tbar ();
}

void
toggle_clickthru (ScreenInfo * s)
{
  if (s)
    s->clickthru[s->desktop] = !s->clickthru[s->desktop];

  update_tbar ();
}

void
toggle_tile_resize (ScreenInfo * s)
{
  if (s)
    s->tile_resize[s->desktop] = !s->tile_resize[s->desktop];

  update_tbar ();
}

void
toggle_skip_focus (ScreenInfo * s)
{
  if (s)
    s->skip_focus[s->desktop] = !s->skip_focus[s->desktop];

  update_tbar ();
}

int
count_clients (ScreenInfo * s)
{
  Client *c;
  int i = 0;

  for (c = clients; c; c = c->next)
    if (DO_TILE (c, s))
      i++;

  return i;
}

void
find_tools (ScreenInfo * s)
{
  Client *c;
  int min_y;
  int min_x, max_x;
  int t;
  int found = 0;

  if (!s)
    abort ();

  t = 0;
  max_x = -1;
  min_x = DisplayWidth (dpy, s->num) + 1;
  min_y = DisplayHeight (dpy, s->num) + 1;

  for (c = clients; c; c = c->next)
    {
      if (c->istool && (c->screen == s) && normal (c) &&
	  (c->issticky || (c->desktop == s->desktop)) &&
	  s->tile_resize[s->desktop])
	{
	  int x1 = c->x - BORDER;
	  int y1 = c->y - BORDER;
	  int x2 = c->x + c->dx + BORDER;

	  found = 1;

	  if (x1 < min_x)
	    min_x = x1;

	  if (x2 > max_x)
	    max_x = x2;

	  if (y1 < min_y)
	    min_y = y1;
	}
    }

  if (found)
    {
      s->res_height = min_y - TILE_PAD;

      if (min_x < track_width[s->num][0])
	t |= 1;
      if (max_x >= track_x[s->num][1])
	t |= 2;
      res_track = t;
    }
  else
    {
      s->res_height = s->tile_height;
      res_track = 0;
    }
}

void
calc_track_sizes (ScreenInfo * s)
{
  int screen_width;

  if (!s)
    abort ();

  screen_width = DisplayWidth (dpy, s->num);
  track_x[s->num][0] = 0;
  track_width[s->num][0] =
    (s->left_track_width[s->desktop] * screen_width) / 100;

  track_x[s->num][1] = track_width[s->num][0];
  track_width[s->num][1] = screen_width - track_width[s->num][0];
}

void
toggle_notile (ScreenInfo * s)
{
  if (s->notile_raised[s->desktop])
    raise_tile (s, 1);
  else
    raise_notile (s);
}

void
raise_notile (ScreenInfo * s)
{
  int i;

  rev_clients ();

  for (i = nrc - 1; i >= 0; i--)
    if (DO_RAISE_NOTILE (rc[i], s))
      XMapRaised (dpy, rc[i]->parent);

  s->notile_raised[s->desktop] = 1;
  raise_tbar (s);

  if (s->notilefocused[s->desktop]
      && DO_RAISE_NOTILE (s->notilefocused[s->desktop], s)
      && !s->notilefocused[s->desktop]->isaside)
    {
      active (s->notilefocused[s->desktop]);
    }
  else
    {
      revert_same (s);
    }

  if (current)
    XMapRaised (dpy, current->parent);

  update_tbar ();
}

void
raise_tile (ScreenInfo * s, int user)
{
  Client *c;

  for (c = clients; c; c = c->next)
    if (DO_TILE (c, s))
      XMapRaised (dpy, c->parent);

  s->notile_raised[s->desktop] = 0;
  raise_tbar (s);

  if (s->focused[s->desktop] && DO_TILE (s->focused[s->desktop], s))
    {
      if (user || (!current && (mouse_on_screen () == s))
	  || (current && (current->screen == s)))
	{
	  active (s->focused[s->desktop]);
	}
    }
  else
    {
      revert_same (s);
    }

  update_tbar ();
}

void
tile_all (ScreenInfo * s)
{
  Client *c;

  unsigned int screen_height_left, screen_height_right;
  unsigned int track_width_left, track_width_right;

  unsigned short clients_left, clients_right;
  unsigned short current_client = 0;

  unsigned short on_right = 0;

  if (!s)
    abort ();


  calc_track_sizes (s);
  find_tools (s);

  track_width_left = track_width[s->num][0];
  track_width_right = track_width[s->num][1];

  screen_height_left = (res_track & 1 ? s->res_height : s->tile_height);
  screen_height_right = (res_track & 2 ? s->res_height : s->tile_height);

  clients_right = count_clients (s);

  if (clients_right == 0)
    return;

  clients_left = 1;
  --clients_right;

  for (c = clients; c; c = c->next)
    {
      /*
       * WARNING
       * The variables "screen_height_left", "odx" and "ody"
       * have more than one purpose.
       */
      if (DO_TILE (c, s))
	{
	  unsigned int x, y, width, height;
	  unsigned int odx, ody;

	  if (on_right)
	    {
	      x = track_width_left;
	      y = screen_height_right - screen_height_left;
	      height = screen_height_right / clients_right;
	      width = track_width_right;

	      if (++current_client < clients_right)
		{
		  screen_height_left -= height;
		  height -= TILE_PAD;
		}
	      else
		height = screen_height_left;

	    }
	  else
	    {
	      x = y = 0;
	      height = screen_height_left;
	      width = track_width_left - TILE_PAD;

	      /*
	       * Here the purpose of
	       * screen_height_left changes
	       */
	      screen_height_left = screen_height_right;
	    }

	  height -= 2 * BORDER;
	  width -= 2 * BORDER;

	  c->isleft = on_right ? 0 : 1;

	  odx = width;
	  ody = height;

	  /*
	   * resize in given increments if the program
	   * wishes this..
	   */
	  if (c->size.flags & PResizeInc)
	    {
	      width = c->min_dx +
		(((signed int) (width -
				c->min_dx) / c->size.width_inc) *
		 c->size.width_inc);

	      height = c->min_dy +
		(((signed int) (height -
				c->min_dy) / c->size.height_inc) *
		 c->size.height_inc);
	    }

	  /* don't exceed this size */
	  if (c->size.flags & PMaxSize)
	    {
	      if (width > c->size.max_width)
		width = c->size.max_width;

	      if (height > c->size.max_height)
		height = c->size.max_height;
	    }

	  /*
	   * adjust track width if left window doesn't use
	   * all of it or height left if a right window
	   * doesn't use it all..
	   */
	  if (on_right)
	    {
	      if (ody != height)
		screen_height_left += ody - height;
	    }
	  else if (odx != width)
	    {
	      track_width_right += odx - width;
	      track_width_left -= odx - width;
	    }

	  /*
	   * The second meaning begins
	   */
	  odx = c->dx;
	  ody = c->dy;

	  c->x = x;
	  c->y = y;

	  /*
	   * if resize is true OR we're at the upper left corner
	   * set size to maximum
	   */
	  if (s->tile_resize[s->desktop] || !on_right)
	    {
	      c->dx = width;
	      c->dy = height;
	    }

	  XMoveResizeWindow (dpy, c->parent, c->x, c->y,
			     c->dx + (2 * BORDER), c->dy + (2 * BORDER));
	  XResizeWindow (dpy, c->window, c->dx, c->dy);
	  sendconfig (c);

	  /* give focus to window if appropriate */
	  if (c->isleft
	      && (mouse_on_screen () == s) && !s->notile_raised[s->desktop])
	    {
	      if (!current
		  || !s->skip_focus[s->desktop]
		  || !s->tile_resize[s->desktop])
		active (c);
	    }

	  on_right = 1;
	}
    }

  if (current && !current->isnotile && !s->skip_focus[s->desktop])
    {
      top (current);
      XMapRaised (dpy, current->parent);
    }

  if (!s->notile_raised[s->desktop])
    {
      raise_tile (s, 0);
    }
}

void
goto_desktop (ScreenInfo * s, int n)
{
  if (s)
    {
      s->desktop = n;
      s->desktop %= prefs.desktops;
      update_desktop (s);
    }
}

void
next_desktop (ScreenInfo * s)
{
  if (s)
    {
      s->desktop++;
      s->desktop %= prefs.desktops;
      update_desktop (s);
    }
}

void
next_desktop_drag (ScreenInfo * s)
{
  Client *t;

  if (s)
    {
      if (current && (current->screen == s))
	{
	  t = current;

	  t->desktop++;
	  t->desktop %= prefs.desktops;

	  next_desktop (s);

	  active (t);

	  if (t->isnotile)
	    raise_notile (s);
	  else
	    raise_tile (s, 0);

	  update_tbar ();
	}
    }
}

void
prev_desktop (ScreenInfo * s)
{
  if (s)
    {
      s->desktop += (prefs.desktops - 1);
      s->desktop %= prefs.desktops;
      update_desktop (s);
    }
}

void
prev_desktop_drag (ScreenInfo * s)
{
  Client *t;

  if (s)
    {
      if (current && (current->screen == s))
	{
	  t = current;

	  t->desktop += (prefs.desktops - 1);
	  t->desktop %= prefs.desktops;

	  prev_desktop (s);

	  active (t);

	  if (t->isnotile)
	    raise_notile (s);
	  else
	    raise_tile (s, 0);

	  update_tbar ();
	}
    }
}

void
move_hide (Client * c)
{
  if (c)
    XMoveWindow (dpy, c->parent, DisplayWidth (dpy, c->screen->num), c->y);
}

void
move_show (Client * c)
{
  if (c)
    XMoveWindow (dpy, c->parent, c->x, c->y);
}

void
update_desktop (ScreenInfo * s)
{
  Client *c;

  for (c = clients; c; c = c->next)
    {
      if ((c->screen != s) || (c->parent == c->screen->root) || c->issticky
	  || (c->state != NormalState))
	continue;

      if (c->desktop == s->desktop)
	move_show (c);
      else
	move_hide (c);
    }

  if (!s->notile_raised[s->desktop] && s->focused[s->desktop])
    active (s->focused[s->desktop]);
  else if (s->notile_raised[s->desktop] && s->notilefocused[s->desktop])
    active (s->notilefocused[s->desktop]);
  else
    {
      revert_same (s);

      if (!current)
	revert_window (s);
    }

  tile_all (s);
  update_tbar ();
}

void
revert_window (ScreenInfo * s)
{
  Client *c;

  if (mouse_on_screen () != s)
    {
      if (current && (current->screen == s))
	nofocus ();

      return;
    }

  if (prev_current)
    {
      c = prev_current;

      if (!c->istool && !c->isaside
	  && (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
	{
	  if (c->isnotile)
	    raise_notile (s);
	  else
	    raise_tile (s, 0);

	  active (c);
	  return;
	}
    }

  for (c = clients; c; c = c->next)
    {
      if (!c->istool && !c->isaside
	  && (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
	{
	  if (c->isnotile)
	    raise_notile (s);
	  else
	    raise_tile (s, 0);

	  active (c);
	  return;
	}
    }

  nofocus ();
}

void
revert_same (ScreenInfo * s)
{
  Client *c;

  if (mouse_on_screen () != s)
    {
      if (current && (current->screen == s))
	nofocus ();

      return;
    }

  if (prev_current)
    {
      c = prev_current;

      if (!c->istool && !c->isaside
	  && (c->isnotile == s->notile_raised[s->desktop]) && (DO_TILE (c, s)
							       ||
							       DO_RAISE_NOTILE
							       (c, s)))
	{
	  active (c);
	  return;
	}
    }

  for (c = clients; c; c = c->next)
    {
      if (!c->istool && !c->isaside
	  && (c->isnotile == s->notile_raised[s->desktop]) && (DO_TILE (c, s)
							       ||
							       DO_RAISE_NOTILE
							       (c, s)))
	{
	  active (c);
	  return;
	}
    }

  nofocus ();
}

void
prev_window (ScreenInfo * s)
{
  Client *c;

  if (!current || !prev_current || (prev_current == current)
      || (prev_current->desktop != current->desktop)
      || (prev_current->isnotile != current->isnotile)
      || (!s->notile_raised[s->desktop] && !s->skip_focus[s->desktop]))
    {
      for (c = clients; c; c = c->next)
	{
	  if (!c->istool && !c->isaside &&
	      (c != current) &&
	      (c->isnotile == s->notile_raised[s->desktop]) &&
	      (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
	    {
	      active (c);

	      if (c->isnotile || (!c->isnotile && !s->skip_focus[s->desktop]))
		{
		  raise_tbar (s);
		  top (c);
		  XMapRaised (dpy, c->parent);
		  tile_all (s);
		}

	      return;
	    }
	}
      return;
    }

  c = prev_current;

  if (!c->istool && !c->isaside &&
      (c != current) &&
      (c->isnotile == s->notile_raised[s->desktop]) &&
      (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
    {
      active (c);

      if (c->isnotile || (!c->isnotile && !s->skip_focus[s->desktop]))
	{
	  raise_tbar (s);
	  top (c);
	  XMapRaised (dpy, c->parent);
	  tile_all (s);
	}
    }
}

void
next_window (ScreenInfo * s)
{
  Client *c;

  if (s->notile_raised[s->desktop] ||
      (!s->notile_raised[s->desktop] && !s->skip_focus[s->desktop]))
    {
      Client *last = 0;

      for (c = clients; c; c = c->next)
	{
	  if (!c->istool && !c->isaside &&
	      (c != current) &&
	      (c->isnotile == s->notile_raised[s->desktop]) &&
	      (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
	    {
	      last = c;
	    }
	}

      if (last)
	{
	  raise_tbar (s);
	  top (last);
	  XMapRaised (dpy, last->parent);

	  if (last->isnotile)
	    active (last);
	  else
	    tile_all (s);
	}

      return;
    }

  if (current &&
      current->next &&
      !s->notile_raised[s->desktop] && s->skip_focus[s->desktop])
    {
      for (c = current->next; c; c = c->next)
	{
	  if (!c->istool && !c->isaside &&
	      (c != current) &&
	      (c->isnotile == s->notile_raised[s->desktop]) &&
	      (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
	    {
	      active (c);

	      if (c->isnotile || (!c->isnotile && !s->skip_focus[s->desktop]))
		{
		  raise_tbar (s);
		  XMapRaised (dpy, c->parent);
		  tile_all (s);
		}

	      return;
	    }
	}
    }

  for (c = clients; c; c = c->next)
    {
      if (!c->istool && !c->isaside &&
	  (c != current) &&
	  (c->isnotile == s->notile_raised[s->desktop]) &&
	  (DO_TILE (c, s) || DO_RAISE_NOTILE (c, s)))
	{
	  active (c);

	  if (c->isnotile || (!c->isnotile && !s->skip_focus[s->desktop]))
	    {
	      raise_tbar (s);
	      top (c);
	      XMapRaised (dpy, c->parent);
	      tile_all (s);
	    }

	  return;
	}
    }
}

void
prev_screen (ScreenInfo * s)
{
  ScreenInfo *ns;
  int cur;

  if (!s)
    abort ();

  cur = s->num;

  if (--cur < 0)
    cur = num_screens - 1;

  raise_tbar (s);

  ns = &screens[cur];

  setmouse (DisplayWidth (dpy, cur) / 2, DisplayHeight (dpy, cur) - 2, ns);

  if (!ns->notile_raised[ns->desktop] && ns->focused[ns->desktop])
    active (ns->focused[ns->desktop]);
  else if (ns->notile_raised[ns->desktop] && ns->notilefocused[ns->desktop])
    active (ns->notilefocused[ns->desktop]);
  else
    {
      revert_same (ns);

      if (!current)
	revert_window (ns);
    }
}

void
next_screen (ScreenInfo * s)
{
  ScreenInfo *ns;
  int cur;

  if (!s)
    abort ();

  cur = s->num;

  if (++cur >= num_screens)
    cur = 0;

  raise_tbar (s);

  ns = &screens[cur];

  setmouse (DisplayWidth (dpy, cur) / 2, DisplayHeight (dpy, cur) - 2, ns);

  if (!ns->notile_raised[ns->desktop] && ns->focused[ns->desktop])
    active (ns->focused[ns->desktop]);
  else if (ns->notile_raised[ns->desktop] && ns->notilefocused[ns->desktop])
    active (ns->notilefocused[ns->desktop]);
  else
    {
      revert_same (ns);

      if (!current)
	revert_window (ns);
    }
}

ScreenInfo *
mouse_on_screen (void)
{
  int i;

  if (num_screens == 1)
    return &screens[0];

  for (i = 0; i < num_screens; i++)
    {
      Window root, child;
      int rx, ry, wx, wy;
      unsigned int mask;

      if (XQueryPointer
	  (dpy, screens[i].root, &root, &child, &rx, &ry, &wx, &wy,
	   &mask) == True)
	return &screens[i];
    }

  return 0;
}

void
shrink_left (ScreenInfo * s)
{
  if (s)
    {
      s->left_track_width[s->desktop] -= 5;

      if (s->left_track_width[s->desktop] < 5)
	s->left_track_width[s->desktop] = 5;

      tile_all (s);
    }
}

void
grow_left (ScreenInfo * s)
{
  if (s)
    {
      s->left_track_width[s->desktop] += 5;

      if (s->left_track_width[s->desktop] > 95)
	s->left_track_width[s->desktop] = 95;

      tile_all (s);
    }
}

void
max_left (ScreenInfo * s)
{
  if (s)
    {
      s->left_track_width[s->desktop] = 95;

      tile_all (s);
    }
}

void
restore_left (ScreenInfo * s)
{
  if (s)
    {
      s->left_track_width[s->desktop] =
	prefs.left_track_width[s->num][s->desktop];

      tile_all (s);
    }
}

void
move_northwest (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = 0;
      c->y = 0;
      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_north (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = (DisplayWidth (dpy, c->screen->num) / 2) - (c->dx / 2) - BORDER;
      c->y = 0;

      if (c->x < 0)
	c->x = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_northeast (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = (DisplayWidth (dpy, c->screen->num)) - (c->dx) - (2 * BORDER);
      c->y = 0;
      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_west (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = 0;
      c->y = (c->screen->tile_height / 2) - (c->dy / 2) - BORDER;

      if (c->y < 0)
	c->y = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_center (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = (DisplayWidth (dpy, c->screen->num) / 2) - (c->dx / 2) - BORDER;
      c->y = (c->screen->tile_height / 2) - (c->dy / 2) - BORDER;

      if (c->x < 0)
	c->x = 0;

      if (c->y < 0)
	c->y = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_east (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = DisplayWidth (dpy, c->screen->num) - c->dx - (2 * BORDER);
      c->y = (c->screen->tile_height / 2) - (c->dy / 2) - BORDER;

      if (c->y < 0)
	c->y = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_southwest (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = 0;
      c->y = c->screen->tile_height - c->dy - (2 * BORDER);
      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_south (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = (DisplayWidth (dpy, c->screen->num) / 2) - (c->dx / 2) - BORDER;
      c->y = c->screen->tile_height - c->dy - (2 * BORDER);

      if (c->x < 0)
	c->x = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_southeast (Client * c)
{
  if (c && c->isnotile)
    {
      c->x = DisplayWidth (dpy, c->screen->num) - c->dx - (2 * BORDER);
      c->y = c->screen->tile_height - c->dy - (2 * BORDER);
      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_up (Client * c)
{
  if (c && c->isnotile)
    {
      if (c->screen->bigmr[c->screen->desktop])
	c->y -= (c->screen->tile_height / 20);
      else
	c->y--;

      if (c->y < 0)
	c->y = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_down (Client * c)
{
  if (c && c->isnotile)
    {
      int max_y = c->screen->tile_height - c->dy - (2 * BORDER);

      if (c->screen->bigmr[c->screen->desktop])
	c->y += (c->screen->tile_height / 20);
      else
	c->y++;

      if (c->y > max_y)
	c->y = max_y;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_left (Client * c)
{
  if (c && c->isnotile)
    {
      if (c->screen->bigmr[c->screen->desktop])
	c->x -= (DisplayWidth (dpy, c->screen->num) / 20);
      else
	c->x--;

      if (c->x < 0)
	c->x = 0;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
move_right (Client * c)
{
  if (c && c->isnotile)
    {
      int max_x = DisplayWidth (dpy, c->screen->num) - c->dx - (2 * BORDER);

      if (c->screen->bigmr[c->screen->desktop])
	c->x += (DisplayWidth (dpy, c->screen->num) / 20);
      else
	c->x++;

      if (c->x > max_x)
	c->x = max_x;

      XMoveWindow (dpy, c->parent, c->x, c->y);
      sendconfig (c);
    }
}

void
zoom (Client * c, int dimension)
{
  if (c)
    {
      if (!c->isnotile)
	{
	  if (c->screen->notile_raised[c->screen->desktop])
	    toggle_notile (c->screen);

	  if (c->isleft)
	    {
	      Client *p;

	      for (p = clients->next; p; p = p->next)
		{
		  if (!p->istool &&
		      (p != current) &&
		      (p->isnotile ==
		       p->screen->notile_raised[p->screen->desktop])
		      && (DO_TILE (p, c->screen)
			  || DO_RAISE_NOTILE (p, c->screen)))
		    {
		      active (p);
		      break;
		    }
		}

	      top (current);
	      tile_all (c->screen);
	    }
	  else
	    {
	      top (c);
	      tile_all (c->screen);
	    }
	}
      else if (c->iszoom == ZOOM_SELECT)
	{
	  move_select (c, 0);
	}
      else if (c->iszoom == ZOOM_NORMAL)
	{
	  c->x = c->zx;
	  c->y = c->zy;
	  c->dx = c->zdx;
	  c->dy = c->zdy;

	  XMoveResizeWindow (dpy, c->parent, c->x, c->y, c->dx + (2 * BORDER),
			     c->dy + (2 * BORDER));
	  XResizeWindow (dpy, c->window, c->dx, c->dy);
	  sendconfig (c);

	  c->iszoom = ZOOM_UNZOOMED;
	}
      else
	{
	  int height;
	  int width;

	  if (dimension & DIM_OUTSIDE)
	    {
	      width = DisplayWidth (dpy, c->screen->num);
	      height = DisplayHeight (dpy, c->screen->num);
	    }
	  else
	    {
	      height = c->screen->tile_height - (2 * BORDER);
	      width = DisplayWidth (dpy, c->screen->num) - (2 * BORDER);
	    }

	  if (c->size.flags & PResizeInc)
	    {
	      width =
		c->min_dx + (width -
			     c->min_dx) / c->size.width_inc *
		c->size.width_inc;
	      height =
		c->min_dy + (height -
			     c->min_dy) / c->size.height_inc *
		c->size.height_inc;
	    }

	  if (c->size.flags & PMaxSize)
	    {
	      if (width > c->size.max_width)
		width = c->size.max_width;

	      if (height > c->size.max_height)
		height = c->size.max_height;
	    }

	  c->zx = c->x;
	  c->zy = c->y;
	  c->zdx = c->dx;
	  c->zdy = c->dy;

	  if (dimension & DIM_HORIZ)
	    {
	      c->dx = width;

	      if (dimension & DIM_OUTSIDE)
		c->x = -BORDER;
	      else
		c->x =
		  (DisplayWidth (dpy, c->screen->num) / 2) - (c->dx / 2) -
		  BORDER;
	    }

	  if (dimension & DIM_VERT)
	    {
	      c->dy = height;

	      if (dimension & DIM_OUTSIDE)
		c->y = -BORDER;
	      else
		c->y = (c->screen->tile_height / 2) - (c->dy / 2) - BORDER;
	    }

	  XMoveResizeWindow (dpy, c->parent, c->x, c->y, c->dx + (2 * BORDER),
			     c->dy + (2 * BORDER));
	  XResizeWindow (dpy, c->window, c->dx, c->dy);
	  sendconfig (c);

	  c->iszoom = ZOOM_NORMAL;
	}

      update_tbar ();
    }
}

void
grow (Client * c, int dimension, int m)
{
  int h, w, dh, dw, x, y;
  int odx, ody, want_w, want_h;
  int ddx, ddy;

  if (!c || !dimension)
    return;

  if (!c->isnotile)
    {
      if ((dimension == DIM_HORIZ) || (dimension == (DIM_HORIZ | DIM_VERT)))
	{
	  if (m > 0)
	    grow_left (c->screen);
	  else
	    shrink_left (c->screen);
	}
      else if (dimension == DIM_VERT)
	{
	  if (m > 0)
	    max_left (c->screen);
	  else
	    restore_left (c->screen);
	}
      return;
    }

  x = c->x;
  y = c->y;

  w = odx = c->dx;
  h = ody = c->dy;

  dw = DisplayWidth (dpy, c->screen->num) - (2 * BORDER);
  dh = c->screen->tile_height - (2 * BORDER);

  if (c->screen->bigmr[c->screen->desktop])
    {
      ddx = (dw / 20);
      ddy = (dh / 20);
    }
  else if (c->size.flags & PResizeInc)
    {
      ddx = c->size.width_inc;
      ddy = c->size.height_inc;
    }
  else
    {
      ddx = 2;
      ddy = 2;
    }

  if (dimension & DIM_HORIZ)
    {
      x -= (ddx / 2) * m;
      w += ddx * m;

      if ((x + w) > dw)
	x = dw - w;

      if (x < 0)
	x = 0;

      if ((x + w) > dw)
	w = dw;

      if (w < 1)
	w = 1;
    }

  if (dimension & DIM_VERT)
    {
      y -= (ddy / 2) * m;
      h += ddy * m;

      if ((y + h) > dh)
	y = dh - h;

      if (y < 0)
	y = 0;

      if ((y + h) > dh)
	h = dh;

      if (h < 1)
	h = 1;
    }

  want_w = w;
  want_h = h;

  if (c->size.flags & PResizeInc)
    {
      w = c->min_dx + (w - c->min_dx) / c->size.width_inc * c->size.width_inc;
      h =
	c->min_dy + (h - c->min_dy) / c->size.height_inc * c->size.height_inc;
    }

  if (c->size.flags & PMinSize)
    {
      if (w < c->size.min_width)
	w = c->size.min_width;

      if (h < c->size.min_height)
	h = c->size.min_height;
    }

  if (c->size.flags & PMaxSize)
    {
      if (w > c->size.max_width)
	w = c->size.max_width;

      if (h > c->size.max_height)
	h = c->size.max_height;
    }

  x += ((want_w - w) / 2);
  y += ((want_h - h) / 2);

  if ((w == odx) && (h == ody))
    return;

  if (w != odx)
    {
      c->x = x;
      c->dx = w;
    }

  if (h != ody)
    {
      c->y = y;
      c->dy = h;
    }

  XMoveResizeWindow (dpy, c->parent, c->x, c->y, c->dx + (2 * BORDER),
		     c->dy + (2 * BORDER));
  XResizeWindow (dpy, c->window, c->dx, c->dy);
  sendconfig (c);
}

void
move_select (Client * c, int untiled)
{
  int h, w, dh, dw, x, y;
  int odx, ody;

  if (!c || !c->isnotile || c->isaside)
    return;

  odx = c->dx;
  ody = c->dy;

  if (c->iszoom == ZOOM_NORMAL)
    {
      zoom (c, DIM_HORIZ | DIM_VERT);

      return;
    }
  else if (c->iszoom == ZOOM_SELECT)
    {
      c->x = c->zx;
      c->y = c->zy;
      c->dx = c->zdx;
      c->dy = c->zdy;

      XMoveResizeWindow (dpy, c->parent, c->x, c->y, c->dx + (2 * BORDER),
			 c->dy + (2 * BORDER));
      XResizeWindow (dpy, c->window, c->dx, c->dy);
      sendconfig (c);

      XMapRaised (dpy, c->parent);
      top (c);
      active (c);

      if (!c->screen->notile_raised[c->screen->desktop])
	raise_notile (c->screen);

      c->iszoom = ZOOM_UNZOOMED;

      update_tbar ();
      return;
    }

  x = c->x;
  y = c->y;

  w = odx;
  h = ody;

  dh = c->screen->tile_height - (2 * BORDER);
  dw = DisplayWidth (dpy, c->screen->num) - (2 * BORDER);

  if (untiled)			/* window that just stopped being tiled, restore original size */
    {
      if ((c->size.flags & PSize)
	  && ((c->size.height != ody) || (c->size.width != odx)))
	{
	  h = c->size.height;
	  w = c->size.width;
	}
      else			/* emacs changes size hints when you resize it... odd. */
	{
	  h = dh / 2;
	  w = dw / 2;
	}
    }
  else
    {
      h = dh;
      w = (17 * h) / 22;	/* US Letter ratio */
    }

  if (w > dw)			/* for screens with non-standard ratios */
    w = dw;

  if (h > dh)			/* for when hints says taller than screen */
    h = dh;

  if (c->size.flags & PResizeInc)
    {
      w = c->min_dx + (w - c->min_dx) / c->size.width_inc * c->size.width_inc;
      h =
	c->min_dy + (h - c->min_dy) / c->size.height_inc * c->size.height_inc;
    }

  if (c->size.flags & PMinSize)
    {
      if (w < c->size.min_width)
	w = c->size.min_width;

      if (h < c->size.min_height)
	h = c->size.min_height;
    }

  if (c->size.flags & PMaxSize)
    {
      if (w > c->size.max_width)
	w = c->size.max_width;

      if (h > c->size.max_height)
	h = c->size.max_height;
    }

  x = (DisplayWidth (dpy, c->screen->num) / 2) - (w / 2) - BORDER;
  y = (c->screen->tile_height / 2) - (h / 2) - BORDER;

  if (c->x < 0)
    c->x = 0;

  if (c->y < 0)
    c->y = 0;

  c->zx = c->x;
  c->zy = c->y;
  c->zdx = c->dx;
  c->zdy = c->dy;

  if (w != odx)
    {
      c->x = x;
      c->dx = w;
    }

  if (h != ody)
    {
      c->y = y;
      c->dy = h;
    }

  XMoveResizeWindow (dpy, c->parent, c->x, c->y, c->dx + (2 * BORDER),
		     c->dy + (2 * BORDER));
  XResizeWindow (dpy, c->window, c->dx, c->dy);
  sendconfig (c);

  XMapRaised (dpy, c->parent);
  top (c);
  active (c);

  if (!c->screen->notile_raised[c->screen->desktop])
    raise_notile (c->screen);

  if (untiled)			/* if just stopped being tiled, do not count this as zoomed */
    c->iszoom = ZOOM_UNZOOMED;
  else
    c->iszoom = ZOOM_SELECT;

  update_tbar ();
}

int
count_aside (ScreenInfo * s)
{
  int i = 0;
  Client *c;

  if (!s)
    return 0;

  for (c = clients; c; c = c->next)
    {
      if (c->isaside && DO_RAISE_NOTILE (c, s))
	{
	  i++;
	}
    }

  return i;
}

Client *
find_aside (ScreenInfo * s)
{
  Client *c;

  if (!s)
    return (Client *) 0;

  for (c = clients; c; c = c->next)
    {
      if (c->isaside && DO_RAISE_NOTILE (c, s))
	{
	  return c;
	}
    }

  return (Client *) 0;
}

void
arrange_aside (ScreenInfo * s)
{
  int wnum = 0;
  int wcount;
  int ystep;
  Client *c;

  if (!s)
    return;

  wcount = count_aside (s);

  if (wcount < 1)
    {
      return;
    }

  ystep = s->tile_height / wcount;

  for (c = clients; c; c = c->next)
    {
      if (c->isaside && DO_RAISE_NOTILE (c, s))
	{
	  c->y = ystep * wnum++;
	  XMapRaised (dpy, c->parent);
	  XMoveWindow (dpy, c->parent, c->x, c->y);
	}
    }
}

void
move_aside (ScreenInfo * s, Client * c)
{
  if (!s)
    return;

  if (!c || !c->isnotile || c->istool)
    {
      Client *t = find_aside (s);

      if (t)
	{
	  raise_notile (s);
	  XMapRaised (dpy, s->barwin);
	  move_aside (s, t);
	}

      return;
    }

  if (c->isaside)
    {
      c->x = c->ax;
      c->y = c->ay;
      c->isaside = 0;

      if (s->notile_raised[s->desktop] != c->isnotile)
	toggle_notile (s);

      arrange_aside (s);
      active (c);
      raise_tbar (s);
      XMapRaised (dpy, c->parent);
      XMoveWindow (dpy, c->parent, c->x, c->y);
    }
  else
    {
      c->ax = c->x;
      c->ay = c->y;
      c->x = (95 * DisplayWidth (dpy, s->num)) / 100;
      c->isaside = 1;
      arrange_aside (s);
      revert_window (s);
      XMapRaised (dpy, s->barwin);

      if (current)
	XMapRaised (dpy, current->parent);
    }
}

void
move_aside_other (ScreenInfo * s)
{
  Client *c;

  if (!s || !current || !current->isnotile)
    return;

  for (c = clients; c; c = c->next)
    {
      if ((c != current) && !c->isaside && !c->istool
	  && DO_RAISE_NOTILE (c, s))
	{
	  c->ax = c->x;
	  c->ay = c->y;
	  c->x = (95 * DisplayWidth (dpy, s->num)) / 100;
	  c->isaside = 1;
	}
    }

  arrange_aside (s);
  XMapRaised (dpy, s->barwin);
  XMapRaised (dpy, current->parent);
}

void
move_aside_clear (ScreenInfo * s, Client * c)
{
  if (s && c && (c->screen == s) && c->isnotile && normal (c) && !c->isaside)
    move_aside (s, c);
}

void
move_sd (Client * c)
{
  if (!c)
    return;

  c->iszoom = ZOOM_UNZOOMED;

  if (c->isnotile)
    {
      c->isnotile = 0;
      raise_tile (c->screen, 0);
      top (c);
      active (c);
      tile_all (c->screen);
    }
  else
    {
      c->isnotile = 1;
      tile_all (c->screen);
      raise_notile (c->screen);
      move_select (c, 1);
    }
}


syntax highlighted by Code2HTML, v. 0.9.1