/*-
 * 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 "wm.h"
#include "decor.h"

static client_t* last_focused_client = NULL;

/* spawn a child */
pid_t action_exec(int screen, char *cmd) {
	char buff[256];
	char number[10];
	char *ptr;
	pid_t pid;

	if ((pid = fork()) == 0) {
		snprintf(buff, sizeof(buff), "DISPLAY=%s", XDisplayName(NULL));
		ptr = strchr(strchr(buff, ':'), '.');
		if (ptr) *ptr = '\0';
		snprintf(number, sizeof(number), "%i", screen);
		strncat(buff, ".", sizeof(buff) - strlen(buff));
		strncat(buff, number, sizeof(buff) - strlen(buff));
		putenv(buff);

		execl(_PATH_BSHELL, "sh", "-c", cmd, NULL);
		exit(1);
	}

	return pid;
}

/*
 * send a synthetic ConfigureNotify event to a client (as on a move).  the
 * ICCCM requires that these fake configurenotify events get sent in a
 * reparenting windowmanager whenever the windowmanager moves the client's
 * parent window (without also resizing the client window).
 */
void action_send_config(client_t *client)
{
	XEvent event;

	event.type = ConfigureNotify;
	event.xconfigure.display = display;
	event.xconfigure.event = client->window;
	event.xconfigure.window = client->window;
	event.xconfigure.x = client->x + client->dgroup->left_space;
	event.xconfigure.y = client->y + client->dgroup->top_space;
	event.xconfigure.width = client->width;
	event.xconfigure.height = client->height;
	event.xconfigure.border_width = 0;
	event.xconfigure.above = client->frame;
	event.xconfigure.override_redirect = 0;
	XSendEvent(display, client->window, 0, StructureNotifyMask, &event);
}

/* send a ICCCM Client Message */
void action_sendcmesg( Window w, Atom a, Time timestamp)
{
	XClientMessageEvent e;

	e.type = ClientMessage;
	e.window = w;
	e.message_type = WM_PROTOCOLS;
	e.format = 32;
	e.data.l[0] = a;
	e.data.l[1] = timestamp;

	XSendEvent(display, w, 0, 0, (XEvent *) &e);
}

/* pointer motion handling for window movement */
static __inline void movemotion( XMotionEvent *e, long eventmask, client_t *client,
                                 point_t *win, point_t *oldwin, point_t *ptr )
{
	win->y += e->y_root - ptr->y;
	win->x += e->x_root - ptr->x;
	ptr->y = e->y_root;
	ptr->x = e->x_root;

	/* either move the window or redraw the winbox */
	if (!options.opaquemove)
   {
		draw_winbox( client->screen, client, oldwin->x,
                   oldwin->y, client->width, client->height );
		draw_winbox( client->screen, client, win->x, win->y,
                   client->width, client->height );
	}
   else
   {
		XMoveWindow(display, client->frame, win->x, win->y);
   }


   /* bva. right here. we shuold draw the resize/move info box */
   /*     oooooooooor add a call-out to plugins to farm out the
          work .... */
   if( options.draw_geometry )
   {
      show_position( 1,
                     0, /* location */
                     client->screen,
                     client,
                     win->x,
                     win->y,
                     client->width,
                     client->height );
   }

	/* update oldwin point */
	oldwin->x = win->x;
	oldwin->y = win->y;
}

/* button release handling for window movement */
static __inline void moverelease(client_t *client, point_t *win,
                                 point_t *oldwin)
{
	/*
	 * release the mouse pointer; in nonopaque mode, erase
	 * the current winbox and let go of the server.
	 */
	XUngrabPointer(display, CurrentTime);
	if (!options.opaquemove) {
		draw_winbox(client->screen, client, oldwin->x,
			oldwin->y, client->width, client->height);
		stacking_raise(client);
		XUngrabServer(display);
	}

	/*
	 * if the window position changed, move the window to
	 * it's final destination, send it a synthetic config
	 * and tell plugins that it moved.
	 */
	if (win->x != client->x || win->y != client->y)
   {
		client->x = win->x;
      log( 6, "^^^^^^^^^^^^^ changing clientx to: %d", client->x );
		client->y = win->y;
		XMoveWindow(display, client->frame, win->x, win->y);
		action_send_config(client);
      // bva .. need this to be sent by the pager !
		plugin_geometry_change(client);
	}
   if( options.draw_geometry )
   {
      show_position( 0,
                     0, /* location */
                     client->screen,
                     client,
                     win->x,
                     win->y,
                     client->width,
                     client->height );
   }
}

