//-*-c++-*-
// -------------------------------------------
// RCS data:
// $Date: 2003/06/23 14:47:22 $
// $Revision: 1.1.1.1 $
// $Source: /cvsroot/miwm/miwm/miwm/client.cc,v $
// $Id: client.cc,v 1.1.1.1 2003/06/23 14:47:22 bwise837 Exp $
// $RCSfile: client.cc,v $
// -------------------------------------------
// Copyright by Ben Paul Wise.
// -------------------------------------------
// 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., 675 Mass Ave, Cambridge, MA 02139, USA.
// -------------------------------------------

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	<time.h>

#include	<unistd.h>

#include	<X11/X.h>
#include	<X11/Xlib.h>
#include	<X11/Xutil.h>


#include	"miwm.h"
#include	"client.h"
#include	"struct.h"
#include "ws.h"

Client *current;

int highestClientID = 0;

Edge interacting_edge;

static void sendcmessage(Window, Atom, long, long);

// -------------------------------------------

void
setactive(Client *c, int on, long timestamp) {
  int inhibit = isshaped(c->window);

  if (c == 0 || hidden(c))
    return;

  if (!inhibit) {
    if (on && c->accepts_focus && c->name) {
      XMoveResizeWindow(dpy, c->parent,
			c->size.x, c->size.y - titleHeight(),
			c->size.width, c->size.height + titleHeight());
      XMoveWindow(dpy, c->window, border, border + titleHeight());
    } else {
      XMoveResizeWindow(dpy, c->parent, c->size.x, c->size.y,
			c->size.width, c->size.height);
      XMoveWindow(dpy, c->window, border, border);
    }
  }

  if (on && c->accepts_focus) {
    XSetInputFocus(dpy, c->window, RevertToPointerRoot, CurrentTime);
    if (c->proto & Ptakefocus)
      sendcmessage(c->window, wm_protocols, wm_take_focus, timestamp);
    cmapfocus(c);
  }

  if (!inhibit)
    theWM->drawClientBorder(c, on);

  return;
}



Client *getClient(Window w) {
  Client *c;
  Node *cND;

  if (w == 0 || w == root)
    return 0;

  // find the client corresponding to this window.
  for (cND = theWM->allClients->first;
       cND != NULL;
       cND = theWM->allClients->nextNode(cND)) {
    c = ((Client*) cND->data);

    // if this client is NULL, we are in deep doo doo
    assert (NULL != c);

    if (c->window == w || c->parent == w)
      return c;
  }

  // Nothing found.
  return NULL;
}


Client* addClient(Window w) {
  Client* c;

  if (w == 0 || w == root)
    return 0;

  // see if we already have a client corresponding to this window.
  c = getClient(w);
  if (c != NULL)
    return c;

  // Because this is 'calloc' rather than 'new',
  // I can not use my memory-leak detector to
  // look for leaks.
  // This is 'calloc' rather than 'new' because
  // I could never get it to work when I allocate
  // via 'new'. The attributes are initialized differently,
  // and I can not figure out how.
  //c = (Client*) calloc(1, sizeof *c);
  c = new Client();
  c->opaqueMoveP = 0; // default is wire-move
  c->gripEdge = ENone;
  c->boxStatus = DBundrawn;
  c->outlinedP = 0;
  c->sticky = 0;
  c->window = w;
  c->parent = root;
  c->hidden = False;
  c->isMapped = 1;
  c->state = WithdrawnState;
  c->internal_state = INormal;
  c->cmap = None;
  c->name = NULL;
  c->fallback_name = NULL;
  c->ncmapwins = 0;
  c->cmapwins = 0;
  c->wmcmaps = 0;
  c->accepts_focus = 1;
  c->workSpace = theWM->workSpace;
  c->clientID = highestClientID;

  c->next = NULL;
  highestClientID = highestClientID + 1;

  theWM->allClients->append((void*)c);

  return c; 
}


