/*-
 * Copyright (c) 2001 Jordan DeLong
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of contributors may be
 *    used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#include "pager.h"

/* the size ratio of a pager's screen to the real thing */
double		pager_ratio	= 0.04;

/* decoration group for pager windows */
dgroup_t	*pager_dgroup	= NULL;

/* some boolean variables from the rcfile */
int		pager_parentrel;	/* use a ParentRelative background? */
int		pager_drawgrid;		/* should we draw a grid */
int		pager_nomove;		/* disalow moves? */

/* width to use for paged_t windows */
int		pager_pagedbdrwidth;

/* stacking layer for pager clients */
int		pager_stacklayer;

/*
 * pixmaps for drawing selected and nonselected
 * workspaces; pager_backpixmap does a pixmap for
 * the full back of a pager, and takes precendence
 * over pager_nonselpixmap;
 */
pixmap_t	*pager_selpixmap;
pixmap_t	*pager_nonselpixmap;
pixmap_t	*pager_backpixmap;
int		pager_backscale;
pixmap_t	*pager_winpixmap;
int		pager_winscale;
pixmap_t	*pager_focwinpixmap;
int		pager_focwinscale;

int pager_focwinpixmap_from_screen_shot;
int pager_winpixmap_from_screen_shot;

/* context for pagers and pageds */
XContext	pager_context;
XContext	paged_context;

/* currently focused paged_t window */
paged_t		*paged_focused;

/* convert screen coords to pager coords */
static __inline void pager_scrtopager(pager_t *pager, int *x, int *y) {
	*x = *x * pager_ratio;
	*y = *y * pager_ratio;
	*x += pager->desktop->viewx * pager->spacewidth;
	*y += pager->desktop->viewy * pager->spaceheight;
}

/* convert pager coords to screen coords */
static __inline void pager_pagertoscr(pager_t *pager, int *x, int *y) {
	*x -= pager->desktop->viewx * pager->spacewidth;
	*y -= pager->desktop->viewy * pager->spaceheight;
	*x = *x / pager_ratio;
	*y = *y / pager_ratio;
}

/* setup the pager system; -1 for error */
int pager2_init() {
	if (pager_ratio <= 0)
		return -1;
	pager_context = XUniqueContext();
	paged_context = XUniqueContext();

	return 0;
}

/*
 * set up the background pixmap for a pager window;
 * if there is a non-selected workspace image, make
 * it into a proper grid pixmap and set it as the
 * background.
 */
static Pixmap pager_bgpixmap(pager_t *pager, screen_t *screen,
                             int width, int height)
{
	struct pagerscr *thisscr = &pagerscr[screen->num];
	image_t *image, *scaled;
	Pixmap pixmap;
	int i, n;

	PDEBUG ("pager_bgpixmap entered");

	/*
	 * set up a background pixmap for the pager according
	 * to the settings the user gave.
	 */
	if (pager_backpixmap) {
		if (pager_backscale) {
			pixmap = XCreatePixmap(display, screen->root, width,
				height, DefaultDepth(display, screen->num));
			image = image_frompixmap(pager_backpixmap, screen);
			scaled = image_scale(image, width, height);
			image_put(scaled, pixmap,
				DefaultGC(display, screen->num),
				0, 0, 0, 0, width, height);
			image_destroy(image);
			image_destroy(scaled);

			return pixmap;
		} else
			return pager_backpixmap->pixmaps[screen->num];
	} else if (thisscr->nonsel_img) {
		pixmap = XCreatePixmap(display, screen->root, width,
			height, DefaultDepth(display, screen->num));

		for (i = 0; i < pager->desktop->width; i++)
			for (n = 0; n < pager->desktop->height; n++)
				image_put(thisscr->nonsel_img, pixmap,
					DefaultGC(display, screen->num), 0, 0,
					i * pager->spacewidth,
					n * pager->spaceheight,
					pager->spacewidth, pager->spaceheight);
		return pixmap;
	} else if (pager_parentrel)
		return ParentRelative;

	return None;
}