/* window movement X event loop */
static __inline void moveloop(client_t *client, long eventmask)
{
	XEvent event;
	point_t win, oldwin, ptr;
	Window dumwin;
	int dumint;
   unsigned int dumuint;

	/*
	 * get the mouse pointer position, window position, and
	 * set old window position values to the current ones.
	 */
	XQueryPointer( display, client->screen->root, &dumwin, &dumwin,
                  &ptr.x, &ptr.y, &dumint, &dumint, &dumuint );
	oldwin.x = win.x = client->x;
	oldwin.y = win.y = client->y;

	/* in nonopaque mode, draw the first winbox now */
	if (!options.opaquemove)
   {
		draw_winbox( client->screen, client, win.x, win.y,
                   client->width, client->height );
   }

	/* handle movement loop events */
	while (XMaskEvent(display, eventmask, &event), 1)
   {
		switch (event.type)
      {
		case Expose:
               event_handle(&event);
               break;
		case MotionNotify:
               movemotion( &event.xmotion, eventmask, client,
                           &win, &oldwin, &ptr );
               break;
		case ButtonRelease:
               moverelease(client, &win, &oldwin);
               return;
		}
   }
}

/*
 * interactively move a window; handle both opaque and nonopaque
 * movement styles.  the client window in question becomes bound
 * to pointer movements until the pointer button is released,
 * at which point the window is left at that final destination
 * location and send a synthetic configure event.
 */
void action_move(client_t *client)
{
	long eventmask;

	/*
	 * movement isn't allowed on nomove clients; ungrab
	 * the passive grab we hold on entry to this func and
	 * return
	 */
	if (client->flags.nomove) {
		XUngrabPointer(display, CurrentTime);
		return;
	}

	/* make grab for moving the window */
	eventmask = PointerMotionMask | ButtonMotionMask
		| ButtonReleaseMask;
	if (XGrabPointer(display, client->screen->root, 0, eventmask,
			GrabModeAsync, GrabModeAsync, client->screen->root,
			cursor_move, CurrentTime) != GrabSuccess)
		return;

	/*
	 * prepare according to opaque or nonopaque style move;
	 * opaque style requires that we handle exposures.
	 */
	if (options.opaquemove) {
		stacking_raise(client);
		eventmask |= ExposureMask;
	} else
		XGrabServer(display);

	/* call window movement loop */
	moveloop(client, eventmask);
}

/*
 * set width of client for resize, only move the bound
 * edge.
 */
static __inline void setwidth(client_t *client, int width,
		rect_t *win, int **dx, int **dy) {
	if (*dx == &win->x1)
		win->x1 = win->x2 - width - DWIDTH(client);
	else if (*dx == &win->x2)
		win->x2 = win->x1 + width + DWIDTH(client);
}

/*
 * set height of client for resize, only move the
 * bound edge.
 */
static __inline void setheight(client_t *client, int height,
		rect_t *win, int **dx, int **dy) {
	if (*dy == &win->y1)
		win->y1 = win->y2 - height - DHEIGHT(client);
	else if (*dy == &win->y2)
		win->y2 = win->y1 + height + DHEIGHT(client);
}

/*
 * check resizing against constraints specified by XNormalHints
 * given to us by the client.
 */