void
removeClient(Client *c) {

  if (c == 0)
    return;

  // Remove the client from our client list.
  Node* cND = theWM->allClients->inList(c);

  assert (cND != NULL);
  theWM->allClients->remove(cND);

  if (hidden(c))
    unhidec(c, 0);

  if (c == current)
    current = 0;

  if (c->parent != root)
    XDestroyWindow(dpy, c->parent);

  if (c->ncmapwins != 0) {
    XFree(c->cmapwins);
    free(c->wmcmaps);
  }

  cout << "Destroying client "<< c->clientID <<endl;

  if (c->name != 0) {
    cout << "Destroying client "<< c->clientID<<" name |"<<c->name<<"|"<<endl;
    XFree(c->name);
    c->name = 0;
  }

  if (c->fallback_name != 0) {
    cout << "Destroying client "<< c->clientID<<" fallback_name |"<<c->fallback_name<<"|"<<endl;
//     XFree(c->fallback_name);
    delete c->fallback_name;
    c->fallback_name = 0;
  }

  //  free(c);
  delete c;
  c = NULL;
  return;
}

void
WindowManager::positionExpose(void) {
  char buf[4*2 + 3 +1];
  Client* c = current;

  sprintf(buf, "%i , %i", c->rX, c->rY - titleHeightc(c));
  XDrawString(dpy, popup, size_gc,
	      (80 - XTextWidth(popup_font, buf, strlen(buf))) / 2,
	      popup_font->ascent + 1, buf, strlen(buf));
  return;
}

void
WindowManager::sizeExpose(void) {
  char buf[4*2 + 3 +1];
  int width, height;
  Client* c = current;
  if (NULL != c) { // client did not die during the operation
    
  width = c->rW - 2 * border;
  height = c->rH - (2 * border);

    // We report 80x24 for an xterm
  // even when a scrollbar is present.
  if (c->size.flags & (PMinSize|PBaseSize) && c->size.flags & PResizeInc) {
    if (c->size.flags & PBaseSize) {
      width -= c->size.base_width;
      height -= c->size.base_height;
    } else {
      width -= c->size.min_width;
      height -= c->size.min_height;
    }
  }

  if (c->size.width_inc != 0)
    width /= c->size.width_inc;
  if (c->size.height_inc != 0)
    height /= c->size.height_inc;

  sprintf(buf, "%i x %i", width, height);
  XDrawString(dpy, popup, size_gc,
	      (80 - XTextWidth(popup_font, buf, strlen(buf))) / 2,
	      popup_font->ascent + 1, buf, strlen(buf));
  }
  else { // client died during the operation. CYA
    XUnmapWindow(dpy, popup);
    mode =   wmReady;
  }
  return;
}

void
closeClient(Client *c) {
  if (c == 0)
    return;

  //  Terminate things nicely if possible. Be brutal otherwise.
  if (c->proto & Pdelete) {
    sendcmessage(c->window, wm_protocols, wm_delete, CurrentTime);
  } else {
    XKillClient(dpy, c->window);
  }
  return;
}

void
setClientState(Client *c, int state) {
  long	data[2];

  data[0] = (long) state;
  data[1] = (long) None;

  c->state = state;
  XChangeProperty(dpy, c->window, wm_state, wm_state, 32, PropModeReplace,
		  (unsigned char *) data, 2);
}

static void
sendcmessage(Window w, Atom a, long data0, long data1) {
  XEvent	ev;
  long	mask;

  memset(&ev, '\0', sizeof ev);
  ev.xclient.type = ClientMessage;
  ev.xclient.window = w;
  ev.xclient.message_type = a;
  ev.xclient.format = 32;
  ev.xclient.data.l[0] = data0;
  ev.xclient.data.l[1] = data1;
  mask = (w == root) ? SubstructureRedirectMask : 0L;

  XSendEvent(dpy, w, False, mask, &ev);
}