/* create a pager */
pager_t *pager_create(screen_t *screen, desktop_t *desktop, int usexy,
                      int x, int y)
{
	XSetWindowAttributes attr;
	clientflags_t flags;
	pager_t *pager;
	long mask;
	int wid, hei;

	pager = calloc(1, sizeof(pager_t));
	if (!pager)
		return NULL;
	pager->desktop = desktop;
	TAILQ_INIT(&pager->paged_list);

	/* get dimensions for pager window */
	pager->spacewidth = pager_ratio * screen->width;
	pager->spaceheight = pager_ratio * screen->height;
	wid = pager->spacewidth * desktop->width;
	hei = pager->spaceheight * desktop->height;

	/* if we aren't given position info it just stacks from 0,0 */
	if (!usexy) {
		x = 0;
		y = desktop->num * (hei + pager_dgroup->top_space
			+ pager_dgroup->bottom_space);
	} else {
		if (x < 0)
			x = screen->width + x - (pager_dgroup->left_space
				+ pager_dgroup->right_space);
		if (y < 0)
			y = screen->height + y - (pager_dgroup->top_space
				+ pager_dgroup->bottom_space);
	}

	/* create the new pager window */
	if ((attr.background_pixmap = pager_bgpixmap(pager, screen,
                                                wid, hei)) != None)
   {
		mask = CWBackPixmap;
   }
	else
   {
		attr.background_pixel = pagerscr[screen->num].nonsel_color;
		mask = CWBackPixel;
	}

   // bva ... why isn't this getting us our mouse clicks ? we aren't even seeing
   // the events in the main loop .....
	pager->win = XCreateWindow( display, screen->root, x, y, wid, hei, 1,
                               CopyFromParent, CopyFromParent, CopyFromParent, mask, &attr );
	XSelectInput(display, pager->win, ExposureMask | ButtonReleaseMask | ButtonPressMask);
	XSaveContext(display, pager->win, pager_context, (XPointer) pager );
	plugin_setcontext(plugin_this, pager->win);

	/* create client_t for our pager */
	bzero(&flags, sizeof(clientflags_t));
	flags.internal = 1;
	flags.nofocus = 1;
	flags.noresize = 1;
	flags.noiconify = 1;
	flags.nodelete = 1;
	flags.sticky = 1;
	flags.nomove = pager_nomove;

   // who is turning our event mask off! I want their head!
	pager->client = client_add(screen, pager->win, &flags, pager_dgroup);
	if (!pager->client)
		goto free;

	/*
	 * if parent relative, the client_t window
	 * needs to be parent relative also
	 */
	if (pager_parentrel && attr.background_pixmap == ParentRelative)
		XSetWindowBackgroundPixmap(display,
			pager->client->frame, ParentRelative);

	/* add to workspace/desktop */
	pager->client->stacklayer = pager_stacklayer;
	workspace_add_client(screen->desktop->current_space, pager->client);
	desktop_add_client(pager->client);

	/* map windows */
	pager->client->state = NormalState;
	XMapWindow(display, pager->win);
	XMapWindow(display, pager->client->frame);

	return pager;

free:
	XDestroyWindow(display, pager->win);
	free(pager);
	return NULL;
}

/* clean up after a pager */
void pager_delete(pager_t *pager)
{
	paged_t *paged;

	/* get rid of paged_t's */
	while (!TAILQ_EMPTY(&pager->paged_list)) {
		paged = TAILQ_FIRST(&pager->paged_list);
		pager_rmpaged(pager, paged, paged->client);
	}

	/* get rid of our window */
	XDeleteContext(display, pager->win, pager_context);
	XDestroyWindow(display, pager->win);
	plugin_rmcontext(pager->win);
	client_rm(pager->client);
	free(pager);
}

/*
 * return the bg pixmap for a paged_t; should only get
 * called if pager_winpixmap && pager_winscale.
 */