static __inline void resizelimit(client_t *client, rect_t *win,
                                 int **dx, int **dy)
{
	int basew, baseh, minw, minh;
   int wid = -1, hei = -1;

	/*
	 * get minimum and base height/width values for
	 * this client.  according to the ICCCM, if the
	 * base size is specified and minsize isn't, minsize
	 * should be base size; if base size is not specified
	 * and minsize is, use minsize for basesize.
	 */
	if (client->normalhints.flags & PBaseSize) {
		basew = client->normalhints.base_width;
		baseh = client->normalhints.base_height;
		if (client->normalhints.flags & PMinSize) {
			minw = client->normalhints.min_width;
			minh = client->normalhints.min_height;
		} else {
			minw = basew;
			minh = baseh;
		}
	} else if (client->normalhints.flags & PMinSize) {
		minw = basew = client->normalhints.min_width;
		minh = baseh = client->normalhints.min_height;
	} else
		baseh = basew = minw = minh = 1;

	/*
	 * keep resizing to increments as specified
	 * in the normal hints.
	 */
	if (client->normalhints.flags & PResizeInc) {
		int wid, hei;

		wid = win->x2 - win->x1 - DWIDTH(client) - basew;
		hei = win->y2 - win->y1 - DHEIGHT(client) - baseh;
		wid -= wid % client->normalhints.width_inc;
		hei -= hei % client->normalhints.height_inc;

		setwidth(client, wid + basew, win, dx, dy);
		setheight(client, hei + baseh, win, dx, dy);

      
      /* for the display below .... */
      wid = wid / client->normalhints.width_inc; 
      hei = hei / client->normalhints.height_inc;
	}

	/*
	 * if a maxmimum size was specified, make sure the client
	 * dimensions do not exceed it.
	 */
	if (client->normalhints.flags & PMaxSize) {
		if (win->x2 - win->x1 - DWIDTH(client) > client->normalhints.max_width)
			setwidth(client, client->normalhints.max_width, win, dx, dy);
		if (win->y2 - win->y1 - DHEIGHT(client) > client->normalhints.max_height)
			setheight(client, client->normalhints.max_height, win, dx, dy);
	}

	/*
	 * handle window minimum size; even if no minimum size hint
	 * was specified we enforce a minimum size of 1x1.
	 */
	if (win->x2 - win->x1 - DWIDTH(client) < minw)
		setwidth(client, minw, win, dx, dy);
	if (win->y2 - win->y1 - DHEIGHT(client) < minh)
		setheight(client, minh, win, dx, dy);

   if( options.draw_geometry )
   {
      show_position( 1,
                     1, /* size */
                     client->screen,
                     client,
                     win->x1,
                     win->y1,
                     wid == -1 ? win->x2 - win->x1 - DWIDTH(client) - basew : wid,
                     hei == -1 ? hei = win->y2 - win->y1 - DHEIGHT(client) - baseh : hei );
   }
}

/* pointer motion handling for window resizing */
static __inline void resizemotion(XMotionEvent *e, client_t *client, rect_t *win,
		rect_t *oldwin, point_t *ptr, int **dx, int **dy, point_t *reloff) {
	ptr->x = e->x_root;
	ptr->y = e->y_root;

	/*
	 * rebind edges to the mouse in the x direction, if
	 * an edge was crossed.
	 */
	if (ptr->x > client->x + FULLWIDTH(client) && *dx != &win->x2) {
		win->x1 = client->x;
		*dx = &win->x2;
		reloff->x = reloff->y = 0;
	} else if (ptr->x < client->x && *dx != &win->x1) {
		win->x2 = client->x + FULLWIDTH(client);
		*dx = &win->x1;
		reloff->x = reloff->y = 0;
	}

	/*
	 * rebind edges to the mouse if edges were crossed
	 * in the y direction.
	 */
	if (ptr->y > client->y + FULLHEIGHT(client) && *dy != &win->y2) {
		win->y1 = client->y;
		*dy = &win->y2;
		reloff->x = reloff->y = 0;
	} else if (ptr->y < client->y && *dy != &win->y1) {
		win->y2 = client->y + FULLHEIGHT(client);
		*dy = &win->y1;
		reloff->x = reloff->y = 0;
	}

	/* move the bound edges */
	if (*dx)
		**dx = ptr->x + reloff->x;
	if (*dy)
		**dy = ptr->y + reloff->y;

	/* constrain based on normalhints */
	resizelimit(client, win, dx, dy);

	/*
	 * erase the old winbox and draw a new one if the
	 * window rectangle changed.
	 */
	if (memcmp(win, oldwin, sizeof(rect_t))) {
		draw_winbox(client->screen, client, oldwin->x1, oldwin->y1,
			oldwin->x2 - oldwin->x1 - DWIDTH(client),
			oldwin->y2 - oldwin->y1 - DHEIGHT(client));
		draw_winbox(client->screen, client, win->x1, win->y1,
			win->x2 - win->x1 - DWIDTH(client),
			win->y2 - win->y1 - DHEIGHT(client));
		memcpy(oldwin, win, sizeof(rect_t));
	}
}