void
WindowManager::freeAllClients(void) {
  Client *c;
  Node *cND;
  XWindowChanges wc;
  cout << "Freeing " << theWM->allClients->length() << " clients"<<endl;
  for (cND = theWM->allClients->first;
       cND != NULL;
       cND = theWM->allClients->nextNode(cND)) {
    c = ((Client*) cND->data);


    if (NULL == c) {
      // if this client is NULL, we are in deep doo doo
      cout << "Found a null client in the client list" << endl << flush;

      // this was a pause before crashing
  //    sleepval.tv_sec = 0;
  //    sleepval.tv_usec = 1250000;
  //    cout <<  "About to sleep "<<  (sleepval.tv_usec)/1000;
  //    cout << " millisec" << flush;
  //    select(0, 0, 0, 0, &sleepval);
  //    cout << " ... done sleeping" << endl << flush;

      assert (NULL != c);
    }

    if (NULL != c->name) {
      cout << "Moving client " << c->clientID << " named |" << c->name << endl;
    }
    else  if (NULL != c->fallback_name) {
      cout << "Moving client " << c->clientID << " fallback_named |" << c->fallback_name << endl;
    }
    else {
      cout << "Moving NAMELESS client " << c->clientID<<endl<<flush;

    }

    innerSendClienttoWS(c, 1);
    int not_mapped = !normal(c);

    // Remap the window if it was hidden.
    if (not_mapped) {
      XMapWindow(dpy, c->parent);
      XMapWindow(dpy, c->window);
      c->isMapped = 1;
    }

    // Reparent it, and then push it to the bottom if it was hidden.
    XReparentWindow(dpy, c->window, root, 
		    c->size.x + border, c->size.y + border);
    if (not_mapped)
      XLowerWindow(dpy, c->window);

    // Give it back its initial border width.
    wc.border_width = c->border;
    XConfigureWindow(dpy, c->window, CWBorderWidth, &wc);
  }
  return;
}

extern void
Client_ColorMap(XEvent *e) {
  int	i;
  Client	*c;
  Node *cND;

  for (cND = theWM->allClients->first;
       cND != NULL;
       cND = theWM->allClients->nextNode(cND)) {
    c = ((Client*) cND->data);
    

    // if this client is NULL, we are in deep doo doo
    assert (NULL != c);


    for (i = 0; i < c->ncmapwins; i++) {
      if (c->cmapwins[i] == e->xcolormap.window) {
	c->wmcmaps[i] = e->xcolormap.colormap;
	if (c == current)
	  cmapfocus(c);
	return;
      }
    }
  }
  return;
}

extern int
hidden(Client *c) {
  if (c->state == IconicState)
    return 1;
  else
    return 0;
}

extern int
withdrawn(Client *c) {
  return c->state == WithdrawnState;
}

extern int
normal(Client *c) {
  return c->state == NormalState;
}

// -------------------------------------------
// window manager manipulations of client appearance

void
WindowManager::backClient(Client *c) {
  if (c == 0)
    return;

  XLowerWindow(dpy, c->window);
  XLowerWindow(dpy, c->parent);
  return;
}


void
WindowManager::raiseClient(Client *c) {
  if (c == 0)
    return;
//   workSpace->topClient = c;
  XMapRaised(dpy, c->parent);
  XMapRaised(dpy, c->window);
  c->isMapped = 1;
  return;
}

void
WindowManager::drawDeadlyBox(Client *c) {
  int	quarter;
  XSetForeground(dpy,  theWM->gc_frame, theWM->boxColor.pixel);
  quarter = (border + titleHeight()) / 4;
  if (DBundrawn == c->boxStatus)
    c->boxStatus = DBunarmed;

  switch (c->boxStatus) {
  case DBunarmed:
    XDrawRectangle(dpy, c->parent, theWM->gc_frame,
		   quarter, quarter, 2 * quarter, 2 * quarter);
    break;

  case DBarmed:
    // xfillrectangle is off by one from xdrawrectangle,
    // and the line width in gc_frame is 2, 
    // so the offset in initial (x,y) is +1 - linewidth = -1
    // and the total width is 2*quarter + linewidth
    XFillRectangle(dpy, c->parent, theWM->gc_frame,
		   quarter-1, quarter-1, 2 * quarter + 2, 2 * quarter + 2);
    break;

  default:
    // this should not happen
    break;
  }

  return;
}