static Pixmap pager_getpagedbg(client_t *client, int width,
                               int height, int foc)
{
	image_t *image, *scaled;
   screen_t *screen;
	Pixmap pixmap;

	XWMHints *hints;

	PDEBUG ("pager_getpagedbg entered");

	if (foc)
	{
		PDEBUG ("``%s'' is a focused window", client->store_name);
	}
	else
	{
		PDEBUG ("``%s'' is an unfocused window", client->store_name);
	}

	screen = client->screen;

	/* if a window is too small, just skip */
	if ((width <= 0) || (height <= 0))
	{
		PDEBUG ("width or height invalid: %ux%u", width, height);
		return None;
	}

	/* scale the paged window bg pixmap */
	pixmap = XCreatePixmap(display, screen->root, width, height,
                          DefaultDepth(display, screen->num));

	if( foc )
	{
      image = NULL;

		PDEBUG ("Using pixmap ``%s''", pager_focwinpixmap->ident);
      if( pager_focwinpixmap_from_screen_shot )
      {
         image = image_fromscreenshot( client );
         if( !image )
         {
            image = image_frompixmap (pager_focwinpixmap, screen);
         }
      }

      if( !image )
      {
         image = image_frompixmap (pager_focwinpixmap, screen);
      }
	}
	else
   {
      /* if( pager_winpixmap_from_screen_shot ) */
      /*       { */
      /*          image = image_fromscreenshot( client ); */
      /*       } */
      /*       else */
      PDEBUG ("Using pixmap ``%s''", pager_winpixmap->ident);
      image = image_frompixmap( pager_winpixmap, screen );
   }

   if( image == NULL )
   {
      /* flee */
      return pixmap;
   }

	if (((foc && pager_focwinscale) || (!foc && pager_winscale))
			&& ((width != image->ximage->width) || (height != image->ximage->height)))
	{
		scaled = image_scale(image, width, height);
	}
	else
	{
		scaled = image;
	}

	image_put(scaled, pixmap, DefaultGC(display, screen->num),
             0, 0, 0, 0, width, height);

	if (scaled != image)
		image_destroy(scaled);
	image_destroy(image);

	image = scaled = NULL;

	hints = XGetWMHints (display, client->window);
	if (hints && (hints->flags & IconPixmapHint))
	{
		int icon_x, icon_y, icon_width, icon_height;
		int parent_width, parent_height;
      unsigned int dummy_uint;

		Pixmap icon_pixmap;
		Pixmap icon_mask;
      XGCValues gc_vals;
		GC icon_gc;

		Window dummy_window;
		int dummy_int;

		PDEBUG ("Adding icon to pager-pixmap");

		icon_pixmap = hints->icon_pixmap;
		icon_mask   = hints->icon_mask;

		XGetGeometry (display, pixmap, &dummy_window,
                    &dummy_int, &dummy_int,
                    (unsigned int*) &parent_width,
                    (unsigned int*) &parent_height,
                    &dummy_uint, &dummy_uint);

		XGetGeometry (display, icon_pixmap, &dummy_window,
                    &dummy_int, &dummy_int,
                    (unsigned int*) &icon_width,
                    (unsigned int*) &icon_height,
                    &dummy_uint, &dummy_uint);

		icon_x = icon_y = 0;
		if (parent_width < icon_width)
			icon_width = parent_width;
		else
			icon_x = (parent_width - icon_width) / 2;

		if (parent_height < icon_height)
			icon_height = parent_height;
		else
			icon_y = (parent_height - icon_height) / 2;

		gc_vals.clip_mask = icon_mask;
		gc_vals.clip_x_origin = icon_x;
		gc_vals.clip_y_origin = icon_y;

		icon_gc = XCreateGC (display, pixmap,
                           GCClipMask | GCClipXOrigin | GCClipYOrigin,
                           &gc_vals);

		XCopyArea (display, icon_pixmap, pixmap,
                 icon_gc, 0, 0,
                 icon_width, icon_height,
                 icon_x, icon_y);
	}

	XFree (hints);
	hints = NULL;

	return (pixmap);
}

/* add a paged client to a pager */
void pager_addpaged(pager_t *pager, client_t *client)
{
	XSetWindowAttributes attr;
   XIconSize icon_size;
	client_t *lowest;
	paged_t *paged;
	int x, y, wid, hei;
	long mask;

	paged = calloc(1, sizeof(paged_t));
	if (!paged)
		return;
	paged->client = client;

	/* get dimensions of the mini window */
	x = client->x;
	y = client->y;

   paged->pixmap_width = wid = client->width * pager_ratio;
	paged->pixmap_height = hei = client->height * pager_ratio;
	paged->client_width = client->width;
	paged->client_height = client->height;
   paged->c_pager = NULL; // bva

	pager_scrtopager(pager, &x, &y);

   icon_size.min_width  = 1;
	icon_size.min_height = 1;
	icon_size.max_width  = wid;
	icon_size.max_height = hei;
	icon_size.width_inc  = 1;
	icon_size.height_inc = 1;
	XSetIconSizes (display, client->window, &icon_size, 1);

	/* create the mini window */
	if (pager_winpixmap)
	{
		paged->pixmap = pager_getpagedbg(client, wid, hei, 0);
		paged->pixmap_width  = wid;
		paged->pixmap_height = hei;

		attr.background_pixmap = paged->pixmap;

		mask = CWBackPixmap;
	}
   else
   {
		attr.background_pixel = pagerscr[client->screen->num].pagedwin_color;
		mask = CWBackPixel;
	}

	attr.border_pixel = pagerscr[client->screen->num].pagedbdr_color;
	mask |= CWBorderPixel;

	paged->win = XCreateWindow(display, pager->win, x, y,
                              wid > 0 ? wid : 1, hei > 0 ? hei : 1, pager_pagedbdrwidth,
                              CopyFromParent, CopyFromParent, CopyFromParent,
                              mask, &attr);
	XSaveContext(display, client->window, paged_context, (XPointer) paged);
	XSaveContext(display, paged->win, paged_context, (XPointer) paged);

   // bva ...
   paged->c_pager = (struct pager_t *) pager;
   // end bva

	plugin_setcontext(plugin_this, paged->win);
	XMapWindow(display, paged->win);

	/* raise it to the appropriate place */
	lowest = TAILQ_PREV(paged->client, stacklayer, c_stacking);
	if (!lowest)
		lowest = stacking_find_lowest(client->workspace->desktop,
                                    client->stacklayer);
	pager_raisepaged(paged, lowest);

	TAILQ_INSERT_HEAD(&pager->paged_list, paged, p_list);
}

