/* action.c- misc. window commands (miniaturize, hide etc.)
 *
 *  Window Maker window manager
 *
 *  Copyright (c) 1997-2003 Alfredo K. Kojima
 *  Copyright (c) 1998-2003 Dan Pascu
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
 *  USA.
 */

#include "wconfig.h"

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <math.h>
#include <time.h>

#include "WindowMaker.h"
#include "wcore.h"
#include "framewin.h"
#include "window.h"
#include "client.h"
#include "icon.h"
#include "funcs.h"
#include "application.h"
#include "actions.h"
#include "stacking.h"
#include "appicon.h"
#include "dock.h"
#include "appmenu.h"
#include "winspector.h"
#include "workspace.h"
#include "wsound.h"
#include "xinerama.h"

/****** Global Variables ******/
extern Time LastTimestamp;
extern Time LastFocusChange;

extern Cursor wCursor[WCUR_LAST];

extern WPreferences wPreferences;

extern Atom _XA_WM_TAKE_FOCUS;

extern void ProcessPendingEvents();
extern int calcIntersectionLength(int p1, int l1, int p2, int l2);

/******* Local Variables *******/
static struct {
	int steps;
	int delay;
} shadePars[5] = {
	{
	SHADE_STEPS_UF, SHADE_DELAY_UF}, {
	SHADE_STEPS_F, SHADE_DELAY_F}, {
	SHADE_STEPS_M, SHADE_DELAY_M}, {
	SHADE_STEPS_S, SHADE_DELAY_S}, {
SHADE_STEPS_US, SHADE_DELAY_US}};

#define UNSHADE         0
#define SHADE           1
#define SHADE_STEPS	shadePars[(int)wPreferences.shade_speed].steps
#define SHADE_DELAY	shadePars[(int)wPreferences.shade_speed].delay

static int compareTimes(Time t1, Time t2)
{
	Time diff;
	if (t1 == t2)
		return 0;
	diff = t1 - t2;
	return (diff < 60000) ? 1 : -1;
}

/*
 *----------------------------------------------------------------------
 * wSetFocusTo--
 * 	Changes the window focus to the one passed as argument.
 * If the window to focus is not already focused, it will be brought
 * to the head of the list of windows. Previously focused window is
 * unfocused.
 *
 * Side effects:
 * 	Window list may be reordered and the window focus is changed.
 *
 *----------------------------------------------------------------------
 */
void wSetFocusTo(WScreen * scr, WWindow * wwin)
{
	static WScreen *old_scr = NULL;

	WWindow *old_focused;
	WWindow *focused = scr->focused_window;
	Time timestamp = LastTimestamp;
	WApplication *oapp = NULL, *napp = NULL;
	int wasfocused;

	if (scr->flags.ignore_focus_events || compareTimes(LastFocusChange, timestamp) > 0)
		return;

	if (!old_scr)
		old_scr = scr;
	old_focused = old_scr->focused_window;

	LastFocusChange = timestamp;

	if (old_focused)
		oapp = wApplicationOf(old_focused->main_window);

	if (wwin == NULL) {
		XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
		if (old_focused) {
			wWindowUnfocus(old_focused);
		}
		if (oapp) {
			wAppMenuUnmap(oapp->menu);
#ifdef NEWAPPICON
			wApplicationDeactivate(oapp);
#endif
		}

		WMPostNotificationName(WMNChangedFocus, NULL, (void *)True);
		return;
	} else if (old_scr != scr && old_focused) {
		wWindowUnfocus(old_focused);
	}

	wasfocused = wwin->flags.focused;
	napp = wApplicationOf(wwin->main_window);

	/* remember last workspace where the app has been */
	if (napp) {
		/*napp->last_workspace = wwin->screen_ptr->current_workspace; */
		napp->last_workspace = wwin->frame->workspace;
	}

	if (wwin->flags.mapped && !WFLAGP(wwin, no_focusable)) {
		/* install colormap if colormap mode is lock mode */
		if (wPreferences.colormap_mode == WCM_CLICK)
			wColormapInstallForWindow(scr, wwin);

		/* set input focus */
		switch (wwin->focus_mode) {
		case WFM_NO_INPUT:
			XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
			break;

		case WFM_PASSIVE:
		case WFM_LOCALLY_ACTIVE:
			XSetInputFocus(dpy, wwin->client_win, RevertToParent, CurrentTime);
			break;

		case WFM_GLOBALLY_ACTIVE:
			break;
		}
		XFlush(dpy);
		if (wwin->protocols.TAKE_FOCUS) {
			wClientSendProtocol(wwin, _XA_WM_TAKE_FOCUS, timestamp);
		}
		XSync(dpy, False);
	} else {
		XSetInputFocus(dpy, scr->no_focus_win, RevertToParent, CurrentTime);
	}
	if (WFLAGP(wwin, no_focusable))
		return;

	/* if this is not the focused window focus it */
	if (focused != wwin) {
		/* change the focus window list order */
		if (wwin->prev)
			wwin->prev->next = wwin->next;

		if (wwin->next)
			wwin->next->prev = wwin->prev;

		wwin->prev = focused;
		focused->next = wwin;
		wwin->next = NULL;
		scr->focused_window = wwin;

		if (oapp && oapp != napp) {
			wAppMenuUnmap(oapp->menu);
#ifdef NEWAPPICON
			wApplicationDeactivate(oapp);
#endif
		}
	}

	wWindowFocus(wwin, focused);

	if (napp && !wasfocused) {
#ifdef USER_MENU
		wUserMenuRefreshInstances(napp->menu, wwin);
#endif				/* USER_MENU */

		if (wwin->flags.mapped)
			wAppMenuMap(napp->menu, wwin);
#ifdef NEWAPPICON
		wApplicationActivate(napp);
#endif
	}

	XFlush(dpy);
	old_scr = scr;
}

void wShadeWindow(WWindow *wwin)
{

	if (wwin->flags.shaded)
		return;

	XLowerWindow(dpy, wwin->client_win);
	wSoundPlay(WSOUND_SHADE);
	shade_animate(wwin, SHADE);

	wwin->flags.skip_next_animation = 0;
	wwin->flags.shaded = 1;
	wwin->flags.mapped = 0;
	/* prevent window withdrawal when getting UnmapNotify */
	XSelectInput(dpy, wwin->client_win, wwin->event_mask & ~StructureNotifyMask);
	XUnmapWindow(dpy, wwin->client_win);
	XSelectInput(dpy, wwin->client_win, wwin->event_mask);

	/* for the client it's just like iconification */
	wFrameWindowResize(wwin->frame, wwin->frame->core->width, wwin->frame->top_width - 1);

	wwin->client.y = wwin->frame_y - wwin->client.height + wwin->frame->top_width;
	wWindowSynthConfigureNotify(wwin);

	/*
	   wClientSetState(wwin, IconicState, None);
	 */

	WMPostNotificationName(WMNChangedState, wwin, "shade");

#ifdef ANIMATIONS
	if (!wwin->screen_ptr->flags.startup) {
		/* Catch up with events not processed while animation was running */
		ProcessPendingEvents();
	}
#endif
}

void wUnshadeWindow(WWindow *wwin)
{

	if (!wwin->flags.shaded)
		return;

	wwin->flags.shaded = 0;
	wwin->flags.mapped = 1;
	XMapWindow(dpy, wwin->client_win);

	wSoundPlay(WSOUND_UNSHADE);
	shade_animate(wwin, UNSHADE);

	wwin->flags.skip_next_animation = 0;
	wFrameWindowResize(wwin->frame, wwin->frame->core->width,
			   wwin->frame->top_width + wwin->client.height + wwin->frame->bottom_width);

	wwin->client.y = wwin->frame_y + wwin->frame->top_width;
	wWindowSynthConfigureNotify(wwin);

	/*
	   wClientSetState(wwin, NormalState, None);
	 */
	/* if the window is focused, set the focus again as it was disabled during
	 * shading */
	if (wwin->flags.focused)
		wSetFocusTo(wwin->screen_ptr, wwin);

	WMPostNotificationName(WMNChangedState, wwin, "shade");
}