void
WindowManager::drawTitle(Client *c) {
  int	quarter;
  quarter = (border + titleHeight()) / 4;
  XSetForeground(dpy,  theWM->gc_frame, theWM->titleColor.pixel);
  if (c->name != 0)
    XDrawString(dpy, c->parent, theWM->gc_frame, border + 3 * quarter,
		font->ascent + font->descent, c->name, strlen(c->name));

  return;
}

void
WindowManager::drawClientBorder(Client *c, int active) {
  XSetWindowAttributes attr; // rely on initialization by system ....

  if (c->parent == root || c->parent == 0)
    return;

  if (active) {
    if ((0 == usePixmapsP) || (None == activeBgd))
      XSetWindowBackground(dpy, c->parent, theWM->activeBorderColor.pixel);
    else {
      attr.background_pixmap = theWM->activeBgd;
      XChangeWindowAttributes(dpy, c->parent, CWBackPixmap  , &attr);
    }
  }


  else {  // inactive window
    if ((0 == usePixmapsP) || (None == inactiveBgd)) {
      XSetWindowBackground(dpy, c->parent, theWM->inactiveBorderColor.pixel);
    }
    else {
      attr.background_pixmap = theWM->inactiveBgd;
      XChangeWindowAttributes(dpy, c->parent, CWBackPixmap  , &attr);
    }
  }

  XClearWindow(dpy, c->parent);

  // draw the thin outline between the client and its border
  if (!active)
    XDrawRectangle(dpy, c->parent, gc_thin, border - 1, border - 1,
 		   c->size.width - 2 * border + 1, c->size.height - 2 * border + 1);

  if (active && c->accepts_focus && c->name) {
    drawDeadlyBox(c);
    drawTitle(c);
  }
  // return to normal

  return;
}

void WindowManager::drawClientOutline(Client* c) {
  // use rX, rY, rW, rH, and title height
//   cout << "draw client outline" << endl << flush;

  assert (c->rW * c->rH > 0);
//   GC gcTmp  = gc;

  // I find it much easier to use a bunch of mnemonic variables
  // than to try to follow convoluted X calls. Any decent
  // compiler will optimize them away, in any case.
  //   int w = c->size.width;
  //   int h = c->size.height;

  // top left corner
  int x1 = c->rX + 1;  // xfillrectangle is off by one from xdrawrectangle!
  int y1 = c->rY + 1; // xfillrectangle is off by one from xdrawrectangle!

  // top right corner
  int x2 = x1 + c->rW;
  int y2 = y1;

  // bottom left corner
//   int x3 = x1;
  int y3 = y1 + c->rH;

  // bottom right corner
//    int x4 = x1 + c->rW;
//    int y4 = y1 + c->rH;

   int tH = theWM->titleHeightc(c);

   // coords of middle points
   int xm, ym;

   int i, N;
   N = 3;

   // draw the window with crossbars
   for (i = 0 ; i <= N; i++) {
       xm = x1 + ((i * (x2 - x1)) / N);
       ym = y1 + ((i * (y3 - y1)) / N);
       XDrawLine(dpy, root, gc, xm, y1, xm, y3);
       XDrawLine(dpy, root, gc, x1, ym, x2, ym);
     }

   // outline the title bar, if any
   if (tH > 0) {
     XDrawLine(dpy, root, gc, 
	       x1, y1,
	       x1, y1 - tH);
     XDrawLine(dpy, root, gc, 
	       x1, y1 - tH,
	       x2, y2 - tH);

     XDrawLine(dpy, root, gc, 
	       x2, y2,
	       x2, y2 - tH);
   }
 return;
}