/*
 * remove a paged client from a pager; if paged is null we find
 * paged ourselves.
 */
void pager_rmpaged(pager_t *pager, paged_t *paged, client_t *client)
{
	XDeleteContext(display, client->window, paged_context);
	XDeleteContext(display, paged->win, paged_context);
	plugin_rmcontext(paged->win);
	XDestroyWindow(display, paged->win);

	TAILQ_REMOVE(&pager->paged_list, paged, p_list);
	free(paged);
}

/* move a paged_t to a new pager */
void pager_movepaged( pager_t *pager, paged_t *paged, pager_t *newpager,
                      int doreparent )
{
	client_t *lowest;

	/*
	 * unlink the paged from the old pager, and
	 * add it onto the new one.
	 */
	TAILQ_REMOVE(&pager->paged_list, paged, p_list);
	TAILQ_INSERT_HEAD(&newpager->paged_list, paged, p_list);

   paged->c_pager = (struct pager_t *) newpager; // bva

	/* handle reparent/sizing if told to */
	if (doreparent)
   {
		XReparentWindow(display, paged->win, newpager->win, 0, 0);
		pager_sizepaged(newpager, paged);

		/* raise it appropriatly */
		lowest = TAILQ_PREV(paged->client, stacklayer, c_stacking);
		if (!lowest)
			lowest = stacking_find_lowest(paged->client->workspace->desktop,
                                       paged->client->stacklayer);
		pager_raisepaged(paged, lowest);
	}
}

/* resize/move our pager representation of a client */
void pager_sizepaged(pager_t *pager, paged_t *paged)
{
	int x, y, wid, hei;

	PDEBUG ("pager_sizepaged entered");

	/* recalc dims */
	x = paged->client->x;
	y = paged->client->y;

	wid = paged->client->width * pager_ratio;
	hei = paged->client->height * pager_ratio;
	pager_scrtopager(pager, &x, &y);

   if (wid <= 0)
      wid = 1;
	if (hei <= 0)
		hei = 1;

	/*
	 * if we have a pager_winpixmap and pager_winscale, we
	 * need to rescale the image if the paged_t window
	 * changed size.
	 */
	if ((paged->pixmap_width != wid) || (paged->pixmap_height != hei))
	{
		if ((paged_focused == paged) && pager_focwinpixmap)
		{
			XIconSize icon_size;

			icon_size.min_width  = 1;
			icon_size.min_height = 1;
			icon_size.max_width  = wid;
			icon_size.max_height = hei;
			icon_size.width_inc  = 1;
			icon_size.height_inc = 1;
			XSetIconSizes (display, paged->client->window, &icon_size, 1);

			XFreePixmap (display, paged->pixmap);

			paged->pixmap = pager_getpagedbg (paged->client, wid, hei, 1);
			paged->pixmap_width  = wid;
			paged->pixmap_height = hei;

			XSetWindowBackgroundPixmap(display, paged->win, paged->pixmap);
		}
		else if (paged_focused != paged)
		{
			XIconSize icon_size;

			icon_size.min_width  = 1;
			icon_size.min_height = 1;
			icon_size.max_width  = wid;
			icon_size.max_height = hei;
			icon_size.width_inc  = 1;
			icon_size.height_inc = 1;
			XSetIconSizes (display, paged->client->window, &icon_size, 1);

			XFreePixmap (display, paged->pixmap);

			paged->pixmap = pager_getpagedbg (paged->client, wid, hei, 0);
			paged->pixmap_width  = wid;
			paged->pixmap_height = hei;

			XSetWindowBackgroundPixmap(display, paged->win, paged->pixmap);
		}
   }

   paged->client_width = wid;
   paged->client_height = hei;

	/* resize it */
   XMoveResizeWindow (display, paged->win, x, y, wid, hei);
}