/* Set the old coordinates using the current values */
static void save_old_geometry(WWindow *wwin)
{
	wwin->old_geometry.width = wwin->client.width;
	wwin->old_geometry.height = wwin->client.height;
	wwin->old_geometry.x = wwin->frame_x;
	wwin->old_geometry.y = wwin->frame_y;
}

void wMaximizeWindow(WWindow * wwin, int directions)
{
	int new_x, new_y;
	unsigned int new_width, new_height, half_scr_width;
	WArea usableArea, totalArea;

	if (!IS_RESIZABLE(wwin))
		return;

	/* save old coordinates before we change the current values */
	save_old_geometry(wwin);

	totalArea.x1 = 0;
	totalArea.y1 = 0;
	totalArea.x2 = wwin->screen_ptr->scr_width;
	totalArea.y2 = wwin->screen_ptr->scr_height;
	usableArea = totalArea;

	if (!(directions & MAX_IGNORE_XINERAMA)) {
		WScreen *scr = wwin->screen_ptr;
		int head;

		if (directions & MAX_KEYBOARD)
			head = wGetHeadForWindow(wwin);
		else
			head = wGetHeadForPointerLocation(scr);

		usableArea = wGetUsableAreaForHead(scr, head, &totalArea, True);
	}

	/* Only save directions, not kbd or xinerama hints */
	directions &= (MAX_HORIZONTAL | MAX_VERTICAL | MAX_LEFTHALF | MAX_RIGHTHALF | MAX_MAXIMUS);

	if (WFLAGP(wwin, full_maximize)) {
		usableArea = totalArea;
	}
	half_scr_width = (usableArea.x2 - usableArea.x1)/2;

	if (wwin->flags.shaded) {
		wwin->flags.skip_next_animation = 1;
		wUnshadeWindow(wwin);
	}

	if (directions & MAX_HORIZONTAL) {
		new_width = usableArea.x2 - usableArea.x1;
		if (HAS_BORDER(wwin))
			new_width -= FRAME_BORDER_WIDTH * 2;
		new_x = usableArea.x1;
	} else if (directions & MAX_LEFTHALF) {
		new_width = half_scr_width;
		if (HAS_BORDER(wwin))
			new_width -= FRAME_BORDER_WIDTH * 2;
		new_x = usableArea.x1;
	} else if (directions & MAX_RIGHTHALF) {
		new_width = half_scr_width;
		if (HAS_BORDER(wwin))
			new_width -= FRAME_BORDER_WIDTH * 2;
		new_x = usableArea.x1 + half_scr_width;
	} else {
		new_x = wwin->frame_x;
		new_width = wwin->frame->core->width;
	}

	if (directions & MAX_VERTICAL) {
		new_height = usableArea.y2 - usableArea.y1;
		if (HAS_BORDER(wwin))
			new_height -= FRAME_BORDER_WIDTH * 2;
		new_y = usableArea.y1;
		if (WFLAGP(wwin, full_maximize)) {
			new_y -= wwin->frame->top_width;
			new_height += wwin->frame->bottom_width - 1;
		}
	} else {
		new_y = wwin->frame_y;
		new_height = wwin->frame->core->height;
	}

	if (!WFLAGP(wwin, full_maximize)) {
		new_height -= wwin->frame->top_width + wwin->frame->bottom_width;
	}

	if (directions & MAX_MAXIMUS)
		find_Maximus_geometry(wwin, usableArea, &new_x, &new_y, &new_width, &new_height);

	wWindowConstrainSize(wwin, &new_width, &new_height);

	wWindowCropSize(wwin, usableArea.x2 - usableArea.x1,
			usableArea.y2 - usableArea.y1, &new_width, &new_height);

	wWindowConfigure(wwin, new_x, new_y, new_width, new_height);

	WMPostNotificationName(WMNChangedState, wwin, "maximize");

	wSoundPlay(WSOUND_MAXIMIZE);
	/* set maximization state */
	wwin->flags.maximized = directions;
}

/*
 * Maximus: tiled maximization (maximize without overlapping other windows)
 *
 * The window to be maximized will be denoted by w_0 (sub-index zero)
 * while the windows which will stop the maximization of w_0 are denoted by w_j.
 */
static void find_Maximus_geometry(WWindow *wwin, WArea usableArea, int *new_x, int *new_y,
				  unsigned int *new_width, unsigned int *new_height)
{
	WWindow *tmp;
	int x_0            = wwin->frame_x;
	int y_0            = wwin->frame_y;
	int width_0        = wwin->frame->core->width;
	int height_0       = wwin->frame->core->height;
	int botton_0       = y_0 + height_0;
	int right_border_0 = x_0 + width_0;
	int new_x_0, new_y_0, new_botton_0, new_right_border_0, new_height_0;
	int x_j,  y_j, width_j, height_j, botton_j, top_j, right_border_j;
	int x_intsect, y_intsect;
	/* Assume that the window w_0 has titlebar etc */
	int has_titlebar = 1, has_resizebar = 1, has_border = 1;
	int adjust_height, adjust_width;

	/* Try to fully maximize first, then readjust later */
	new_x_0            = usableArea.x1;
	new_y_0            = usableArea.y1;
	new_botton_0       = usableArea.y2;
	new_right_border_0 = usableArea.x2;

	if (!HAS_TITLEBAR(wwin))
		has_titlebar = 0;
	if (!HAS_RESIZEBAR(wwin))
		has_resizebar = 0;
	if (!HAS_BORDER(wwin))
		has_border = 0;

	/* the lengths to be subtracted if w_0 has titlebar, etc */
	adjust_height = TITLEBAR_HEIGHT * has_titlebar
		+ 2 * FRAME_BORDER_WIDTH * has_border + RESIZEBAR_HEIGHT * has_resizebar;
	adjust_width = 2 * FRAME_BORDER_WIDTH * has_border;

	tmp = wwin;
	/* TODO: Is the focused window always the last in the list? */
	while (tmp->prev) {
		/* ignore windows in other workspaces or minimized */
		if (tmp->prev->frame->workspace != wwin->screen_ptr->current_workspace
		    || tmp->prev->flags.miniaturized) {
			tmp = tmp->prev;
			continue;
		}
		tmp = tmp->prev;

		/* set the w_j window coordinates */
		x_j = tmp->frame_x;
		y_j = tmp->frame_y;
		width_j = tmp->frame->core->width;
		height_j = tmp->frame->core->height;
		botton_j = y_j + height_j;
		top_j = y_j;
		right_border_j = x_j + width_j;

		/* Try to maximize in the y direction first */
		x_intsect = calcIntersectionLength(x_0, width_0, x_j, width_j);
		if (x_intsect != 0) {
			if (botton_j < y_0 && botton_j > new_y_0) {
				/* w_0 is below the botton of w_j */
				new_y_0 = botton_j;
			}
			if (botton_0 < top_j && top_j < new_botton_0) {
				/* The botton of w_0 is above the top of w_j */
				new_botton_0 = top_j;
			}
		}

		/*
		 * Use the updated y coordinates from the above step to account
		 * the possibility that the new value of y_0 will have different
		 * intersections with w_j
		 */
		new_height_0 = new_botton_0 - new_y_0 - adjust_height;
		y_intsect = calcIntersectionLength(new_y_0, new_height_0, y_j, height_j);
		if (y_intsect != 0) {
			if (right_border_j < x_0 && right_border_j > new_x_0) {
				/* w_0 is completely to the right of w_j */
				new_x_0 = right_border_j;
			}
			if (right_border_0 < x_j && x_j < new_right_border_0) {
				/* w_0 is completely to the left of w_j */
				new_right_border_0 = x_j;
			}
		}
	}

	new_height_0 = new_botton_0 - new_y_0 - adjust_height;
	*new_x = new_x_0;
	*new_y = new_y_0;
	*new_height = new_height_0;
	*new_width = new_right_border_0 - new_x_0 - adjust_width;
}

