/*-
 * 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;

/* 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 pager_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;

	/*
	 * 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;
	}
	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;
	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(screen_t *screen, int width,
		int height, int foc) {
	image_t *image, *scaled;
	Pixmap pixmap;

	/* if a window is too small, just skip */
	if (width <= 0 || height <= 0)
		return None;

	/* scale the paged window bg pixmap */
	pixmap = XCreatePixmap(display, screen->root, width, height,
		DefaultDepth(display, screen->num));
	image = foc ? image_frompixmap(pager_focwinpixmap, screen)
		: image_frompixmap(pager_winpixmap, 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;
}

/* add a paged client to a pager */
void pager_addpaged(pager_t *pager, client_t *client) {
	XSetWindowAttributes attr;
	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->last_width = wid = client->width * pager_ratio;
	paged->last_height = hei = client->height * pager_ratio;
	pager_scrtopager(pager, &x, &y);

	/* create the mini window */
	if (pager_winpixmap) {
		attr.background_pixmap = pager_winscale
			? pager_getpagedbg(client->screen, wid, hei, 0)
			: pager_winpixmap->pixmaps[client->screen->num];
		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);
	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);

	/* 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;

	/* 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 we have a pager_winpixmap and pager_winscale, we
	 * need to rescale the image if the paged_t window
	 * changed size.
	 */
	if (pager_winpixmap && (paged->last_width != wid || paged->last_height != hei)) {
		if (paged_focused == paged) {
			if (pager_focwinpixmap && pager_focwinscale)
				XSetWindowBackgroundPixmap(display, paged->win,
					pager_getpagedbg(pager->client->screen, wid, hei, 1));
		} else if (pager_winscale)
			XSetWindowBackgroundPixmap(display, paged->win,
				pager_getpagedbg(pager->client->screen, wid, hei, 0));
	}
	paged->last_width = wid;
	paged->last_height = hei;

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

/*
 * 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;

	/*
	 * 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_focwinpixmap != pager_winpixmap) {
			XSetWindowBackgroundPixmap(display, oldfocus->win, pager_winscale
				? pager_getpagedbg(oldfocus->client->screen,
				oldfocus->last_width, oldfocus->last_height, 0)
				: pager_winpixmap->pixmaps[oldfocus->client->screen->num]);
			XClearWindow(display, oldfocus->win);
		} else if (thisscr->pagedfocwin_color != thisscr->pagedwin_color) {
			XSetWindowBackground(display, oldfocus->win,
				thisscr->pagedwin_color);
			XClearWindow(display, oldfocus->win);
		}
		if (thisscr->pagedfocbdr_color != thisscr->pagedbdr_color)
			XSetWindowBorder(display, oldfocus->win,
				thisscr->pagedbdr_color);
	}

	/*
	 * 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_focwinpixmap != pager_winpixmap) {
		XSetWindowBackgroundPixmap(display, paged_focused->win, pager_focwinscale
			? pager_getpagedbg(paged_focused->client->screen,
			paged_focused->last_width, paged_focused->last_height, 1)
			: pager_focwinpixmap->pixmaps[paged_focused->client->screen->num]);
		XClearWindow(display, paged_focused->win);
	} else if (thisscr->pagedfocwin_color != thisscr->pagedwin_color) {
		XSetWindowBackground(display, paged_focused->win,
			thisscr->pagedfocwin_color);
		XClearWindow(display, paged_focused->win);
	}
	if (thisscr->pagedfocbdr_color != thisscr->pagedbdr_color)
		XSetWindowBorder(display, paged_focused->win,
			thisscr->pagedfocbdr_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);
	}

	/*
	 * 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;
	client->y = newy;
	client_sizeframe(client);
	action_send_config(client);
	workspace_add_bypos(pager->desktop, 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) {
		/* 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 {
		/* 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)
			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);
}
