/*
 * xvmisc.c - random 'handy' routines used in XV
 *
 *  Author:    John Bradley, University of Pennsylvania
 *                (bradley@cis.upenn.edu)
 *
 *  Contains:
 *     Window CreateWindow(name, geom, w, h, fg, bg)
 *     void   CenterString(win, str, x, y)
 *     void   ULineString(win, str, x, y)
 *     int    StringWidth(str)
 *     void   FakeButtonPress(bptr)
 *     void   DimRect(win, x, y, w, h, bg);
 *     void   SetCropString(active)
 *     void   Warning()
 *     void   FatalError(str)
 *     void   Quit(int)
 *     void   LoadFishCursors()
 *     void   WaitCursor()
 *     void   SetCursors(int)
 *     void   Timer(milliseconds)
 */

/*
 * Copyright 1989, 1990, 1991, 1992 by John Bradley and
 *                       The University of Pennsylvania
 *
 * Permission to use, copy, and distribute for non-commercial purposes,
 * is hereby granted without fee, providing that the above copyright
 * notice appear in all copies and that both the copyright notice and this
 * permission notice appear in supporting documentation. 
 *
 * The software may be modified for your own purposes, but modified versions
 * may not be distributed.
 *
 * This software is provided "as is" without any expressed or implied warranty.
 *
 * The author may be contacted via:
 *    US Mail:   John Bradley
 *               GRASP Lab, Room 301C
 *               3401 Walnut St.  
 *               Philadelphia, PA  19104
 *
 *    Phone:     (215) 898-8813
 *    EMail:     bradley@cis.upenn.edu       
 */


#define NEEDSTIME
#include "xv.h"
#include "bitmaps.h"



/***************************************************/
Window CreateWindow(name,geom,w,h,fg,bg)
char         *name, *geom;
unsigned int  w,h;
unsigned long fg, bg;
{
  Window               win;
  XSetWindowAttributes xswa;
  unsigned long        xswamask;
  XWMHints             xwmh;
  XSizeHints           hints;
  int                  i,x,y;

  /* note that only x,y are gotten from geom spec.  w,h are fixed */
  x = y = 1;
  i = XParseGeometry(geom,&x,&y,&w,&h);

  if ((i&XValue || i&YValue)) hints.flags = USPosition;  
                         else hints.flags = PPosition;

  hints.flags |= USSize;

  if (i&XValue && i&XNegative)
    x = dispWIDE - w - abs(x);
  if (i&YValue && i&YNegative) 
    y = dispHIGH - h - abs(y);

#ifdef FOO
  if (vrootW != rootW) { /* virtual window manager running */
    int x1,y1;
    Window child;
    XTranslateCoordinates(theDisp, rootW, vrootW, x, y, &x1, &y1, &child);
    fprintf(stderr,"translate:  %d,%d -> %d,%d\n",x,y,x1,y1);
  }
#endif

  hints.x = x;                  hints.y = y;
  hints.width = w;              hints.height = h;
  hints.min_width  = w;         hints.min_height = h;
  hints.max_width  = w;         hints.max_height = h;
  hints.flags |= PMaxSize | PMinSize;

  xswa.background_pixel = bg;
  xswa.border_pixel     = fg;
  xswamask = CWBackPixel | CWBorderPixel;

  win = XCreateWindow(theDisp, rootW, x, y, w, h, 
			bwidth, CopyFromParent, CopyFromParent,
			CopyFromParent, xswamask, &xswa);
  if (!win) return(win);   /* leave immediately if couldn't create */

  XSetStandardProperties(theDisp, win, name, name, None, NULL, 0, &hints);

  xwmh.input = True;
  xwmh.flags = InputHint;
  if (iconPix) { xwmh.icon_pixmap = iconPix;  xwmh.flags |= IconPixmapHint; }
  XSetWMHints(theDisp, win, &xwmh);

  return(win);
}
  


/**************************************************/
void CenterString(win,str,x,y)
Window win;
char *str;
int x,y;
{
  XDrawString(theDisp, win, theGC, CENTERX(mfinfo, x, str),
	    CENTERY(mfinfo, y), str, strlen(str));
}

  
/**************************************************/
void ULineString(win,str,x,y)
Window win;
char *str;
int x,y;
{
  XDrawString(theDisp, win, theGC, x, y, str, strlen(str));
  XDrawLine(theDisp, win, theGC, x, y+DESCENT-1, 
	    x+StringWidth(str), y+DESCENT-1);
}

  
/**************************************************/
int StringWidth(str)
char *str;
{
  return(XTextWidth(mfinfo, str, strlen(str)));
}

  
/***********************************/
void FakeButtonPress(bp)
BUTT *bp;
{
  /* called when a button keyboard equivalent has been pressed.
     'fakes' a ButtonPress event in the button, which A) makes the button
     blink, and B) falls through to ButtonPress command dispatch code */

  Window       rW, cW;
  int          x, y, rx, ry;
  unsigned int mask;
  XButtonEvent ev;

  ev.type = ButtonPress;
  ev.send_event = True;
  ev.display = theDisp;
  ev.window = bp->win;
  ev.root = rootW;
  ev.subwindow = (Window) NULL;
  ev.x = bp->x;
  ev.y = bp->y;
  ev.state = 0;
  ev.button = Button1;
  XSendEvent(theDisp, bp->win, False, NoEventMask, (XEvent *) &ev);

  /* if button1 is pressed, loop until RELEASED to avoid probs in BTTrack */
  while (XQueryPointer(theDisp, rootW, &rW,&cW,&rx,&ry,&x,&y,&mask)) {
    if (!(mask & Button1Mask)) break;    /* button released */
  }
}