// -------------------------------------------
// WIndow Manager resizing clients 
void 
WindowManager::startResizingClient(XButtonEvent *xbe, Client* c) {
  Cursor	cursor;
  assert (wmResizing == mode);
  raiseClient(c);
  warpPointerToBorder(c);
  c->gripEdge = whichEdge(c, xbe->x_root, xbe->y_root);
  cursor = edgecursor(c->gripEdge);
//   cout << "grip edge: "<<c->gripEdge << endl;

  XGrabServer(dpy);
  XChangeActivePointerGrab(dpy, 
			   theWM->moving_event_mask, 
			  cursor, CurrentTime);
  c->sX = c->size.x;
  c->sY = c->size.y;
  c->sW = c->size.width;
  c->sH = c->size.height;
  c->sBX = xbe->x_root;
  c->sBY = xbe->y_root;
  c->rX = c->sX;
  c->rY = c->sY;
  c->rW = c->sW;
  c->rH = c->sH;
  c->rBX = c->sBX;
  c->rBY = c->sBY;

 // do NOT draw the first outline here: wait for a motion event
  c->outlinedP = 0;
  return;
}

void 
WindowManager::motionNotifyResizing() {
  assert (wmResizing == mode);
  int dx, dy;
  Client* c = current;

  if (NULL != c) {  // client did not die during the operation

  if (1 == c->outlinedP) {
    drawClientOutline(c); // erase the previous one
    c->outlinedP = 0;
  }

  getMouse (&c->rBX, &c->rBY);
  dx = c->rBX - c->sBX;
  dy = c->rBY - c->sBY;

  switch (c->gripEdge) {
  case ETopLeft:
    c->rX = c->sX + dx;
    c->rY = c->sY + dy;
    c->rW = c->sW - dx;
    c->rH = c->sH - dy;
    break;

  case ETop:
    c->rY = c->sY + dy;
    c->rH = c->sH - dy;
    break;

  case ETopRight:
    c->rY = c->sY + dy;
    c->rW = c->sW + dx;
    c->rH = c->sH - dy;

    break;

  case ELeft:
    c->rX = c->sX + dx;
    c->rW = c->sW - dx;
    break;

  case ERight:
    c->rW = c->sW + dx;
    break;

  case EBottomLeft:
    c->rX = c->sX + dx;
    c->rW = c->sW - dx;
    c->rH = c->sH + dy;
    break;

  case EBottom:
    c->rH = c->sH + dy;
    break;

  case EBottomRight:
    c->rH = c->sH + dy;
    c->rW = c->sW + dx;
    break;

  default:
    // this should not happen
    break;


  }

  // enforce somewhat sane boundaries.
  int ix, iy;
  int th = theWM->titleHeightc(c);
  c->rW = imax(6*border, c->rW);
  c->rW = imax(c->size.min_width, c->rW);
  if (c->size.width_inc > 1) {
    ix = c->rW / c->size.width_inc;
    c->rW = ix * c->size.width_inc;
  }
  else if (c->size.width_inc == 0)
    c->rW = c->sW;


  c->rH = imax(6*border, c->rH);
  c->rH = imax(c->size.min_height, c->rH);
  if (c->size.height_inc > 1) {
    iy = c->rH / c->size.height_inc;
    c->rH = iy * c->size.height_inc;
  }
  else  if (c->size.height_inc == 0)
      c->rH = c->sH;

  if (1 == protectTopLeft) {
    // ensure the top-left corner is on the screen
    c->rX = imax(0 , c->rX);
    c->rX = imin(c->rX, display_width - 2 * border);
    c->rY = imax(th , c->rY);
    c->rY = imin(c->rY, display_height - 2 * border);
  }
  // there is a slight bug in the above: it you keep trying to
  // resize the top edge off the screen, it will sometimes bump
  // the lower edge down by th


  if (0 == c->outlinedP) {
    drawClientOutline(c); // draw the next one
    c->outlinedP = 1;
  } 

  positionFeedbackWindow();
  }
  else {     // client died during the operation. CYA.
    XUnmapWindow(dpy, popup);
    mode =   wmReady;
  }
  return;
}