/* button release handling for window resizing */
static __inline void resizerelease(client_t *client, rect_t *win,
		rect_t *oldwin) {
	/*
	 * erase the last window box, release the mouse pointer
	 * and release the server.
	 */
	draw_winbox(client->screen, client, oldwin->x1, oldwin->y1,
		oldwin->x2 - oldwin->x1 - DWIDTH(client),
		oldwin->y2 - oldwin->y1 - DHEIGHT(client));
	XUngrabPointer(display, CurrentTime);
	XUngrabServer(display);

	/*
	 * if the window geometry has changed, resize the client
	 * window and notify plugins.
	 */
	if (win->x1 != client->x || win->y1 != client->y
			|| win->x2 != client->x + FULLWIDTH(client)
			|| win->y2 != client->y + FULLHEIGHT(client)) {
		client->x = win->x1;
		client->y = win->y1;
		client->width = win->x2 - win->x1 - DWIDTH(client);
		client->height = win->y2 - win->y1 - DHEIGHT(client);
		client_sizeframe(client);
		plugin_geometry_change(client);
	}

   
   if( options.draw_geometry )
   {
      show_position( 0,
                     1, /* size */
                     client->screen,
                     client,
                     win->x1,
                     win->y1,
                     client->width,
                     client->height );
   }
}

/* window resizing X event loop */
static __inline void resizeloop(client_t *client, long eventmask) {
	XEvent event;
	rect_t win, oldwin;
	point_t ptr, reloff;
	Window dumwin;
	unsigned int dumuint;
	int dumint;
	int *dx, *dy;

	/*
	 * get the mouse pointer position, window rectangle, and
	 * set old window rectangle values to the current ones.
	 */
	XQueryPointer(display, client->screen->root, &dumwin, &dumwin,
                 &ptr.x, &ptr.y, &dumint, &dumint, &dumuint);
	oldwin.x1 = win.x1 = client->x;
	oldwin.y1 = win.y1 = client->y;
	oldwin.x2 = win.x2 = win.x1 + FULLWIDTH(client);
	oldwin.y2 = win.y2 = win.y1 + FULLHEIGHT(client);

	/*
	 * in relative_resize, we start with dx and dy bound to
	 * the corner that the mouse is nearest to.  otherwise
	 * dx and dy start unbound
	 */
	if (options.relative_resize) {
		if (ptr.x - client->x < FULLWIDTH(client) / 2) {
			reloff.x = -(ptr.x - client->x);
			dx = &win.x1;
		} else {
			reloff.x = FULLWIDTH(client) - (ptr.x - client->x);
			dx = &win.x2;
		}
		if (ptr.y - client->y < FULLHEIGHT(client) / 2) {
			reloff.y = -(ptr.y - client->y);
			dy = &win.y1;
		} else {
			reloff.y = FULLHEIGHT(client) - (ptr.y - client->y);
			dy = &win.y2;
		}
	} else {
		reloff.x = reloff.y = 0;
		dx = dy = NULL;
	}

	/* draw winbox of the first window position */
	draw_winbox(client->screen, client, win.x1, win.y1,
		win.x2 - win.x1 - DWIDTH(client),
		win.y2 - win.y1 - DHEIGHT(client));

	/* handle resize loop events */
	while (XMaskEvent(display, eventmask, &event), 1)
		switch (event.type) {
		case MotionNotify:
			resizemotion(&event.xmotion, client, &win,
				&oldwin, &ptr, &dx, &dy, &reloff);
			break;
		case ButtonRelease:
			resizerelease(client, &win, &oldwin);
			return;
		}
}

/*
 * interactively resize a window.  when the mouse crosses a
 * particular edge, that edge becomes bound to mouse movements.
 * moving the mouse across an opposite edite will unbind the
 * currently bound edge in favor of the new one.  the user
 * may elect to use a relative resize option (options.relative_resize),
 * in which case the mouse pointer starts bound to the corner that
 * it is closest too, and maintains that binding till opposite edges
 * get crossed.
 *
 * during resizing it is also neccesary to make sure that the
 * dimensions of the window do not contradict limits specified
 * through the XNormalHints structure in the client_t.
 */