/***********************************/
void DimRect(win, x, y, w, h, bg)
Window win;
int x,y,w,h;
u_long bg;
{
  /* stipple a rectangular region by drawing 'bg' where there's 1's 
     in the stipple pattern */

  XSetFillStyle(theDisp, theGC, FillStippled);
  XSetStipple(theDisp, theGC, grayStip);
  XSetForeground(theDisp, theGC, bg);
  XFillRectangle(theDisp,win,theGC,x,y,w,h);
  XSetFillStyle(theDisp, theGC, FillSolid);
}





/**************************************************/
void SetCropString(active)
int active;
{
  /* sets the crop string in the info box to be correct.  should
     be called whenever 'but[BCROP].active', cXOFF,cYOFF,cWIDE,cHIGH 
     are changed */

  /* if 'active' there's a cropping rectangle drawn on the picture.
     the string should reflect that.  */

  if (active) {
    /* figure out current cropping rectangle in 'pic' coordinates */
    int x,y,x1,y1,w,h;
    int cx,cy,dx,dy;
    
    if (crx1<crx2) cx=crx1; else cx=crx2;
    if (cry1<cry2) cy=cry1; else cy=cry2;
    dx = abs(crx1-crx2);  dy = abs(cry1-cry2);

    x = cXOFF + (cx * cWIDE) / eWIDE;
    y = cYOFF + (cy * cHIGH) / eHIGH;
    x1 = cXOFF + ((cx+dx) * cWIDE) / eWIDE;
    y1 = cYOFF + ((cy+dy) * cHIGH) / eHIGH;
    w = (x1 - x) + 1;
    h = (y1 - y) + 1;

    if (w<1) w = 1;
    if (x+w > pWIDE) w = pWIDE - x;
    if (h<1) h = 1;
    if (y+h > pHIGH) h = pHIGH - y;

    SetISTR(ISTR_CROP, "%dx%d rectangle starting at %d,%d", w, h, x, y);
  }

  else {   /* cropping rectangle is turned off */
    if (cpic != pic)
      SetISTR(ISTR_CROP, "%dx%d rectangle starting at %d,%d", 
	      cWIDE, cHIGH, cXOFF, cYOFF);
    else
      SetISTR(ISTR_CROP, "<none>");
  }
}


/***********************************/
void Warning()
{
  char *st;

  if (infoUp || ctrlUp) sleep(5);   /* give 'em time to read message */
  else {
    st = GetISTR(ISTR_INFO);
    fprintf(stderr,"%s: %s\n", cmd, st);
  }
}
    

/***********************************/
void FatalError (identifier)
      char *identifier;
{
  fprintf(stderr, "%s: %s\n",cmd, identifier);
  Quit(-1);
}


/***********************************/
void Quit(i)
int i;
{ 
  /* called when the program exits.  frees everything explictly created
     EXCEPT allocated colors.  This is used when 'useroot' is in operation,
     as we have to keep the alloc'd colors around, but we don't want anything
     else to stay */

  if (!theDisp) exit(i);   /* called before connection opened */

  if (useroot && i==0) {   /* save the root info */
    SaveRootInfo();

    /* kill the various windows, since we're in RetainPermanent mode now */
    if (dirW)  XDestroyWindow(theDisp, dirW);
    if (infoW) XDestroyWindow(theDisp, infoW);
    if (ctrlW) XDestroyWindow(theDisp, ctrlW);
    if (gamW)  XDestroyWindow(theDisp, gamW);

    XFlush(theDisp);
  }

  exit(i);
}


static Cursor flcurs, fl1curs, fmcurs, fr1curs, frcurs;