void wUnmaximizeWindow(WWindow * wwin)
{
	int x, y, w, h;
        WMRect old_geom_rect;
        int old_head;
	Bool same_head;

	if (!wwin->flags.maximized)
		return;

	if (wwin->flags.shaded) {
		wwin->flags.skip_next_animation = 1;
		wUnshadeWindow(wwin);
	}
	/* Use old coordinates if they are set, current values otherwise */
        old_geom_rect = wmkrect(wwin->old_geometry.x, wwin->old_geometry.y, wwin->old_geometry.width, wwin->old_geometry.height);
	old_head = wGetHeadForRect(wwin->screen_ptr, old_geom_rect);
	same_head = (wGetHeadForWindow(wwin) == old_head);
	x = (wwin->old_geometry.x && same_head) ? wwin->old_geometry.x : wwin->frame_x;
	y = (wwin->old_geometry.y && same_head) ? wwin->old_geometry.y : wwin->frame_y;
	w = wwin->old_geometry.width ? wwin->old_geometry.width : wwin->client.width;
	h = wwin->old_geometry.height ? wwin->old_geometry.height : wwin->client.height;

	wwin->flags.maximized = 0;
	wWindowConfigure(wwin, x, y, w, h);

	WMPostNotificationName(WMNChangedState, wwin, "maximize");

	wSoundPlay(WSOUND_UNMAXIMIZE);
}

void wFullscreenWindow(WWindow * wwin)
{
	int head;
	WMRect rect;

	if (wwin->flags.fullscreen)
		return;

	wwin->flags.fullscreen = True;

	wWindowConfigureBorders(wwin);

	ChangeStackingLevel(wwin->frame->core, WMFullscreenLevel);

	wwin->bfs_geometry.x = wwin->frame_x;
	wwin->bfs_geometry.y = wwin->frame_y;
	wwin->bfs_geometry.width = wwin->frame->core->width;
	wwin->bfs_geometry.height = wwin->frame->core->height;

	head = wGetHeadForWindow(wwin);
	rect = wGetRectForHead(wwin->screen_ptr, head);
	wWindowConfigure(wwin, rect.pos.x, rect.pos.y, rect.size.width, rect.size.height);

	WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
}

void wUnfullscreenWindow(WWindow * wwin)
{
	if (!wwin->flags.fullscreen)
		return;

	wwin->flags.fullscreen = False;

	if (wwin->frame->core->stacking->window_level == WMFullscreenLevel) {
		if (WFLAGP(wwin, sunken)) {
			ChangeStackingLevel(wwin->frame->core, WMSunkenLevel);
		} else if (WFLAGP(wwin, floating)) {
			ChangeStackingLevel(wwin->frame->core, WMFloatingLevel);
		} else {
			ChangeStackingLevel(wwin->frame->core, WMNormalLevel);
		}
	}

	wWindowConfigure(wwin, wwin->bfs_geometry.x, wwin->bfs_geometry.y,
			 wwin->bfs_geometry.width, wwin->bfs_geometry.height);

	wWindowConfigureBorders(wwin);
	/*
	   // seems unnecessary, but also harmless (doesn't generate flicker) -Dan
	   wFrameWindowPaint(wwin->frame);
	 */

	WMPostNotificationName(WMNChangedState, wwin, "fullscreen");
}