void action_resize(client_t *client) {
	long eventmask;

	/*
	 * resizing isn't allowed for noresize clients; ungrab
	 * the grab we have from earlier passive grab and
	 * return
	 */

	if (client->flags.noresize || client->flags.shaded)
   {
		XUngrabPointer(display, CurrentTime);
		return;
	}

	/* grab for resizing the window */
	eventmask = ButtonReleaseMask | PointerMotionMask
		| ButtonMotionMask;
	if (XGrabPointer(display, client->screen->root, 0, eventmask,
			GrabModeAsync, GrabModeAsync, client->screen->root,
			cursor_move, CurrentTime) != GrabSuccess)
		return;

	/* prepare for resizing */
	XGrabServer(display);
	stacking_raise(client);

	/* call the window resizing loop */
	resizeloop(client, eventmask);
}

/* move a client window to the icon'd window list */
void action_iconify(client_t *client)
{
	if (client->flags.noiconify)
   {
		return;
   }

   //  we are telling external programs the window id of
   // client->window, but unmapping client->frame ... so if they issue a maprequest
   // for client->window I'm not seeing it in the main event loop .... geeeez


   log( 4, "unmapping window %d", (uint32_t) client->window );

	/* let plugs know, and then unmap it to iconic */

   // hmmm. even if I comment this out, we don't get the Unmap events from a panel
   // trying to restore the window .... and I can't figure out why!

   // bva. This needs to be client->window .... but that is causing a more violent
   // removal of the window that intended.

   // who the hell is removing our client_context ... I'm not seeing it on the restore.

   // ** windowmaker is restoring *both* the window and the frame ...
	plugin_iconify_notify(client);

	XUnmapWindow(display, client->window /*client->frame*/ );
   XUnmapWindow(display, client->frame  /*client->frame*/ );

	client_setstate(client, IconicState );


	/* get it out of the workspace */

   // bva. we want iconfied windows to stay in the task list ... so this
   // will have to change at some point, which has to be paired with cahnges
   // to action restore if we dont' want to die!
	desktop_rm_client(client);
   workspace_rm_client(client);

}

/* restore an iconified client window */
void action_restore(client_t *client)
{
	if (client->state != IconicState)
		return;

   log( 8, "================== action_restore ======================");

	/* set state to normal, and add it to the current workspace */
	client_setstate(client, NormalState);

	workspace_add_client(client->screen->desktop->current_space, client);
	desktop_add_client(client);

	plugin_restore_notify(client);

   // bva
	XMapWindow( display, client->frame );
   XMapWindow( display, client->window );
   // end bva

	stacking_raise(client);
	focus_setfocused(client);
}

void action_hzoom(client_t *client) {
	int tmpint = 0;

	if (client->flags.zoomed || client->flags.noresize
			|| client->flags.nomove)
		return;
	client->flags.hzoomed = !client->flags.hzoomed;
	if (!client->flags.hzoomed) {
		swap(tmpint, client->save_x, client->x);
		swap(tmpint, client->save_width, client->width);
		plugin_zoom_notify(client);
	} else {
		client->save_x = client->x;
		client->save_width = client->width;
		client->x = 0;
		client->width = client->screen->width - DWIDTH(client);
		plugin_unzoom_notify(client);
	}
	client_sizeframe(client);
	if (client->width == client->save_width && client->height == client->save_height)
		if (client->x != client->save_x || client->y != client->save_y)
			action_send_config(client);
}

void action_vzoom(client_t *client) {
	int tmpint = 0;

	if (client->flags.zoomed || client->flags.noresize ||
			client->flags.nomove)
		return;
	client->flags.vzoomed = !client->flags.vzoomed;
	if (!client->flags.vzoomed) {
		swap(tmpint, client->save_y, client->y);
		swap(tmpint, client->save_height, client->height);
		plugin_zoom_notify(client);
	} else {
		client->save_y = client->y;
		client->save_height = client->height;
		client->y = 0;
		client->height = client->screen->height - DHEIGHT(client);
		plugin_unzoom_notify(client);
	}
	client_sizeframe(client);
	if (client->width == client->save_width && client->height == client->save_height)
		if (client->x != client->save_x || client->y != client->save_y)
			action_send_config(client);
}