/*
 * handle raising/lowering of paged windows.  the lowest param is the
 * lowest client known to be above paged; paged if it is
 * not known what is above it; or NULL if there is nothing
 * above it.
 */
void pager_raisepaged(paged_t *paged, client_t *lowest) {
	paged_t *lowestpaged;
	desktop_t *desktop;
	client_t *client;
	int i;

	if (!lowest)
		goto noneabove;
	desktop = paged->client->workspace->desktop;

	/* try to find where to raise this under */
	if (lowest == paged->client)
		client = TAILQ_PREV(paged->client, stacklayer, c_stacking);
	else
		client = lowest;
	if (client)
		i = client->stacklayer;
	else
		i = paged->client->stacklayer + 1;
	for (; i < STACKLAYER_COUNT; i++)
		TAILQ_FOREACH_REVERSE(client, &desktop->stacking_list[i],
				stacklayer, c_stacking) {
			if (XFindContext(display, client->window, paged_context,
					(XPointer *) &lowestpaged))
				continue;

			/* found the paged immediatly above it */
			stacking_raise_under(paged->win, lowestpaged->win);
			return;
		}

	/* if no pageds are above it */
noneabove:
	XRaiseWindow(display, paged->win);
}

/*
 * handle changing the focus of paged windows.  this doesn't
 * matter unless a seperate color or pixmap was defined for the
 * focused windows, in which case we switch it here.
 */
void pager_focuspaged(paged_t *paged)
{
	struct pagerscr *thisscr;
	paged_t *oldfocus;

	PDEBUG ("pager_focuspaged entered");

	/*
	 * remove the old focused paged, and set the
	 * new one.
	 */
	oldfocus = paged_focused;
	paged_focused = paged;

	/*
	 * handle removing any changes in color or pixmap for the
	 * old focus paged.
	 */
	if (oldfocus)
	{
		thisscr = &pagerscr[oldfocus->client->screen->num];
		if (pager_winpixmap)
		{
			if (pager_winpixmap != pager_focwinpixmap)
			{
				PDEBUG ("Changing pixmap for ``%s''", oldfocus->client->store_name);

				XFreePixmap (display, oldfocus->pixmap);

				oldfocus->pixmap = pager_getpagedbg (oldfocus->client,
                                                 oldfocus->pixmap_width,
                                                 oldfocus->pixmap_height, 0);

				XSetWindowBackgroundPixmap(display, oldfocus->win, oldfocus->pixmap);
				XClearWindow(display, oldfocus->win);
			}
		}
		else
		{
			if (oldfocus->pixmap)
			{
				XFreePixmap (display, oldfocus->pixmap);
				oldfocus->pixmap = oldfocus->pixmap_width = oldfocus->pixmap_height = 0;
			}

			if (thisscr->pagedwin_color)
			{
				if (pager_focwinpixmap || (thisscr->pagedwin_color != thisscr->pagedfocwin_color))
				{
					XSetWindowBackground(display, oldfocus->win, thisscr->pagedwin_color);
					XClearWindow(display, oldfocus->win);
				}
			}
		}

		if (thisscr->pagedbdr_color && (thisscr->pagedbdr_color != thisscr->pagedfocbdr_color))
			XSetWindowBorder(display, oldfocus->win, thisscr->pagedbdr_color);
	} // if (oldfocus)

	/*
	 * set the new focused paged, and change it's background
	 * pixmap or colors if we are supposed to.
	 */
	if (!paged_focused)
		return;

	thisscr = &pagerscr[paged_focused->client->screen->num];
	if (pager_winpixmap)
	{
		if (pager_winpixmap != pager_focwinpixmap)
		{
			PDEBUG ("Changing pixmap for ``%s''", paged_focused->client->store_name);

			XFreePixmap (display, paged_focused->pixmap);

			paged_focused->pixmap = pager_getpagedbg (paged_focused->client,
                                                   paged_focused->pixmap_width,
                                                   paged_focused->pixmap_height, 1);

			XSetWindowBackgroundPixmap(display, paged_focused->win, paged_focused->pixmap);
			XClearWindow(display, paged_focused->win);
		}
	}
	else
	{
		if (paged_focused->pixmap)
		{
			XFreePixmap (display, paged_focused->pixmap);
			paged_focused->pixmap = paged_focused->pixmap_width = paged_focused->pixmap_height = 0;
		}

		if (thisscr->pagedwin_color)
		{
			if (pager_focwinpixmap || (thisscr->pagedwin_color != thisscr->pagedfocwin_color))
			{
				XSetWindowBackground(display, paged_focused->win, thisscr->pagedwin_color);
				XClearWindow(display, paged_focused->win);
			}
		}
	}

	if (thisscr->pagedbdr_color && (thisscr->pagedbdr_color != thisscr->pagedfocbdr_color))
		XSetWindowBorder(display, paged_focused->win, thisscr->pagedbdr_color);
}