#ifdef ANIMATIONS
static void animateResizeFlip(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
{
#define FRAMES (MINIATURIZE_ANIMATION_FRAMES_F)
	float cx, cy, cw, ch;
	float xstep, ystep, wstep, hstep;
	XPoint points[5];
	float dx, dch, midy;
	float angle, final_angle, delta;

	xstep = (float)(fx - x) / steps;
	ystep = (float)(fy - y) / steps;
	wstep = (float)(fw - w) / steps;
	hstep = (float)(fh - h) / steps;

	cx = (float)x;
	cy = (float)y;
	cw = (float)w;
	ch = (float)h;

	final_angle = 2 * WM_PI * MINIATURIZE_ANIMATION_TWIST_F;
	delta = (float)(final_angle / FRAMES);
	for (angle = 0;; angle += delta) {
		if (angle > final_angle)
			angle = final_angle;

		dx = (cw / 10) - ((cw / 5) * sin(angle));
		dch = (ch / 2) * cos(angle);
		midy = cy + (ch / 2);

		points[0].x = cx + dx;
		points[0].y = midy - dch;
		points[1].x = cx + cw - dx;
		points[1].y = points[0].y;
		points[2].x = cx + cw + dx;
		points[2].y = midy + dch;
		points[3].x = cx - dx;
		points[3].y = points[2].y;
		points[4].x = points[0].x;
		points[4].y = points[0].y;

		XGrabServer(dpy);
		XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
		XFlush(dpy);
#if (MINIATURIZE_ANIMATION_DELAY_F > 0)
		wusleep(MINIATURIZE_ANIMATION_DELAY_F);
#else
		wusleep(10);
#endif

		XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
		XUngrabServer(dpy);
		cx += xstep;
		cy += ystep;
		cw += wstep;
		ch += hstep;
		if (angle >= final_angle)
			break;

	}
	XFlush(dpy);
}

#undef FRAMES

static void
animateResizeTwist(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
{
#define FRAMES (MINIATURIZE_ANIMATION_FRAMES_T)
	float cx, cy, cw, ch;
	float xstep, ystep, wstep, hstep;
	XPoint points[5];
	float angle, final_angle, a, d, delta;

	x += w / 2;
	y += h / 2;
	fx += fw / 2;
	fy += fh / 2;

	xstep = (float)(fx - x) / steps;
	ystep = (float)(fy - y) / steps;
	wstep = (float)(fw - w) / steps;
	hstep = (float)(fh - h) / steps;

	cx = (float)x;
	cy = (float)y;
	cw = (float)w;
	ch = (float)h;

	final_angle = 2 * WM_PI * MINIATURIZE_ANIMATION_TWIST_T;
	delta = (float)(final_angle / FRAMES);
	for (angle = 0;; angle += delta) {
		if (angle > final_angle)
			angle = final_angle;

		a = atan(ch / cw);
		d = sqrt((cw / 2) * (cw / 2) + (ch / 2) * (ch / 2));

		points[0].x = cx + cos(angle - a) * d;
		points[0].y = cy + sin(angle - a) * d;
		points[1].x = cx + cos(angle + a) * d;
		points[1].y = cy + sin(angle + a) * d;
		points[2].x = cx + cos(angle - a + WM_PI) * d;
		points[2].y = cy + sin(angle - a + WM_PI) * d;
		points[3].x = cx + cos(angle + a + WM_PI) * d;
		points[3].y = cy + sin(angle + a + WM_PI) * d;
		points[4].x = cx + cos(angle - a) * d;
		points[4].y = cy + sin(angle - a) * d;
		XGrabServer(dpy);
		XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
		XFlush(dpy);
#if (MINIATURIZE_ANIMATION_DELAY_T > 0)
		wusleep(MINIATURIZE_ANIMATION_DELAY_T);
#else
		wusleep(10);
#endif

		XDrawLines(dpy, scr->root_win, scr->frame_gc, points, 5, CoordModeOrigin);
		XUngrabServer(dpy);
		cx += xstep;
		cy += ystep;
		cw += wstep;
		ch += hstep;
		if (angle >= final_angle)
			break;

	}
	XFlush(dpy);
}

#undef FRAMES

static void animateResizeZoom(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int steps)
{
#define FRAMES (MINIATURIZE_ANIMATION_FRAMES_Z)
	float cx[FRAMES], cy[FRAMES], cw[FRAMES], ch[FRAMES];
	float xstep, ystep, wstep, hstep;
	int i, j;

	xstep = (float)(fx - x) / steps;
	ystep = (float)(fy - y) / steps;
	wstep = (float)(fw - w) / steps;
	hstep = (float)(fh - h) / steps;

	for (j = 0; j < FRAMES; j++) {
		cx[j] = (float)x;
		cy[j] = (float)y;
		cw[j] = (float)w;
		ch[j] = (float)h;
	}
	XGrabServer(dpy);
	for (i = 0; i < steps; i++) {
		for (j = 0; j < FRAMES; j++) {
			XDrawRectangle(dpy, scr->root_win, scr->frame_gc,
				       (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
		}
		XFlush(dpy);
#if (MINIATURIZE_ANIMATION_DELAY_Z > 0)
		wusleep(MINIATURIZE_ANIMATION_DELAY_Z);
#else
		wusleep(10);
#endif
		for (j = 0; j < FRAMES; j++) {
			XDrawRectangle(dpy, scr->root_win, scr->frame_gc,
				       (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
			if (j < FRAMES - 1) {
				cx[j] = cx[j + 1];
				cy[j] = cy[j + 1];
				cw[j] = cw[j + 1];
				ch[j] = ch[j + 1];
			} else {
				cx[j] += xstep;
				cy[j] += ystep;
				cw[j] += wstep;
				ch[j] += hstep;
			}
		}
	}

	for (j = 0; j < FRAMES; j++) {
		XDrawRectangle(dpy, scr->root_win, scr->frame_gc, (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
	}
	XFlush(dpy);
#if (MINIATURIZE_ANIMATION_DELAY_Z > 0)
	wusleep(MINIATURIZE_ANIMATION_DELAY_Z);
#else
	wusleep(10);
#endif
	for (j = 0; j < FRAMES; j++) {
		XDrawRectangle(dpy, scr->root_win, scr->frame_gc, (int)cx[j], (int)cy[j], (int)cw[j], (int)ch[j]);
	}

	XUngrabServer(dpy);
}

#undef FRAMES

void animateResize(WScreen * scr, int x, int y, int w, int h, int fx, int fy, int fw, int fh, int hiding)
{
	int style = wPreferences.iconification_style;	/* Catch the value */
	int steps, k;

	if (style == WIS_NONE)
		return;

	if (style == WIS_RANDOM) {
		style = rand() % 3;
	}

	k = (hiding ? 2 : 3);

	switch (style) {
	case WIS_TWIST:
		steps = (MINIATURIZE_ANIMATION_STEPS_T * k) / 3;
		if (steps > 0)
			animateResizeTwist(scr, x, y, w, h, fx, fy, fw, fh, steps);
		break;
	case WIS_FLIP:
		steps = (MINIATURIZE_ANIMATION_STEPS_F * k) / 3;
		if (steps > 0)
			animateResizeFlip(scr, x, y, w, h, fx, fy, fw, fh, steps);
		break;
	case WIS_ZOOM:
	default:
		steps = (MINIATURIZE_ANIMATION_STEPS_Z * k) / 3;
		if (steps > 0)
			animateResizeZoom(scr, x, y, w, h, fx, fy, fw, fh, steps);
		break;
	}
}
#endif				/* ANIMATIONS */

static void flushExpose()
{
	XEvent tmpev;

	while (XCheckTypedEvent(dpy, Expose, &tmpev))
		WMHandleEvent(&tmpev);
	XSync(dpy, 0);
}

static void unmapTransientsFor(WWindow * wwin)
{
	WWindow *tmp;

	tmp = wwin->screen_ptr->focused_window;
	while (tmp) {
		/* unmap the transients for this transient */
		if (tmp != wwin && tmp->transient_for == wwin->client_win
		    && (tmp->flags.mapped || wwin->screen_ptr->flags.startup || tmp->flags.shaded)) {
			unmapTransientsFor(tmp);
			tmp->flags.miniaturized = 1;
			if (!tmp->flags.shaded) {
				wWindowUnmap(tmp);
			} else {
				XUnmapWindow(dpy, tmp->frame->core->window);
			}
			/*
			   if (!tmp->flags.shaded)
			 */
			wClientSetState(tmp, IconicState, None);

			WMPostNotificationName(WMNChangedState, tmp, "iconify-transient");
		}
		tmp = tmp->prev;
	}
}

static void mapTransientsFor(WWindow * wwin)
{
	WWindow *tmp;

	tmp = wwin->screen_ptr->focused_window;
	while (tmp) {
		/* recursively map the transients for this transient */
		if (tmp != wwin && tmp->transient_for == wwin->client_win && /*!tmp->flags.mapped */ tmp->flags.miniaturized
		    && tmp->icon == NULL) {
			mapTransientsFor(tmp);
			tmp->flags.miniaturized = 0;
			if (!tmp->flags.shaded) {
				wWindowMap(tmp);
			} else {
				XMapWindow(dpy, tmp->frame->core->window);
			}
			tmp->flags.semi_focused = 0;
			/*
			   if (!tmp->flags.shaded)
			 */
			wClientSetState(tmp, NormalState, None);

			WMPostNotificationName(WMNChangedState, tmp, "iconify-transient");
		}
		tmp = tmp->prev;
	}
}

#if 0
static void setupIconGrabs(WIcon * icon)
{
	/* setup passive grabs on the icon */
	XGrabButton(dpy, Button1, AnyModifier, icon->core->window, True,
		    ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
	XGrabButton(dpy, Button2, AnyModifier, icon->core->window, True,
		    ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
	XGrabButton(dpy, Button3, AnyModifier, icon->core->window, True,
		    ButtonPressMask, GrabModeSync, GrabModeAsync, None, None);
	XSync(dpy, 0);
}
#endif

static WWindow *recursiveTransientFor(WWindow * wwin)
{
	int i;

	if (!wwin)
		return None;

	/* hackish way to detect transient_for cycle */
	i = wwin->screen_ptr->window_count + 1;

	while (wwin && wwin->transient_for != None && i > 0) {
		wwin = wWindowFor(wwin->transient_for);
		i--;
	}
	if (i == 0 && wwin) {
		wwarning("%s has a severely broken WM_TRANSIENT_FOR hint.", wwin->frame->title);
		return NULL;
	}

	return wwin;
}

#if 0
static void removeIconGrabs(WIcon * icon)
{
	/* remove passive grabs on the icon */
	XUngrabButton(dpy, Button1, AnyModifier, icon->core->window);
	XUngrabButton(dpy, Button2, AnyModifier, icon->core->window);
	XUngrabButton(dpy, Button3, AnyModifier, icon->core->window);
	XSync(dpy, 0);
}
#endif

void wIconifyWindow(WWindow * wwin)
{
	XWindowAttributes attribs;
	int present;

	if (!XGetWindowAttributes(dpy, wwin->client_win, &attribs)) {
		/* the window doesn't exist anymore */
		return;
	}

	if (wwin->flags.miniaturized) {
		return;
	}

	if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
		WWindow *owner = wWindowFor(wwin->transient_for);

		if (owner && owner->flags.miniaturized)
			return;
	}

	present = wwin->frame->workspace == wwin->screen_ptr->current_workspace;

	/* if the window is in another workspace, simplify process */
	if (present) {
		/* icon creation may take a while */
		XGrabPointer(dpy, wwin->screen_ptr->root_win, False,
			     ButtonMotionMask | ButtonReleaseMask, GrabModeAsync,
			     GrabModeAsync, None, None, CurrentTime);
	}

	if (!wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
	    && !wwin->flags.net_handle_icon
#endif
	    ) {
		if (!wwin->flags.icon_moved) {
			PlaceIcon(wwin->screen_ptr, &wwin->icon_x, &wwin->icon_y, wGetHeadForWindow(wwin));
		}
		wwin->icon = wIconCreate(wwin);

		wwin->icon->mapped = 1;
	}

	wwin->flags.miniaturized = 1;
	wwin->flags.mapped = 0;

	/* unmap transients */

	unmapTransientsFor(wwin);

	if (present) {
		wSoundPlay(WSOUND_ICONIFY);

		XUngrabPointer(dpy, CurrentTime);
		wWindowUnmap(wwin);
		/* let all Expose events arrive so that we can repaint
		 * something before the animation starts (and the server is grabbed) */
		XSync(dpy, 0);

		if (wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
		    || wwin->flags.net_handle_icon
#endif
		    )
			wClientSetState(wwin, IconicState, None);
		else
			wClientSetState(wwin, IconicState, wwin->icon->icon_win);

		flushExpose();
#ifdef ANIMATIONS
		if (!wwin->screen_ptr->flags.startup && !wwin->flags.skip_next_animation
		    && !wPreferences.no_animations) {
			int ix, iy, iw, ih;

			if (!wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
			    && !wwin->flags.net_handle_icon
#endif
			    ) {
				ix = wwin->icon_x;
				iy = wwin->icon_y;
				iw = wwin->icon->core->width;
				ih = wwin->icon->core->height;
			} else {
#ifdef NETWM_HINTS
				if (wwin->flags.net_handle_icon) {
					ix = wwin->icon_x;
					iy = wwin->icon_y;
					iw = wwin->icon_w;
					ih = wwin->icon_h;
				} else
#endif
				{
					ix = 0;
					iy = 0;
					iw = wwin->screen_ptr->scr_width;
					ih = wwin->screen_ptr->scr_height;
				}
			}
			animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y,
				      wwin->frame->core->width, wwin->frame->core->height, ix, iy, iw, ih, False);
		}
#endif
	}

	wwin->flags.skip_next_animation = 0;

	if (!wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
	    && !wwin->flags.net_handle_icon
#endif
	    ) {

		if (wwin->screen_ptr->current_workspace == wwin->frame->workspace ||
		    IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)

			XMapWindow(dpy, wwin->icon->core->window);

		AddToStackList(wwin->icon->core);

		wLowerFrame(wwin->icon->core);
	}

	if (present) {
		WWindow *owner = recursiveTransientFor(wwin->screen_ptr->focused_window);

		/*
		 * It doesn't seem to be working and causes button event hangup
		 * when deiconifying a transient window.
		 setupIconGrabs(wwin->icon);
		 */
		if ((wwin->flags.focused || (owner && wwin->client_win == owner->client_win))
		    && wPreferences.focus_mode == WKF_CLICK) {
			WWindow *tmp;

			tmp = wwin->prev;
			while (tmp) {
				if (!WFLAGP(tmp, no_focusable)
				    && !(tmp->flags.hidden || tmp->flags.miniaturized)
				    && (wwin->frame->workspace == tmp->frame->workspace))
					break;
				tmp = tmp->prev;
			}
			wSetFocusTo(wwin->screen_ptr, tmp);
		} else if (wPreferences.focus_mode != WKF_CLICK) {
			wSetFocusTo(wwin->screen_ptr, NULL);
		}
#ifdef ANIMATIONS
		if (!wwin->screen_ptr->flags.startup) {
			/* Catch up with events not processed while animation was running */
			Window clientwin = wwin->client_win;

			ProcessPendingEvents();

			/* the window can disappear while ProcessPendingEvents() runs */
			if (!wWindowFor(clientwin)) {
				return;
			}
		}
#endif
	}

	/* maybe we want to do this regardless of net_handle_icon
	 * it seems to me we might break behaviour this way.
	 */
	if (wwin->flags.selected && !wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
	    && !wwin->flags.net_handle_icon
#endif
	    )
		wIconSelect(wwin->icon);

	WMPostNotificationName(WMNChangedState, wwin, "iconify");

	if (wPreferences.auto_arrange_icons)
		wArrangeIcons(wwin->screen_ptr, True);
}

void wDeiconifyWindow(WWindow * wwin)
{
#ifdef NETWM_HINTS
	/* we're hiding for show_desktop */
	int netwm_hidden = wwin->flags.net_show_desktop &&
	    wwin->frame->workspace != wwin->screen_ptr->current_workspace;
#else
	int netwm_hidden = False;
#endif

	if (!netwm_hidden)
		wWindowChangeWorkspace(wwin, wwin->screen_ptr->current_workspace);

	if (!wwin->flags.miniaturized)
		return;

	if (wwin->transient_for != None && wwin->transient_for != wwin->screen_ptr->root_win) {
		WWindow *owner = recursiveTransientFor(wwin);

		if (owner && owner->flags.miniaturized) {
			wDeiconifyWindow(owner);
			wSetFocusTo(wwin->screen_ptr, wwin);
			wRaiseFrame(wwin->frame->core);
			return;
		}
	}

	wwin->flags.miniaturized = 0;

	if (!netwm_hidden && !wwin->flags.shaded) {
		wwin->flags.mapped = 1;
	}

	if (!netwm_hidden || wPreferences.sticky_icons) {
		/* maybe we want to do this regardless of net_handle_icon
		 * it seems to me we might break behaviour this way.
		 */
		if (!wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
		    && !wwin->flags.net_handle_icon
#endif
		    && wwin->icon != NULL) {
			if (wwin->icon->selected)
				wIconSelect(wwin->icon);

			XUnmapWindow(dpy, wwin->icon->core->window);
		}
	}

	if (!netwm_hidden)
		wSoundPlay(WSOUND_DEICONIFY);

	/* if the window is in another workspace, do it silently */
	if (!netwm_hidden) {
#ifdef ANIMATIONS
		if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations
		    && !wwin->flags.skip_next_animation && wwin->icon != NULL) {
			int ix, iy, iw, ih;

			if (!wPreferences.disable_miniwindows
#ifdef NETWM_HINTS
			    && !wwin->flags.net_handle_icon
#endif
			    ) {
				ix = wwin->icon_x;
				iy = wwin->icon_y;
				iw = wwin->icon->core->width;
				ih = wwin->icon->core->height;
			} else {
#ifdef NETWM_HINTS
				if (wwin->flags.net_handle_icon) {
					ix = wwin->icon_x;
					iy = wwin->icon_y;
					iw = wwin->icon_w;
					ih = wwin->icon_h;
				} else
#endif
				{
					ix = 0;
					iy = 0;
					iw = wwin->screen_ptr->scr_width;
					ih = wwin->screen_ptr->scr_height;
				}
			}
			animateResize(wwin->screen_ptr, ix, iy, iw, ih,
				      wwin->frame_x, wwin->frame_y,
				      wwin->frame->core->width, wwin->frame->core->height, False);
		}
#endif				/* ANIMATIONS */
		wwin->flags.skip_next_animation = 0;
		XGrabServer(dpy);
		if (!wwin->flags.shaded) {
			XMapWindow(dpy, wwin->client_win);
		}
		XMapWindow(dpy, wwin->frame->core->window);
		wRaiseFrame(wwin->frame->core);
		if (!wwin->flags.shaded) {
			wClientSetState(wwin, NormalState, None);
		}
		mapTransientsFor(wwin);
	}

	if (!wPreferences.disable_miniwindows && wwin->icon != NULL
#ifdef NETWM_HINTS
	    && !wwin->flags.net_handle_icon
#endif
	    ) {
		RemoveFromStackList(wwin->icon->core);
		/*    removeIconGrabs(wwin->icon); */
		wIconDestroy(wwin->icon);
		wwin->icon = NULL;
	}

	if (!netwm_hidden) {
		XUngrabServer(dpy);

		wSetFocusTo(wwin->screen_ptr, wwin);

#ifdef ANIMATIONS
		if (!wwin->screen_ptr->flags.startup) {
			/* Catch up with events not processed while animation was running */
			Window clientwin = wwin->client_win;

			ProcessPendingEvents();

			/* the window can disappear while ProcessPendingEvents() runs */
			if (!wWindowFor(clientwin)) {
				return;
			}
		}
#endif
	}

	if (wPreferences.auto_arrange_icons) {
		wArrangeIcons(wwin->screen_ptr, True);
	}

	WMPostNotificationName(WMNChangedState, wwin, "iconify");

	/* In case we were shaded and iconified, also unshade */
	if (!netwm_hidden)
		wUnshadeWindow(wwin);
}

static void hideWindow(WIcon * icon, int icon_x, int icon_y, WWindow * wwin, int animate)
{
	if (wwin->flags.miniaturized) {
		if (wwin->icon) {
			XUnmapWindow(dpy, wwin->icon->core->window);
			wwin->icon->mapped = 0;
		}
		wwin->flags.hidden = 1;

		WMPostNotificationName(WMNChangedState, wwin, "hide");
		return;
	}

	if (wwin->flags.inspector_open) {
		wHideInspectorForWindow(wwin);
	}

	wwin->flags.hidden = 1;
	wWindowUnmap(wwin);

	wClientSetState(wwin, IconicState, icon->icon_win);
	flushExpose();
	wSoundPlay(WSOUND_HIDE);
#ifdef ANIMATIONS
	if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations &&
	    !wwin->flags.skip_next_animation && animate) {
		animateResize(wwin->screen_ptr, wwin->frame_x, wwin->frame_y,
			      wwin->frame->core->width, wwin->frame->core->height,
			      icon_x, icon_y, icon->core->width, icon->core->height, True);
	}
#endif
	wwin->flags.skip_next_animation = 0;

	WMPostNotificationName(WMNChangedState, wwin, "hide");
}

void wHideOtherApplications(WWindow * awin)
{
	WWindow *wwin;
	WApplication *tapp;

	if (!awin)
		return;
	wwin = awin->screen_ptr->focused_window;

	while (wwin) {
		if (wwin != awin
		    && wwin->frame->workspace == awin->screen_ptr->current_workspace
		    && !(wwin->flags.miniaturized || wwin->flags.hidden)
		    && !wwin->flags.internal_window
		    && wGetWindowOfInspectorForWindow(wwin) != awin && !WFLAGP(wwin, no_hide_others)) {

			if (wwin->main_window == None || WFLAGP(wwin, no_appicon)) {
				if (!WFLAGP(wwin, no_miniaturizable)) {
					wwin->flags.skip_next_animation = 1;
					wIconifyWindow(wwin);
				}
			} else if (wwin->main_window != None && awin->main_window != wwin->main_window) {
				tapp = wApplicationOf(wwin->main_window);
				if (tapp) {
					tapp->flags.skip_next_animation = 1;
					wHideApplication(tapp);
				} else {
					if (!WFLAGP(wwin, no_miniaturizable)) {
						wwin->flags.skip_next_animation = 1;
						wIconifyWindow(wwin);
					}
				}
			}
		}
		wwin = wwin->prev;
	}
	/*
	   wSetFocusTo(awin->screen_ptr, awin);
	 */
}

void wHideApplication(WApplication * wapp)
{
	WScreen *scr;
	WWindow *wlist;
	int hadfocus;
	int animate;

	if (!wapp) {
		wwarning("trying to hide a non grouped window");
		return;
	}
	if (!wapp->main_window_desc) {
		wwarning("group leader not found for window group");
		return;
	}
	scr = wapp->main_window_desc->screen_ptr;
	hadfocus = 0;
	wlist = scr->focused_window;
	if (!wlist)
		return;

	if (wlist->main_window == wapp->main_window)
		wapp->last_focused = wlist;
	else
		wapp->last_focused = NULL;

	animate = !wapp->flags.skip_next_animation;

	while (wlist) {
		if (wlist->main_window == wapp->main_window) {
			if (wlist->flags.focused) {
				hadfocus = 1;
			}
			if (wapp->app_icon) {
				hideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
					   wapp->app_icon->y_pos, wlist, animate);
				animate = False;
			}
		}
		wlist = wlist->prev;
	}

	wapp->flags.skip_next_animation = 0;

	if (hadfocus) {
		if (wPreferences.focus_mode == WKF_CLICK) {
			wlist = scr->focused_window;
			while (wlist) {
				if (!WFLAGP(wlist, no_focusable) && !wlist->flags.hidden
				    && (wlist->flags.mapped || wlist->flags.shaded))
					break;
				wlist = wlist->prev;
			}
			wSetFocusTo(scr, wlist);
		} else {
			wSetFocusTo(scr, NULL);
		}
	}

	wapp->flags.hidden = 1;

	if (wPreferences.auto_arrange_icons) {
		wArrangeIcons(scr, True);
	}
#ifdef HIDDENDOT
	if (wapp->app_icon)
		wAppIconPaint(wapp->app_icon);
#endif
}

static void unhideWindow(WIcon * icon, int icon_x, int icon_y, WWindow * wwin, int animate, int bringToCurrentWS)
{
	if (bringToCurrentWS)
		wWindowChangeWorkspace(wwin, wwin->screen_ptr->current_workspace);

	wwin->flags.hidden = 0;

	wSoundPlay(WSOUND_UNHIDE);
#ifdef ANIMATIONS
	if (!wwin->screen_ptr->flags.startup && !wPreferences.no_animations && animate) {
		animateResize(wwin->screen_ptr, icon_x, icon_y,
			      icon->core->width, icon->core->height,
			      wwin->frame_x, wwin->frame_y,
			      wwin->frame->core->width, wwin->frame->core->height, True);
	}
#endif
	wwin->flags.skip_next_animation = 0;
	if (wwin->screen_ptr->current_workspace == wwin->frame->workspace) {
		XMapWindow(dpy, wwin->client_win);
		XMapWindow(dpy, wwin->frame->core->window);
		wClientSetState(wwin, NormalState, None);
		wwin->flags.mapped = 1;
		wRaiseFrame(wwin->frame->core);
	}
	if (wwin->flags.inspector_open) {
		wUnhideInspectorForWindow(wwin);
	}

	WMPostNotificationName(WMNChangedState, wwin, "hide");
}

void wUnhideApplication(WApplication * wapp, Bool miniwindows, Bool bringToCurrentWS)
{
	WScreen *scr;
	WWindow *wlist, *next;
	WWindow *focused = NULL;
	int animate;

	if (!wapp)
		return;

	scr = wapp->main_window_desc->screen_ptr;
	wlist = scr->focused_window;
	if (!wlist)
		return;

	/* goto beginning of list */
	while (wlist->prev)
		wlist = wlist->prev;

	animate = !wapp->flags.skip_next_animation;

	while (wlist) {
		next = wlist->next;

		if (wlist->main_window == wapp->main_window) {
			if (wlist->flags.focused)
				focused = wlist;
			else if (!focused || !focused->flags.focused)
				focused = wlist;

			if (wlist->flags.miniaturized) {
				if ((bringToCurrentWS || wPreferences.sticky_icons ||
				     wlist->frame->workspace == scr->current_workspace) && wlist->icon) {
					if (!wlist->icon->mapped) {
						int x, y;

						PlaceIcon(scr, &x, &y, wGetHeadForWindow(wlist));
						if (wlist->icon_x != x || wlist->icon_y != y) {
							XMoveWindow(dpy, wlist->icon->core->window, x, y);
						}
						wlist->icon_x = x;
						wlist->icon_y = y;
						XMapWindow(dpy, wlist->icon->core->window);
						wlist->icon->mapped = 1;
					}
					wRaiseFrame(wlist->icon->core);
				}
				if (bringToCurrentWS)
					wWindowChangeWorkspace(wlist, scr->current_workspace);
				wlist->flags.hidden = 0;
				if (miniwindows && wlist->frame->workspace == scr->current_workspace) {
					wDeiconifyWindow(wlist);
				}
				WMPostNotificationName(WMNChangedState, wlist, "hide");
			} else if (wlist->flags.shaded) {
				if (bringToCurrentWS)
					wWindowChangeWorkspace(wlist, scr->current_workspace);
				wlist->flags.hidden = 0;
				if (wlist->frame->workspace == scr->current_workspace) {
					XMapWindow(dpy, wlist->frame->core->window);
					if (miniwindows) {
						wUnshadeWindow(wlist);
						wRaiseFrame(wlist->frame->core);
					}
				}
				WMPostNotificationName(WMNChangedState, wlist, "hide");
			} else if (wlist->flags.hidden) {
				unhideWindow(wapp->app_icon->icon, wapp->app_icon->x_pos,
					     wapp->app_icon->y_pos, wlist, animate, bringToCurrentWS);
				animate = False;
			} else {
				if (bringToCurrentWS && wlist->frame->workspace != scr->current_workspace) {
					wWindowChangeWorkspace(wlist, scr->current_workspace);
				}
				wRaiseFrame(wlist->frame->core);
			}
		}
		wlist = next;
	}

	wapp->flags.skip_next_animation = 0;
	wapp->flags.hidden = 0;

	if (wapp->last_focused && wapp->last_focused->flags.mapped) {
		wRaiseFrame(wapp->last_focused->frame->core);
		wSetFocusTo(scr, wapp->last_focused);
	} else if (focused) {
		wSetFocusTo(scr, focused);
	}
	wapp->last_focused = NULL;
	if (wPreferences.auto_arrange_icons) {
		wArrangeIcons(scr, True);
	}
#ifdef HIDDENDOT
	wAppIconPaint(wapp->app_icon);
#endif
}

void wShowAllWindows(WScreen * scr)
{
	WWindow *wwin, *old_foc;
	WApplication *wapp;

	old_foc = wwin = scr->focused_window;
	while (wwin) {
		if (!wwin->flags.internal_window &&
		    (scr->current_workspace == wwin->frame->workspace || IS_OMNIPRESENT(wwin))) {
			if (wwin->flags.miniaturized) {
				wwin->flags.skip_next_animation = 1;
				wDeiconifyWindow(wwin);
			} else if (wwin->flags.hidden) {
				wapp = wApplicationOf(wwin->main_window);
				if (wapp) {
					wUnhideApplication(wapp, False, False);
				} else {
					wwin->flags.skip_next_animation = 1;
					wDeiconifyWindow(wwin);
				}
			}
		}
		wwin = wwin->prev;
	}
	wSetFocusTo(scr, old_foc);
	/*wRaiseFrame(old_foc->frame->core); */
}

void wRefreshDesktop(WScreen * scr)
{
	Window win;
	XSetWindowAttributes attr;

	attr.backing_store = NotUseful;
	attr.save_under = False;
	win = XCreateWindow(dpy, scr->root_win, 0, 0, scr->scr_width,
			    scr->scr_height, 0, CopyFromParent, CopyFromParent,
			    (Visual *) CopyFromParent, CWBackingStore | CWSaveUnder, &attr);
	XMapRaised(dpy, win);
	XDestroyWindow(dpy, win);
	XFlush(dpy);
}

void wArrangeIcons(WScreen * scr, Bool arrangeAll)
{
	WWindow *wwin;
	WAppIcon *aicon;

	int head;
	const int heads = wXineramaHeads(scr);

	struct HeadVars {
		int pf;		/* primary axis */
		int sf;		/* secondary axis */
		int fullW;
		int fullH;
		int pi, si;
		int sx1, sx2, sy1, sy2;	/* screen boundary */
		int sw, sh;
		int xo, yo;
		int xs, ys;
	} *vars;

	int isize = wPreferences.icon_size;

	vars = (struct HeadVars *)wmalloc(sizeof(struct HeadVars) * heads);

	for (head = 0; head < heads; ++head) {
#if 0
		WMRect rect = wGetRectForHead(scr, head);
#else
		WArea area = wGetUsableAreaForHead(scr, head, NULL, False);
		WMRect rect = wmkrect(area.x1, area.y1, area.x2 - area.x1, area.y2 - area.y1);
#endif

		vars[head].pi = vars[head].si = 0;
		vars[head].sx1 = rect.pos.x;
		vars[head].sy1 = rect.pos.y;
		vars[head].sw = rect.size.width;
		vars[head].sh = rect.size.height;
		vars[head].sx2 = vars[head].sx1 + vars[head].sw;
		vars[head].sy2 = vars[head].sy1 + vars[head].sh;

#if 0
		if (scr->dock) {
			if (scr->dock->on_right_side)
				vars[head].sx2 -= isize + DOCK_EXTRA_SPACE;
			else
				vars[head].sx1 += isize + DOCK_EXTRA_SPACE;
		}
#endif

		vars[head].sw = isize * (vars[head].sw / isize);
		vars[head].sh = isize * (vars[head].sh / isize);
		vars[head].fullW = (vars[head].sx2 - vars[head].sx1) / isize;
		vars[head].fullH = (vars[head].sy2 - vars[head].sy1) / isize;

		/* icon yard boundaries */
		if (wPreferences.icon_yard & IY_VERT) {
			vars[head].pf = vars[head].fullH;
			vars[head].sf = vars[head].fullW;
		} else {
			vars[head].pf = vars[head].fullW;
			vars[head].sf = vars[head].fullH;
		}
		if (wPreferences.icon_yard & IY_RIGHT) {
			vars[head].xo = vars[head].sx2 - isize;
			vars[head].xs = -1;
		} else {
			vars[head].xo = vars[head].sx1;
			vars[head].xs = 1;
		}
		if (wPreferences.icon_yard & IY_TOP) {
			vars[head].yo = vars[head].sy1;
			vars[head].ys = 1;
		} else {
			vars[head].yo = vars[head].sy2 - isize;
			vars[head].ys = -1;
		}
	}

#define X ((wPreferences.icon_yard & IY_VERT) \
    ? vars[head].xo + vars[head].xs*(vars[head].si*isize) \
    : vars[head].xo + vars[head].xs*(vars[head].pi*isize))

#define Y ((wPreferences.icon_yard & IY_VERT) \
    ? vars[head].yo + vars[head].ys*(vars[head].pi*isize) \
    : vars[head].yo + vars[head].ys*(vars[head].si*isize))

	/* arrange application icons */
	aicon = scr->app_icon_list;
	/* reverse them to avoid unnecessarily sliding of icons */
	while (aicon && aicon->next)
		aicon = aicon->next;

	while (aicon) {
		if (!aicon->docked) {
			/* CHECK: can icon be NULL here ? */
			/* The intention here is to place the AppIcon on the head that
			 * contains most of the applications _main_ window. */
			head = wGetHeadForWindow(aicon->icon->owner);

			if (aicon->x_pos != X || aicon->y_pos != Y) {
#ifdef ANIMATIONS
				if (!wPreferences.no_animations) {
					SlideWindow(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, X, Y);
				}
#endif				/* ANIMATIONS */
			}
			wAppIconMove(aicon, X, Y);
			vars[head].pi++;
			if (vars[head].pi >= vars[head].pf) {
				vars[head].pi = 0;
				vars[head].si++;
			}
		}
		aicon = aicon->prev;
	}

	/* arrange miniwindows */
	wwin = scr->focused_window;
	/* reverse them to avoid unnecessarily shuffling */
	while (wwin && wwin->prev)
		wwin = wwin->prev;

	while (wwin) {
		if (wwin->icon && wwin->flags.miniaturized && !wwin->flags.hidden &&
		    (wwin->frame->workspace == scr->current_workspace ||
		     IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)) {

			head = wGetHeadForWindow(wwin);

			if (arrangeAll || !wwin->flags.icon_moved) {
				if (wwin->icon_x != X || wwin->icon_y != Y) {
#ifdef ANIMATIONS
					if (wPreferences.no_animations) {
						XMoveWindow(dpy, wwin->icon->core->window, X, Y);
					} else {
						SlideWindow(wwin->icon->core->window, wwin->icon_x,
							    wwin->icon_y, X, Y);
					}
#else
					XMoveWindow(dpy, wwin->icon->core->window, X, Y);
#endif				/* ANIMATIONS */
				}
				wwin->icon_x = X;
				wwin->icon_y = Y;

				vars[head].pi++;
				if (vars[head].pi >= vars[head].pf) {
					vars[head].pi = 0;
					vars[head].si++;
				}
			}
		}
		if (arrangeAll) {
			wwin->flags.icon_moved = 0;
		}
		/* we reversed the order, so we use next */
		wwin = wwin->next;
	}

	wfree(vars);
}

#if 0
void wArrangeIcons(WScreen * scr, Bool arrangeAll)
{
	WWindow *wwin;
	WAppIcon *aicon;
	int pf;			/* primary axis */
	int sf;			/* secondary axis */
	int fullW;
	int fullH;
	int pi, si;
	int sx1, sx2, sy1, sy2;	/* screen boundary */
	int sw, sh;
	int xo, yo;
	int xs, ys;
	int isize = wPreferences.icon_size;

	/*
	 * Find out screen boundaries.
	 */

	/*
	 * Allows each head to have miniwindows
	 */
	WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));

	sx1 = rect.pos.x;
	sy1 = rect.pos.y;
	sw = rect.size.width;
	sh = rect.size.height;
	sx2 = sx1 + sw;
	sy2 = sy1 + sh;
	if (scr->dock) {
		if (scr->dock->on_right_side)
			sx2 -= isize + DOCK_EXTRA_SPACE;
		else
			sx1 += isize + DOCK_EXTRA_SPACE;
	}
#if 0
	sw = isize * (scr->scr_width / isize);
	sh = isize * (scr->scr_height / isize);
#else
	sw = isize * (sw / isize);
	sh = isize * (sh / isize);
#endif
	fullW = (sx2 - sx1) / isize;
	fullH = (sy2 - sy1) / isize;

	/* icon yard boundaries */
	if (wPreferences.icon_yard & IY_VERT) {
		pf = fullH;
		sf = fullW;
	} else {
		pf = fullW;
		sf = fullH;
	}
	if (wPreferences.icon_yard & IY_RIGHT) {
		xo = sx2 - isize;
		xs = -1;
	} else {
		xo = sx1;
		xs = 1;
	}
	if (wPreferences.icon_yard & IY_TOP) {
		yo = sy1;
		ys = 1;
	} else {
		yo = sy2 - isize;
		ys = -1;
	}

	/* arrange icons putting the most recently focused window
	 * as the last icon */
#define X ((wPreferences.icon_yard & IY_VERT) ? xo + xs*(si*isize)\
    : xo + xs*(pi*isize))
#define Y ((wPreferences.icon_yard & IY_VERT) ? yo + ys*(pi*isize)\
    : yo + ys*(si*isize))

	/* arrange application icons */
	aicon = scr->app_icon_list;
	/* reverse them to avoid unnecessarily sliding of icons */
	while (aicon && aicon->next)
		aicon = aicon->next;

	pi = 0;
	si = 0;
	while (aicon) {
		if (!aicon->docked) {
			if (aicon->x_pos != X || aicon->y_pos != Y) {
#ifdef ANIMATIONS
				if (!wPreferences.no_animations) {
					SlideWindow(aicon->icon->core->window, aicon->x_pos, aicon->y_pos, X, Y);
				}
#endif				/* ANIMATIONS */
			}
			wAppIconMove(aicon, X, Y);
			pi++;
		}
		/* we reversed the order so we use prev */
		aicon = aicon->prev;
		if (pi >= pf) {
			pi = 0;
			si++;
		}
	}

	/* arrange miniwindows */

	wwin = scr->focused_window;
	/* reverse them to avoid unnecessarily shuffling */
	while (wwin && wwin->prev)
		wwin = wwin->prev;

	while (wwin) {
		if (wwin->icon && wwin->flags.miniaturized && !wwin->flags.hidden &&
		    (wwin->frame->workspace == scr->current_workspace ||
		     IS_OMNIPRESENT(wwin) || wPreferences.sticky_icons)) {

			if (arrangeAll || !wwin->flags.icon_moved) {
				if (wwin->icon_x != X || wwin->icon_y != Y) {
#ifdef ANIMATIONS
					if (wPreferences.no_animations) {
						XMoveWindow(dpy, wwin->icon->core->window, X, Y);
					} else {
						SlideWindow(wwin->icon->core->window, wwin->icon_x,
							    wwin->icon_y, X, Y);
					}
#else
					XMoveWindow(dpy, wwin->icon->core->window, X, Y);
#endif				/* ANIMATIONS */
				}
				wwin->icon_x = X;
				wwin->icon_y = Y;
				pi++;
			}
		}
		if (arrangeAll) {
			wwin->flags.icon_moved = 0;
		}
		/* we reversed the order, so we use next */
		wwin = wwin->next;
		if (pi >= pf) {
			pi = 0;
			si++;
		}
	}
}
#endif