/* zoom (maximize) or unzoom a window */
void action_zoom(client_t *client) {
	int tmpint;

	/* zooming counts as both resizing and moving */
	if (client->flags.noresize || client->flags.nomove || client->flags.shaded)
   {
		return;
   }

	/*
	 * when zooming a client we save it's position in the
	 * save_ members that match with geometry/dgroup
	 * information for the client in it's unzoomed state.
	 * when unzooming we save the zoomed state into the
	 * save variables so it will be accessable to plugins
	 * in plugin_unzoom_notify.
	 */
	if (client->flags.zoomed) {
		client->flags.zoomed = 0;
		swap(tmpint, client->save_x, client->x);
		swap(tmpint, client->save_y, client->y);
		swap(tmpint, client->save_width, client->width);
		swap(tmpint, client->save_height, client->height);
		if (options.fullscreen_zoom) {
			dgroup_t *dgrouptmp = client->dgroup;
			dgroup_switch(client, client->save_dgroup);
			client->save_dgroup = dgrouptmp;
		}
	} else {
		client->flags.zoomed = 1;
		client->save_x = client->x;
		client->save_y = client->y;
		client->save_width = client->width;
		client->save_height = client->height;
		if (options.fullscreen_zoom) {
			client->save_dgroup = client->dgroup;
			dgroup_switch(client, &dgroup_empty);
		}
		if (xinerama_zoom(client)) {
         log( 6, "^^^^^^^^^^^^^ changing clientx to: %d", 0 );
			client->x = 0;
			client->y = 0;
			client->width = client->screen->width - DWIDTH(client);
			client->height = client->screen->height - DHEIGHT(client);
		}
		stacking_raise(client);
		focus_setfocused(client);
	}

	/* notify plugins */
	if (client->flags.zoomed)
		plugin_zoom_notify(client);
	else
		plugin_unzoom_notify(client);

	/*
	 * It is neccesary to send a synth configure notify if we didn't
	 * resize the client, but only moved it, because it wont recieve
	 * a real one in that case.
	 */
	client_sizeframe(client);
	if (client->width == client->save_width && client->height == client->save_height)
		if (client->x != client->save_x || client->y != client->save_y)
			action_send_config(client);
}

/* shade or unshade */
void action_shade(client_t *client)
{
	int tmpint;

	/* zooming counts as both resizing and moving */
	if( client->flags.noresize || client->flags.nomove )
    {
       return; // ---------->
    }

	/*
	 * when shading a client we save it's position in the
	 * save_ members that match with geometry/dgroup
	 * information for the client in it's unshaded state.
	 */
	if( client->flags.shaded )
    {
       client->flags.shaded = 0;
       swap(tmpint, client->save_width, client->width);
       swap(tmpint, client->save_height, client->height);

       // Keeping this as a reminder, that if we don't like the bottom
       // decorations on the shaded window, we could create a new dgroup
       // if (options.fullscreen_zoom)
       // {
       //    dgroup_t *dgrouptmp = client->dgroup;
       //    dgroup_switch(client, client->save_dgroup);
       //    client->save_dgroup = dgrouptmp;
       // }
	}
    else
    {
       client->flags.shaded = 1;
       client->save_width = client->width;
       client->save_height = client->height;

       // Set the client's height to 1 as the big shading trick
       client->height = 1;

       // Keeping this as a reminder, that if we don't like the bottom
       // decorations on the shaded window, we could create a new dgroup
       // if (options.fullscreen_zoom)
       // {
       //    client->save_dgroup = client->dgroup;
       //    dgroup_switch(client, &dgroup_empty);
       // }

       stacking_raise(client);
       focus_setfocused(client);
	}

   client_sizeframe(client);

	/* notify plugins */
	if( client->flags.shaded )
   {
      plugin_shade_notify(client);
   }
	else
   {
      plugin_unshade_notify(client);
   }

}


/******************************************************************************************/

void action_move_win(client_t* client, int direction, int val)
{
    int tmp;
    if ( (last_focused_client == NULL) || (client->window != last_focused_client->window) ) {
        last_focused_client = client;
        client->speed = 1;
    }
    tmp=val * client->speed;

    if (direction)
        client->y += tmp;
    else
        client->x += tmp;

    log( 6, "^^^^^^^^^^^^^ changing clientx to: %d", client->x );

    XMoveWindow(display, client->frame, client->x, client->y);
    action_send_config(client);
    plugin_geometry_change(client);
    client->speed++;
}

void action_update_dim(client_t* client, int direction,int val)
{
    int tmp;
    if ( (last_focused_client == NULL) || (client->window != last_focused_client->window) ) {
        last_focused_client = client;
        client->speed = 1;
    }
    tmp=val * client->speed;
    if (direction)
        client->height += tmp;
    else
        client->width += tmp;
    client_sizeframe(client);
    action_send_config(client);
    plugin_geometry_change(client);
    client->speed++;
}