void 
WindowManager::buttonReleaseResizing() {
  assert (wmResizing == mode);
  Client* c = current;
  XUngrabServer(dpy);
  //   cout << "Finish resizing" << endl << flush;
  int th = titleHeightc(c);

  if (1 == c->outlinedP) {
    drawClientOutline(c); // erase the last one
    c->outlinedP = 0;
  }

  c->sX = c->rX;
  c->sY = c->rY;
  c->size.x = c->rX;
  c->size.y = c->rY;
  c->sW = c->rW;
  c->sH = c->rH;
  c->size.width = c->rW;
  c->size.height = c->rH;
  c->sBX = c->rBX;
  c->sBY = c->rBY;
  assert (c == current);
  XMoveResizeWindow(dpy, c->parent,
		    c->sX, c->sY - th,
		    c->sW, c->sH + th);
  sendConfigureNotify(c);
  XMoveResizeWindow(dpy, c->window, border, border + th,
		    c->sW - 2 * border, c->sH - 2 * border);
  XUnmapWindow(dpy, popup);
  mode = wmReady;
  return;
}


Edge
WindowManager::whichEdge(Client *c, int bx, int by) {
  int sensWidth = 2*border;
  int x = c->size.x;
  int y = c->size.y;
  int w = c->size.width;
  int h = c->size.height;
  int th = titleHeightc(c);

  if ((abs (x - bx) <= sensWidth) && (abs ((y - th) - by) <= sensWidth + th))
    return ETopLeft;

  if ((abs (x - bx) <= sensWidth) && (abs ((y + h) - by) <= sensWidth))
    return EBottomLeft;

  if ((abs ((x+w) - bx) <= sensWidth) && (abs ((y - th) - by) <= sensWidth + th))
    return ETopRight;

  if ((abs ((x+w) - bx) <= sensWidth) && (abs ((y + h) - by) <= sensWidth))
    return EBottomRight;

  if (abs(x-bx) <= sensWidth)
    return ELeft;

  if (abs((y+h)-by) <= sensWidth)
    return EBottom;

  if (abs((x+w)-bx) <= sensWidth)
    return ERight;
  // the only remaining case!
  return ETop;
}

// -------------------------------------------
// WIndow Manager moving clients around
void 
WindowManager::startMovingClient(XEvent *xe, Client* c) {
  assert (wmMoving == mode);
  warpPointerToBorder(c);
  Cursor cursor;
  // start moving client

  c->gripEdge = ENone;
  cursor = edgecursor(c->gripEdge);
  XGrabServer(dpy);
  XChangeActivePointerGrab(dpy, 
			   theWM->moving_event_mask, 
			   cursor, CurrentTime);


  if (c->size.width * c->size.height * 100 <= opaqueMoveSize * display_width * display_height)
    c->opaqueMoveP = 1; 
  else
    c->opaqueMoveP = 0; 

  c->sX = c->size.x;
  c->sY = c->size.y;
  c->sW = c->size.width;
  c->sH = c->size.height;
  getMouse ( &c->sBX, &c->sBY);
  c->rX = c->sX;
  c->rY = c->sY;
  c->rW = c->sW;
  c->rH = c->sH;
  c->rBX = c->sBX;
  c->rBY = c->sBY;

  // do NOT draw the first outline here: wait for a motion event
  return;
}