void wSelectWindow(WWindow * wwin, Bool flag)
{
	WScreen *scr = wwin->screen_ptr;

	if (flag) {
		wwin->flags.selected = 1;
		XSetWindowBorder(dpy, wwin->frame->core->window, scr->white_pixel);

		if (!HAS_BORDER(wwin)) {
			XSetWindowBorderWidth(dpy, wwin->frame->core->window, FRAME_BORDER_WIDTH);
		}

		if (!scr->selected_windows)
			scr->selected_windows = WMCreateArray(4);
		WMAddToArray(scr->selected_windows, wwin);
	} else {
		wwin->flags.selected = 0;
		XSetWindowBorder(dpy, wwin->frame->core->window, scr->frame_border_pixel);

		if (!HAS_BORDER(wwin)) {
			XSetWindowBorderWidth(dpy, wwin->frame->core->window, 0);
		}

		if (scr->selected_windows) {
			WMRemoveFromArray(scr->selected_windows, wwin);
		}
	}
}

void wMakeWindowVisible(WWindow * wwin)
{
	if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
		wWorkspaceChange(wwin->screen_ptr, wwin->frame->workspace);

	if (wwin->flags.shaded) {
		wUnshadeWindow(wwin);
	}
	if (wwin->flags.hidden) {
		WApplication *app;

		app = wApplicationOf(wwin->main_window);
		if (app) {
			/* trick to get focus to this window */
			app->last_focused = wwin;
			wUnhideApplication(app, False, False);
		}
	}
	if (wwin->flags.miniaturized) {
		wDeiconifyWindow(wwin);
	} else {
		if (!WFLAGP(wwin, no_focusable))
			wSetFocusTo(wwin->screen_ptr, wwin);
		wRaiseFrame(wwin->frame->core);
	}
}