/* clicks on the pager window */
void pager_click(pager_t *pager, int x, int y) {
	desktop_t *desktop;

	desktop = pager->desktop;
	x /= pager->client->screen->width * pager_ratio;
	y /= pager->client->screen->height * pager_ratio;

	/* we switch workspaces, and desktops if it's on a differnt desk */
	workspace_viewport_move(pager->client->screen, desktop, x - desktop->viewx,
		y - desktop->viewy);
	if (desktop != pager->client->screen->desktop)
		desktop_switch(pager->client->screen, desktop->num);
}

/* handle exposures */
void pager_expose(pager_t *pager, GC gc, XExposeEvent *e) {
	struct pagerscr *thisscr = &pagerscr[pager->client->screen->num];
	int x, y, wid, hei;
	int i, tmp;
	int vx, vy, vw, vh;

	/* if e is null we do expose for the whole thing */
	x = e ? e->x : 0;
	y = e ? e->y : 0;
	wid = e ? e->width : pager->client->width;
	hei = e ? e->height : pager->client->height;

	/* draw the grid if we're supposed to */
	if (pager_drawgrid) {
		XSetForeground(display, gc, thisscr->grid_color);

		for (i = 1; i < pager->desktop->width; i++) {
			tmp = i * pager->spacewidth;
			if (tmp >= x && tmp <= x + wid)
				XDrawLine(display, pager->win, gc, tmp, y, tmp, y + hei);
		}
		for (i = 1; i < pager->desktop->height; i++) {
			tmp = i * pager->spaceheight;
			if (tmp >= y && tmp <= y + hei)
				XDrawLine(display, pager->win, gc, x, tmp, x + wid, tmp);
		}
	}

	/*
	 * XXX: right now I have no decent way to indicate the
	 * current desktop in parentrelative bg mode.
	 */
	if (pager_parentrel && !thisscr->sel_img)
		return;

	/* get information about the current workspace */
	if (pager->client->screen->desktop != pager->desktop)
		return;
	vx = pager->desktop->viewx * pager->spacewidth;
	vy = pager->desktop->viewy * pager->spaceheight;
	vw = pager->spacewidth;
	vh = pager->spaceheight;

	/* offset for the grid if neccesary */
	if (pager_drawgrid) {
		if (vx)
			vx++, vw--;
		if (vy)
			vy++, vh--;
	}

	/* get constraints from the exposure */
	if (x + wid < vx || y + hei < vy
			|| x > vx + vw || y > vy + vh)
		return;
	if (x < vx)
		x = vx;
	if (x + wid > vx + vw)
		wid = vx + vw - x;
	if (y < vy)
		y = vy;
	if (y + hei > vy + vh)
		hei = vy + vh - y;

	/* draw the selection stuffs */
	if (thisscr->sel_img)
		image_put(thisscr->sel_img, pager->win, gc,
                x % pager->spacewidth, y % pager->spaceheight,
                x, y, wid, hei);
	else {
		XSetForeground(display, gc, thisscr->sel_color);
		XFillRectangle(display, pager->win, gc, x, y, wid, hei);
	}
}

/*
 * handle a dragged paged_t; we need to move the client_t frame to the new
 * location and put the client_t into it's new workspace.
 */