void 
WindowManager::motionNotifyMoving() {
  assert (mode ==  wmMoving);
  int dx, dy;
  int xT, yT;
  Client* c = current;
  // if the client just terminated itself during the move,
  // the current client could be NULL. In that
  // case, abort it and go to the ready state
  if (NULL != c) {
    getMouse ( &c->rBX, &c->rBY);

  dx = c->rBX - c->sBX;
  dy = c->rBY - c->sBY;

  xT = c->sX + dx;
  yT = c->sY + dy;

  if (1 == protectTopLeft) {
    // ensure the top-left corner is on the screen
    // ensure the top-left corner is on the screen
    xT = imax(0 , xT);
    xT = imin(xT, display_width - 2 * border);
    yT = imax(theWM->titleHeightc(c) , yT);
    yT = imin(yT, display_height - 2 * border);
  }
  dx = xT - c->sX;
  dy = yT - c->sY;

  if (0 == c->opaqueMoveP) {
    if (1 == c->outlinedP) {
      theWM->drawClientOutline(c); // erase old one
      c->outlinedP = 0;
    }
    c->rX = c->sX + dx;
    c->rY = c->sY + dy;
    if (0 == c->outlinedP) {
      theWM->drawClientOutline(c); // draw the new one
      c->outlinedP = 1;
    }
  }
  else { // opaque moving
    c->rX = c->sX + dx;
    c->rY = c->sY + dy;

    c->sX = c->rX;
    c->sY = c->rY;
    c->sBX = c->rBX;
    c->sBY = c->rBY;
    c->size.x = c->rX;
    c->size.y = c->rY;
    // actually move the client
    XMoveWindow(dpy, c->parent,
		c->size.x, c->size.y - titleHeightc(c));
    sendConfigureNotify(c);
  }

  positionFeedbackWindow();
  }
  else  {       // client died during the operation. CYA.
    XUnmapWindow(dpy, popup);
    mode =   wmReady;
    }
  return;
}


void 
WindowManager::buttonReleaseMoving() {
  Client* c = current;
//   cout << "Stop moving" << endl << flush;

  XUngrabServer(dpy);
  if (0 == c->opaqueMoveP) {
    if (1 == c->outlinedP) {
      theWM->drawClientOutline(c); // erase last one
      c->outlinedP = 0;
    }
  }

  c->sX = c->rX;
  c->sY = c->rY;
  c->sBX = c->rBX;
  c->sBY = c->rBY;
  c->size.x = c->rX;
  c->size.y = c->rY;
  // actually move the client
   XMoveWindow(dpy, c->parent,
 	      c->size.x, c->size.y - titleHeightc(c));
  sendConfigureNotify(c);
  XUnmapWindow(dpy, popup);
  mode = wmReady;
  return;
}


void
WindowManager::positionFeedbackWindow() {
//   int x, y;
  Client* c = current;
  // must use starting coords, to avoid unpleasant stray lines on the client
  XMoveResizeWindow(dpy, popup,  c->sX + c->sW/4, c->sY + c->sH/4,
 		    80,  popup_font->ascent + popup_font->descent + 1);
  XMapRaised(dpy, popup);

  // popup contents get redrawn. Eventually, the function
  // positionExpose or sizeExpose will get called to do the actual redrawing.
  XClearArea(dpy, popup, 0, 0, 0, 0, True);
  return;
}

void 
WindowManager::startKillingClient(XButtonEvent *xbe, Client* c) {
  assert (wmKilling == mode);
   XChangeActivePointerGrab(dpy, 
 			   theWM->moving_event_mask, 
 			   frame_cursor, CurrentTime);
  c->boxStatus = DBarmed;
  drawClientBorder(c, 1);
  return;
}

void 
WindowManager::motionNotifyKilling() {
  assert (wmKilling == mode);
  Client* c = current;
  BoxStatus oldBS =  c->boxStatus;
  BoxStatus nuBS;
  if (1 ==  mouseInDeadlyBox(c))
    nuBS = DBarmed;
  else
    nuBS = DBunarmed;

  if (oldBS != nuBS) {
    c->boxStatus = nuBS;
    drawClientBorder(c, 1);
  }
  return;
}

void 
WindowManager::buttonReleaseKilling() {
  assert (wmKilling == mode);
//   if in the current clients deadly box, kill the current client
  if (1 == mouseInDeadlyBox(current))
    closeClient(current);
  mode = wmReady;
  return;
}


// -------------------------------------------
// end of client.cc
// -------------------------------------------