/***********************************/
void LoadFishCursors()
{
#define fc_w 16
#define fc_h 16 

  Pixmap flpix,flmpix,fmpix,fmmpix,frpix,frmpix;
  Pixmap fl1pix, fl1mpix, fr1pix, fr1mpix;
  XColor fg, bg;

  flcurs = fl1curs = fmcurs = fr1curs = frcurs = (Pixmap) NULL;

  flpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left_bits,
	     fc_w, fc_h, 1, 0, 1);
  flmpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_leftm_bits,
	     fc_w, fc_h, 1, 0, 1);

  fl1pix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left1_bits,
	     fc_w, fc_h, 1, 0, 1);
  fl1mpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_left1m_bits,
	     fc_w, fc_h, 1, 0, 1);

  fmpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_mid_bits,
	     fc_w, fc_h, 1, 0, 1);
  fmmpix= XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_midm_bits,
	     fc_w, fc_h, 1, 0, 1);

  fr1pix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right1_bits,
	     fc_w, fc_h, 1, 0, 1);
  fr1mpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right1m_bits,
	     fc_w, fc_h, 1, 0, 1);

  frpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_right_bits,
	     fc_w, fc_h, 1, 0, 1);
  frmpix = XCreatePixmapFromBitmapData(theDisp, ctrlW, fc_rightm_bits,
	     fc_w, fc_h, 1, 0, 1);

  if (!flpix || !flmpix || !fmpix || !fmmpix || !frpix || !frmpix
      || !fl1pix || !fl1mpix || !fr1pix || !fr1mpix) return;

  fg.red = fg.green = fg.blue = 0;
  bg.red = bg.green = bg.blue = 0xffff;

  flcurs = XCreatePixmapCursor(theDisp, flpix, flmpix, &fg, &bg, 8,8);
  fl1curs= XCreatePixmapCursor(theDisp, fl1pix,fl1mpix,&fg, &bg, 8,8);
  fmcurs = XCreatePixmapCursor(theDisp, fmpix, fmmpix, &fg, &bg, 8,8);
  fr1curs= XCreatePixmapCursor(theDisp, fr1pix,fr1mpix,&fg, &bg, 8,8);
  frcurs = XCreatePixmapCursor(theDisp, frpix, frmpix, &fg, &bg, 8,8);

  if (!flcurs || !fmcurs || !frcurs || !fl1curs || !fr1curs) 
    { flcurs = fmcurs = frcurs = (Cursor) NULL; }
}

static int fishno=0;


/***********************************/
void WaitCursor()
{
  SetCursors(fishno);
  fishno = (fishno+1) % 8;
}


/***********************************/
void SetCursors(n)
int n;
{
  Cursor c;

  c = cross;
  /* if n < 0   sets normal cursor in all windows
     n = 0..6   cycles through fish cursors */

  if (n<0) {
    if (!useroot && mainW) XDefineCursor(theDisp, mainW, cross);
    if (infoW) XDefineCursor(theDisp, infoW, arrow);
    if (ctrlW) XDefineCursor(theDisp, ctrlW, arrow);
    if (dirW)  XDefineCursor(theDisp, dirW, arrow);
    if (gamW)  XDefineCursor(theDisp, gamW, arrow);
    if (psW)   XDefineCursor(theDisp, psW,  arrow);
#ifdef HAVE_JPEG
    if (jpegW) XDefineCursor(theDisp, jpegW,  arrow);
#endif
    fishno = 0;
  }

  else if (flcurs) {    /* was able to load the cursors */
    switch (n%8) {
    case 0: c = flcurs;   break;
    case 1: c = fl1curs;  break;
    case 2: c = fmcurs;   break;
    case 3: c = fr1curs;  break;
    case 4: c = frcurs;   break;
    case 5: c = fr1curs;  break;
    case 6: c = fmcurs;   break;
    case 7: c = fl1curs;  break;
    }

    if (!useroot && mainW) XDefineCursor(theDisp, mainW, c);
    if (infoW) XDefineCursor(theDisp, infoW, c);
    if (ctrlW) XDefineCursor(theDisp, ctrlW, c);
    if (dirW)  XDefineCursor(theDisp, dirW, c);
    if (gamW)  XDefineCursor(theDisp, gamW, c);
    if (psW)   XDefineCursor(theDisp, psW, c);
#ifdef HAVE_JPEG
    if (jpegW) XDefineCursor(theDisp, jpegW, c);
#endif
  }

  XFlush(theDisp);
}
  
    


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

static int timerdone;

/*******/
static void onalarm()
/*******/
{
  timerdone=1;
}


/*******/
void Timer(n)   /* waits for 'n' milliseconds */
 int  n;
/*******/
{
  long usec;
  struct itimerval it;

  if (!n) return;
  usec = (long) n * 1000;

#ifdef sco
  nap(usec);  return;
#endif

#ifdef USLEEP
  usleep(usec);  return;
#endif

#ifdef NOTIMER
  return;
#endif

  memset(&it, 0, sizeof(it));
  if (usec>=1000000L) {  /* more than 1 second */
    it.it_value.tv_sec = usec / 1000000L;
    usec %= 1000000L;
  }

  it.it_value.tv_usec = usec;
  timerdone=0;
  signal(SIGALRM,onalarm);
  setitimer(ITIMER_REAL, &it, (struct itimerval *)0);
  while (1) {
    HOLD_SIG;                    /* note:  have to block, so that ALRM */
    if (timerdone) break;        /* doesn't occur between 'if (timerdone)' */
    else PAUSE_SIG;              /* and calling PAUSE_SIG */
  }

  RELEASE_SIG;                   /* turn ALRM blocking off */
  signal(SIGALRM,SIG_DFL);
}