static void pager_dragged(pager_t *pager, paged_t *paged, int x, int y,
                          int newx, int newy, int usenewxy)
{
	client_t *client;

	/* save some typing */
	client = paged->client;

	/* find the new spot to put our client's frame, if it wasn't told to us */
	if (!usenewxy)
   {
		newx = x;
		newy = y;
		pager_pagertoscr(pager, &newx, &newy);
	}


   log( 2, "pager_dragged. x: %d y: %d", newx, newy );

	/*
	 * don't actually do anything if it didn't move, but we need to do a
	 * workspace_add_bypos still if it changed desktops.
	 */
	if (newx == client->x && newy == client->y)
   {
		if (client->workspace->desktop != pager->desktop)
			workspace_add_bypos(pager->desktop, client);
		return;
	}

	/* sizeframe will move the client, and we send a ICCCM synth-config */
	client->x = newx;
   log( 6, "^^^^^^^^^^^^^ changing clientx to: %d", client->x );

	client->y = newy;
	client_sizeframe(client);

   log( 4, "****** sending a synthetic move event *****");

	action_send_config(client);
	workspace_add_bypos(pager->desktop, client);

   // bva ... gotta let those plugins know!
   plugin_geometry_change(client);

}

/*
 * main dragging routine for moving paged_t's.  paged_t's need to be
 * dragable between desktops, as well as workspaces.
 */