/*
 * Do the animation while shading (called with what = SHADE)
 * or unshading (what = UNSHADE).
 */
#ifdef ANIMATIONS
static void shade_animate(WWindow *wwin, Bool what)
{
	int y, s, w, h;
	time_t time0 = time(NULL);

	if (wwin->flags.skip_next_animation && wPreferences.no_animations)
		return;

	switch(what) {
	case SHADE:
		if (!wwin->screen_ptr->flags.startup) {
			/* do the shading animation */
			h = wwin->frame->core->height;
			s = h / SHADE_STEPS;
			if (s < 1)
				s = 1;
			w = wwin->frame->core->width;
			y = wwin->frame->top_width;
			while (h > wwin->frame->top_width + 1) {
				XMoveWindow(dpy, wwin->client_win, 0, y);
				XResizeWindow(dpy, wwin->frame->core->window, w, h);
				XFlush(dpy);

				if (time(NULL) - time0 > MAX_ANIMATION_TIME)
					break;

				if (SHADE_DELAY > 0) {
				wusleep(SHADE_DELAY * 1000L);
				} else {
					wusleep(10);
				}
				h -= s;
			y -= s;
			}
			XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
		}
		break;

	case UNSHADE:
		h = wwin->frame->top_width + wwin->frame->bottom_width;
		y = wwin->frame->top_width - wwin->client.height;
		s = abs(y) / SHADE_STEPS;
		if (s < 1)
			s = 1;
		w = wwin->frame->core->width;
		XMoveWindow(dpy, wwin->client_win, 0, y);
		if (s > 0) {
			while (h < wwin->client.height + wwin->frame->top_width + wwin->frame->bottom_width) {
				XResizeWindow(dpy, wwin->frame->core->window, w, h);
				XMoveWindow(dpy, wwin->client_win, 0, y);
				XFlush(dpy);
				if (SHADE_DELAY > 0) {
					wusleep(SHADE_DELAY * 2000L / 3);
				} else {
					wusleep(10);
				}
				h += s;
				y += s;

				if (time(NULL) - time0 > MAX_ANIMATION_TIME)
					break;
			}
		}
		XMoveWindow(dpy, wwin->client_win, 0, wwin->frame->top_width);
		break;
	}
}
#else
static void shade_animate(WWindow *wwin, Bool what) { return; }
#endif /* ANIMATIONS */