void pager_drag(pager_t *origpager, paged_t *paged, XButtonEvent *ev) {
	XEvent event;
	pager_t *pager;
	client_t *client;
	Window dumwin;
	long eventmask;
	int ptx, pty;
	int tmpx, tmpy;
	int oldx, oldy;
	int haveold = 0;
	int i;

	/* don't move nomove clients */
	if (paged->client->flags.nomove)
		return;

	/* set up some vars */
	pager = origpager;
	client = paged->client;
	eventmask = PointerMotionMask | ButtonMotionMask | ButtonReleaseMask;
	if (!XTranslateCoordinates(display, pager->win, paged->win, ev->x, ev->y,
			&ptx, &pty, &dumwin))
		return;

	/* obtain pointer grab */
	if (XGrabPointer(display, client->screen->root, 0, eventmask, GrabModeAsync, GrabModeAsync,
			client->screen->root, cursor_move, CurrentTime) != GrabSuccess)
		return;
	eventmask |= ExposureMask;

	/* setup for the opaque or nonopaque movement */
	if (options.opaquemove)
		stacking_raise(client);
	else if (pager->desktop == client->screen->desktop) {
		XUnmapWindow(display, client->frame);
		XGrabServer(display);
		oldx = ev->x - ptx;
		oldy = ev->y - pty;
		pager_pagertoscr(pager, &oldx, &oldy);
		draw_winbox(client->screen, client, oldx, oldy, client->width, client->height);
		haveold = 1;
	}

loop:
	/* dragging event loop */
	while (1) {
		XMaskEvent(display, eventmask, &event);

		switch (event.type) {
		case MotionNotify:
			/* handle dragging in a pager, else on the root */
			if (pager) {
				/* translate coordinates to relative to the pager window */
				tmpx = event.xmotion.x_root - pager->client->x
					- pager->client->dgroup->left_space;
				tmpy = event.xmotion.y_root - pager->client->y
					- pager->client->dgroup->top_space;

				/* erase the old winbox before moving the paged_t window  */
				if (!options.opaquemove && haveold && pager->desktop == client->screen->desktop)
					draw_winbox(client->screen, client, oldx, oldy, client->width, client->height);

				/* see if they are dragging it out of a pager now */
				if (tmpx > pager->client->width || tmpx < 0
						|| tmpy > pager->client->height || tmpy < 0) {
					XReparentWindow(display, paged->win, client->screen->root,
						event.xmotion.x_root - ptx, event.xmotion.y_root - pty);
					pager = NULL;
					if (options.opaquemove)
						XUnmapWindow(display, client->frame);
					else
						XUngrabServer(display);
					continue;
				}

				/* else we move it inside of the current pager */
				XMoveWindow(display, paged->win, tmpx - ptx, tmpy - pty);

				/* draw the winbox or move the client window */
				if (pager->desktop != client->screen->desktop)
					continue;
				tmpx -= ptx;
				tmpy -= pty;
				pager_pagertoscr(pager, &tmpx, &tmpy);
				if (!options.opaquemove) {
					draw_winbox(client->screen, client, tmpx, tmpy,
						client->width, client->height);

					oldx = tmpx; oldy = tmpy;
					haveold = 1;
				} else
					XMoveWindow(display, client->frame, tmpx, tmpy);
			} else {
				/*
				 * check if it is being dragged into a pager
				 * XXX: unfortunatly because I'm moving the paged->win around
				 * with the cursor, subwindow always is the paged->win, so I have
				 * to do this mess.
				 */
				for (i = 0; i < pagerscr[client->screen->num].pager_count; i++) {
					/* get coords relative to the pager window */
					tmpx = event.xmotion.x_root - pagerscr[client->screen->num].pagers[i]->client->x
						- pagerscr[client->screen->num].pagers[i]->client->dgroup->left_space;
					tmpy = event.xmotion.y_root - pagerscr[client->screen->num].pagers[i]->client->y
						- pagerscr[client->screen->num].pagers[i]->client->dgroup->top_space;

					/* now check if it's inside */
					if (tmpx <= pagerscr[client->screen->num].pagers[i]->client->width
							&& tmpy <= pagerscr[client->screen->num].pagers[i]->client->height
							&& tmpx >= 0 && tmpy >= 0) {
						pager = pagerscr[client->screen->num].pagers[i];
						XReparentWindow(display, paged->win, pager->win, tmpx - ptx, tmpy - pty);
						if (pager->desktop == client->screen->desktop) {
							if (!options.opaquemove) {
								XGrabServer(display);
								haveold = 0;
							} else
								XMapWindow(display, client->frame);
						}
						goto loop;
					}
				}

				/* move it to follow cursor */
				XMoveWindow(display, paged->win, event.xmotion.x_root - ptx, event.xmotion.y_root - pty);
			}
			break;
		case Expose:
			/*
			 * XXX: erase lines before handling exposures in nonopaque move
			 * mode, then redraw them afterward.
			 */
			if (!options.opaquemove && haveold && pager
					&& pager->desktop == client->screen->desktop)
				draw_winbox(client->screen, client, oldx,
					oldy, client->width, client->height);
			event_handle(&event);
			if (!options.opaquemove && haveold && pager
					&& pager->desktop == client->screen->desktop)
				draw_winbox(client->screen, client, oldx,
					oldy, client->width, client->height);
			break;
		case ButtonRelease:
			goto done;
		}
	}

done:
	/* release grab */
	XUngrabPointer(display, CurrentTime);

	/*
	 * if they aren't in a pager, we drop the client where the cursor,
	 * otherwise we place the client based on the paged_t win in the pager.
	 * the client may need to be moved across desktops.
	 */
	if (!pager)
   {
      log( 4, "!pager target");
		/* find which pager to be using, and if neccesary move it there */
		pager = pagerscr[client->screen->num].pagers[client->screen->desktop->num];
		if (origpager != pager)
			pager_movepaged(origpager, paged, pager, 0);

		/* we need to put the paged_t window back, and map the client frame */
		XReparentWindow(display, paged->win, pager->win, 0, 0);

		/* drag the stuff and size the paged */
		pager_dragged( pager, paged, tmpx - ptx, tmpy - pty, event.xbutton.x_root,
                     event.xbutton.y_root, 1);

		XMapWindow(display, client->frame);
		pager_sizepaged(pager, paged);
	}
   else
   {
      log( 4, "pager is valid target");
      // bva. we are inside another pager window ...

		/* translate coordinates to relative to the pager window */
		tmpx = event.xmotion.x_root - pager->client->x
          - pager->client->dgroup->left_space;
		tmpy = event.xmotion.y_root - pager->client->y
          - pager->client->dgroup->top_space;

		/* move paged around */
		if (pager != origpager)
      {
         log( 4, "pager isn't the original pager" );
			pager_movepaged(origpager, paged, pager, 0);
      }

		if (!options.opaquemove && pager->desktop == client->screen->desktop)
      {
			draw_winbox(client->screen, client, oldx, oldy, client->width, client->height);
			XUngrabServer(display);
		}

		/* drag the stuff */
		pager_dragged(pager, paged, tmpx - ptx, tmpy - pty, 0, 0, 0);

		/* map after moving it properly if not opaque */
		if (pager->desktop == client->screen->desktop && !options.opaquemove)
      {
			XMapWindow(display, client->frame);
			stacking_raise(client);
		}
	}

	/* focus the client_t they were dragging */
	focus_setfocused(client);
}
