/***********************************************************************
 *                                                                     *
 * ImgToolKit.c - set of routines for work with X11 server             *
 *                                                                     *
 * Author: Evgeni Chernyaev (chernaev@mx.ihep.su)                      *
 *                                                                     *
 * Contains:                                                           *
 *   int     ImgCheckDisplay (dsp)                                     *
 *   int     ImgPause (dsp, pause)                                     *
 *   void    ImgFreeImage (image)                                      *
 *   XImage* ImgPickImage (dsp, x, y, w, h)                            *
 *   void    ImgPickPalette (dsp, window, ncol, r, g, b)               *
 *   void    ImgGetRectangle (dsp, window, x, y, w, h)                 *
 *   void    ImgGetCurrentWindow (dsp, window, x, y, w, h)             *
 *   int     ImgGetWindowById (dsp, window, x, y, w, h)                *
 *   void    ImgFinalFlash (dsp, x, y, w, h)                           * 
 *                                                                     *
 *                                                                     *
 * Copyright (C) 1993, 1994 by Evgeni Chernyaev.                       *
 *                                                                     *
 * Permission to use, copy, modify, and distribute this software and   *
 * its documentation for non-commercial purpose is hereby granted      *
 * without fee, provided that the above copyright notice appear in all *
 * copies and that both the copyright notice and this permission       *
 * notice appear in supporting documentation.                          *
 *                                                                     *
 * This software is provided "as is" without express or implied        *
 * warranty.                                                           *
 *                                                                     *
 ***********************************************************************/
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include <stdio.h>
#include <time.h>

#define MAXCOL  256

/***********************************************************************
 *                                                                     *
 * Name: ImgCheckDisplay                             Date:    22.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Check X-display parameters                                *
 *                                                                     *
 * Input: theDsp - pointer to display structure                        *
 *                                                                     *
 * Return: 0 - if O.K.                                                 *
 *        -1 - too many colors                                         *
 *        -2 - unable to get RootWindow attributes                     *
 *        -3 - DirectColor                                             *
 *        -4 - TrueColor                                               *
 *                                                                     *
 ***********************************************************************/
int ImgCheckDisplay(theDsp)
           Display *theDsp;
{
  int               theScr;
  Window            theRoot;
  XWindowAttributes theRWA;
  Visual           *theVis;

  theScr  = DefaultScreen(theDsp);
  theRoot = RootWindow(theDsp, theScr);

  if (DisplayCells(theDsp, theScr) > MAXCOL)           return -1;
  if (!XGetWindowAttributes(theDsp, theRoot, &theRWA)) return -2;
  theVis  = theRWA.visual;
  if (theVis == NULL) theVis = DefaultVisual(theDsp, theScr);
  if (theVis->class == DirectColor)                    return -3;
  if (theVis->class == TrueColor)                      return -4;
  return 0;
}
/***********************************************************************
 *                                                                     *
 * Name: ImgPause                                    Date:    22.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Wait a few seconds for input from keyboard                *
 *                                                                     *
 * Input: theDsp - pointer to display structure                        *
 *        pause  - time delay in seconds                               *
 *                                                                     *
 * Return: 0 - O.K. there was input from keyboard                      *
 *        -1 - time out                                                *
 *                                                                     *
 ***********************************************************************/
int ImgPause(theDsp, pause)
    Display *theDsp;
        int  pause;
{
  Window     theRoot;
  time_t     t1, t2;
  XEvent     event;

  theRoot = RootWindow(theDsp, DefaultScreen(theDsp));
  XGrabKeyboard(theDsp, theRoot,
                False, GrabModeAsync, GrabModeAsync, CurrentTime);
  time(&t1); t2 = t1;
  while(1) {
    if ((t2 - t1) > pause) {
      XUngrabKeyboard(theDsp, CurrentTime);
      return -1;
    }
    if (0 != XCheckTypedEvent(theDsp, KeyPress, &event)) {
      XUngrabKeyboard(theDsp, CurrentTime);
      return 0; 
    }
    time(&t2);
  }
}
/***********************************************************************
 *                                                                     *
 * Name: ImgFreeImage                                Date:    07.01.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Free image                                                *
 *                                                                     *
 * Input: theImg - pointer to the image                                *
 *                                                                     *
 ***********************************************************************/
void ImgFreeImage(theImg)
          XImage *theImg;
{
  if (theImg) {
    if (theImg->data) free(theImg->data);
    theImg->data = NULL;
    XDestroyImage(theImg);
  }
}
/***********************************************************************
 *                                                                     *
 * Name: ImgPickImage                                Date:    22.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Pick image                                                *
 *                                                                     *
 * Input: theDsp - pointer to display structure                        *
 *        x      - x-coordinate of the upper left corner               *
 *        y      - y-coordinate of the upper left corner               * 
 *        width  - image width                                         *
 *        height - image height                                        *
 *                                                                     *
 * Return: pointer to the image                                        *
 *                                                                     *
 ***********************************************************************/
XImage* ImgPickImage(theDsp, x, y, width, height)
            Display *theDsp;
                int  x, y, width, height;
{
  Window  theRoot;
  XImage *theImg;
  
  theRoot = RootWindow(theDsp, DefaultScreen(theDsp));
  theImg = XGetImage(theDsp, theRoot, x, y, width, height, AllPlanes, ZPixmap);
  if (!theImg || !theImg->data) {
    ImgFreeImage(theImg); theImg = NULL;
  }
  return theImg;
}

/***********************************************************************
 *                                                                     *
 * Name: ImgPickPalette                              Date:    22.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Pick palette for image                                    *
 *                                                                     *
 * Input: theDsp - pointer to display structure                        *
 *        window - window                                              *
 *                                                                     *
 * Output: Ncol  - number of colors                                    *
 *         R[]   - red components                                      *
 *         G[]   - green components                                    *
 *         B[]   - blue components                                     *
 *                                                                     *
 ***********************************************************************/
void ImgPickPalette(theDsp, window, Ncol, R, G, B)
           Display *theDsp;
           Window   window;
           int     *Ncol, *R, *G, *B;
{
  int               i, theScr, nmaps, ncolors;
  Window            theRoot, theWin;
  Colormap          cmap, *cmaps;
  XColor            colors[MAXCOL];
  XWindowAttributes theAtt;
  
  theScr  = DefaultScreen(theDsp);
  theRoot = RootWindow(theDsp, theScr);

  if (window != NULL && window != theRoot)
    theWin  = window;
  else
    theWin  = theRoot;

  XGetWindowAttributes(theDsp, theWin, &theAtt);

  if (theAtt.colormap && theAtt.map_installed) {
    ncolors = theAtt.visual->map_entries;
    cmap    = theAtt.colormap;
  }else{
    ncolors = DisplayCells(theDsp, theScr);
    cmap    = DefaultColormap(theDsp, theScr);
    cmaps   = XListInstalledColormaps(theDsp, theRoot, &nmaps);
    if (nmaps != 0) {
      for (i=0; i<nmaps; i++) {
        if (cmap == cmaps[i]) break;
        if (i == nmaps-1) cmap = cmaps[0];
      }
    }
    XFree((void *) cmaps);
  }

  for (i=0; i<ncolors; i++) {
    colors[i].pixel = i;
    colors[i].pad   = 0;
  }
  XQueryColors(theDsp, cmap, colors, ncolors);

  *Ncol   = ncolors;
  for (i=0; i<ncolors; i++) {
    R[i] = colors[i].red;
    G[i] = colors[i].green;
    B[i] = colors[i].blue;
  }
}

static Display          *theDsp;
static int               theScr;
static Window            theRoot;
static GC                theGC;
static XWindowAttributes theRWA;

/***********************************************************************
 *                                                                     *
 * Name: ImgFindRectangle                            Date:    14.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Find surounding rectangle for window                      *
 *                                                                     *
 * Input: win    - window ID                                           *
 *        px, py - point inside the window                             *
 *                                                                     *
 * Output: x, y - left upper corner                                    *
 *         w, h - width and height of rectangle                        *
 *                                                                     *
 ***********************************************************************/
static void ImgFindRectangle(win, px, py, x, y, w, h)
                     Window  win;   
                     int     px, py, *x, *y, *w, *h;
{
  Window            curW, kidW;
  Atom              state, type;
  int               format, xw, yw, ww, hw;
  unsigned char    *data;
  unsigned long     after, number_items;
  XWindowAttributes atrW;

  if (win == theRoot && win == NULL) {
    *x = 0; *y = 0; *w = theRWA.width; *h = theRWA.height;
    return;
  }
  curW  = win;
  state = XInternAtom(theDsp, "WM_STATE", True);
  if (state != NULL) {
    type = NULL;
    kidW = win;
    while (kidW != NULL) {
      curW = kidW;
      XGetWindowProperty(theDsp, curW, state, 0L, 0L, False, AnyPropertyType,
                         &type, &format, &number_items, &after, &data);
/*    if (data != NULL) XFree ((void *) data); */
      if (type != NULL) break;
      XTranslateCoordinates(theDsp, theRoot, curW, px, py, &xw, &yw, &kidW);
      curW = win;
    }
  }
  XGetWindowAttributes(theDsp, curW, &atrW);
  ww = atrW.width;
  hw = atrW.height;
  XTranslateCoordinates(theDsp, curW, theRoot, 0, 0, &xw, &yw, &kidW);
  if (xw < 0) { ww += xw; xw = 0; }
  if (yw < 0) { hw += yw; yw = 0; }
  if ((xw+ww) > theRWA.width)  ww = theRWA.width - xw;
  if ((yw+hw) > theRWA.height) hw = theRWA.height - yw;
  *x = xw; *y = yw; *w = ww; *h = hw;
}
/***********************************************************************
 *                                                                     *
 * Name: ImgFlashRectangle                           Date:    14.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Draw flashing rectangle                                   *
 *                                                                     *
 * Input: x, y - left upper corner                                     *
 *        w, h - width and height of rectangle                         *
 *        show - show flag                                             *
 *                                                                     *
 ***********************************************************************/
static void ImgFlashRectangle(x, y, w, h, show)
                          int x, y, w, h, show;
{
  static int isvis = 0, mask1 = 0, mask2 = 1, xcur, ycur, wcur, hcur;
  static unsigned long masks[8] = {
                                   0x01010101,0x02020203,0x04040405,0x08080809,
                                   0x10101011,0x20202021,0x40404041,0x80808081
                                  };

  /*   T U R N   O F F   R E C T A N G L E   */

  if (show == 0) {
    if (isvis == 0) return;
    XSetPlaneMask(theDsp, theGC, masks[mask1]);
    XDrawRectangle(theDsp, theRoot, theGC, xcur, ycur, wcur-1, hcur-1);
    if (wcur>3 && hcur>3)
      XDrawRectangle(theDsp, theRoot, theGC, xcur+1, ycur+1, wcur-3, hcur-3);
    isvis = 0;
    return;
  }

  /*   T U R N   O N   R E C T A N G L E   */

  if (isvis == 0) {
    if (w>1 && h>1) {
      XSetPlaneMask(theDsp, theGC, masks[mask2]);
      XDrawRectangle(theDsp, theRoot, theGC, x, y, w-1, h-1);
      isvis = 1;
    }
    if (w>3 && h>3) 
      XDrawRectangle(theDsp, theRoot, theGC, x+1, y+1, w-3, h-3);
  }else{
    isvis = 0;
    XSetPlaneMask(theDsp, theGC, masks[mask1]);
    XDrawRectangle(theDsp, theRoot, theGC, xcur, ycur, wcur-1, hcur-1);
    if (w>1 && h>1) {
      XSetPlaneMask(theDsp, theGC, masks[mask2]);
      XDrawRectangle(theDsp, theRoot, theGC, x, y, w-1, h-1);
      isvis = 1;
    }
    if (wcur>3 && hcur>3) {
      XSetPlaneMask(theDsp, theGC, masks[mask1]);
      XDrawRectangle(theDsp, theRoot, theGC, xcur+1, ycur+1, wcur-3, hcur-3);
    }
    if (w>3 && h>3) {
      XSetPlaneMask(theDsp, theGC, masks[mask2]);
      XDrawRectangle(theDsp, theRoot, theGC, x+1, y+1, w-3, h-3);
    }
  }
  if (isvis == 1) {
    mask1 = mask2;
    mask2 = (mask2 + 1) & 7;
    xcur = x; ycur = y; wcur = w; hcur = h;
  }
}
/***********************************************************************
 *                                                                     *
 * Name: ImgGetRectangle                             Date:    23.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Select rectangle on the screen                            *
 *                                                                     *
 * Input: display - pointer to display structure                       *
 *                                                                     *
 * Output: window  - window (for picking of palette)                   *
 *         xorigin - x-coordinate of the upper left corner             *
 *         yorigin - y-coordinate of the upper left corner             *
 *         width   - rectangle width                                   *
 *         height  - rectangle height                                  *
 *                                                                     *
 ***********************************************************************/
void ImgGetRectangle(display, window, xorigin, yorigin, width, height)
           Display  *display;
           Window   *window;
           int      *xorigin, *yorigin, *width, *height;
{
  int          x, y, rx, ry, x1, y1, x2, y2, ix, iy, iw, ih;
  Cursor       theCrsr;
  Window       rW, cW;
  unsigned int mask;
  XColor       fc, bc;
  XEvent       event;

  /*   I N I T I A L I S A T I O N   */

  theDsp  = display;
  theScr  = DefaultScreen(theDsp);
  theRoot = RootWindow(theDsp, theScr);
  theGC   = DefaultGC(theDsp, theScr);
  XGetWindowAttributes(theDsp, theRoot, &theRWA);

  /*   S E T   C U R S O R   */

  theCrsr = XCreateFontCursor(theDsp, XC_tcross);
  fc.red  = fc.green = fc.blue = 0xffff;
  bc.red  = bc.green = bc.blue = 0x0000;
  XRecolorCursor(theDsp, theCrsr, &fc, &bc);

  /*   G R A B   P O I N T E R  */

  XGrabPointer(theDsp, theRoot, False, 0L,
               GrabModeAsync, GrabModeSync, None, theCrsr, CurrentTime);

  XSetFunction(theDsp, theGC, GXinvert);  /* start flash rectangle */
  XSetSubwindowMode(theDsp, theGC, IncludeInferiors);

  /*   G E T    R E C T A N G L E   */

  x = -1; y = -1;
  while (1) {                              /* Wait for button press */
    if (XQueryPointer(theDsp, theRoot, &rW, &cW, &rx, &ry, &x1, &y1, &mask)) {
      if (mask & Button1Mask) break;
    }
    if (x1 != x || y1 != y) {
      x = x1; y = y1;
      if (cW == NULL || cW == theRoot) {
        ix = 0; iy = 0; iw = theRWA.width; ih = theRWA.height;
      }else{
        ImgFindRectangle(cW,x1,y1,&ix,&iy,&iw,&ih);
      } 
    }
    ImgFlashRectangle(ix, iy, iw, ih, 1);  /* turn ON rectangle */
  }

  *xorigin = ix; *yorigin = iy; *width = iw; *height = ih;

  x2 = x1;  y2 = y1; iw = 0; ih = 0;
  while (1) {                              /* Wait for button release */
    if (XQueryPointer(theDsp, theRoot, &rW, &cW, &rx, &ry, &x, &y, &mask)) {
      if (0 != XCheckTypedEvent(theDsp, KeyPress,&event)) break;
      if (!(mask & Button1Mask)) break;
    }
    if (x != x2 || y != y2) {              /* move rectangle */
      ix = (x1<x) ? x1 : x; iy = (y1<y) ? y1 : y;
      iw = abs(x - x1) + 1; ih = abs(y - y1) + 1;
      x2 = x;  y2 = y;
    }
    ImgFlashRectangle(ix, iy, iw, ih, 1);  /* turn ON rectangle */
  }
  
  ImgFlashRectangle(0, 0, 0, 0, 0);        /* turn OFF rectangle */
  XSetFunction(theDsp, theGC, GXcopy);     /* end flash rectangle */
  XSetSubwindowMode(theDsp, theGC, ClipByChildren);
  XSetPlaneMask(theDsp, theGC, AllPlanes);

  /*   U N G R A B E   P O I N T E R   */

  XUngrabPointer(theDsp, CurrentTime);
  XFlush(theDsp); 

  /*   F I N D   W I N D O W   F O R   P I C K   P A L E T T E  */

  if (iw > 2 || ih > 2) {
    *xorigin = ix; *yorigin = iy; *width = iw; *height = ih;
    x        = ix + iw/2;
    y        = iy + ih/2;
  }
  rW       = theRoot;
  while (rW != NULL) {
    cW = rW;
    XTranslateCoordinates(theDsp, theRoot, cW, x, y, &rx, &ry, &rW);
  }
  *window = cW;
}
/***********************************************************************
 *                                                                     *
 * Name: ImgGetCurrentWindow                         Date:    23.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Get coordinates of the current window                     *
 *                                                                     *
 * Input: display - pointer to display structure                       *
 *                                                                     *
 * Output: window  - current window                                    *
 *         xorigin - x-coordinate of the upper left corner             *
 *         yorigin - y-coordinate of the upper left corner             *
 *         width   - rectangle width                                   *
 *         height  - rectangle height                                  *
 *                                                                     *
 ***********************************************************************/
void ImgGetCurrentWindow(display, window, xorigin, yorigin, width, height)
               Display  *display;
               Window   *window;
               int      *xorigin, *yorigin, *width, *height;
{
  int          x, y, rx, ry;
  Window       rW, cW;
  unsigned int mask;

  theDsp  = display;
  theScr  = DefaultScreen(theDsp);
  theRoot = RootWindow(theDsp, theScr);
  theGC   = DefaultGC(theDsp, theScr);
  XGetWindowAttributes(theDsp, theRoot, &theRWA);

  XQueryPointer(theDsp, theRoot, &rW, &cW, &rx, &ry, &x, &y, &mask);
  if (cW == NULL) cW = theRoot;
  ImgFindRectangle(cW,x,y,xorigin,yorigin,width,height);

  /*   F I N D   W I N D O W   F O R   P I C K   P A L E T T E  */

  rW       = theRoot;
  while (rW != NULL) {
    cW = rW;
    XTranslateCoordinates(theDsp, theRoot, cW, x, y, &rx, &ry, &rW);
  }
  *window = cW;
}
/***********************************************************************
 *                                                                     *
 * Name: ImgGetWindowById                            Date:    01.04.94 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised:          *
 *                                                                     *
 * Function: Get coordinates of window by id                           *
 *                                                                     *
 * Input: display - pointer to display structure                       *
 *        window  - window id                                          *
 *                                                                     *
 * Output: xorigin - x-coordinate of the upper left corner             *
 *         yorigin - y-coordinate of the upper left corner             *
 *         width   - window width                                      *
 *         height  - window height                                     *
 *                                                                     *
 * Return: 0 - O.K.                                                    *
 *        -1 - window does not exist                                   *
 *                                                                     *
 ***********************************************************************/
int ImgGetWindowById(display, window, xorigin, yorigin, width, height)
        Display  *display;
        Window    window;
        int      *xorigin, *yorigin, *width, *height;
{
  int          x, y, w, h;
  unsigned int ww, hh, border, depth;
  Window       rW;

  theDsp  = display;
  theScr  = DefaultScreen(theDsp);
  theRoot = RootWindow(theDsp, theScr);
  theGC   = DefaultGC(theDsp, theScr);
  XGetWindowAttributes(theDsp, theRoot, &theRWA);

  if (window == theRoot) {
    *xorigin = 0; *yorigin = 0;
    *width = theRWA.width; *height = theRWA.height;
    return 0;
  }

  if (!XGetGeometry(theDsp, window, &rW, &x, &y, &ww, &hh, &border, &depth))
    return (-1);

  w = ww; h = hh;
  XTranslateCoordinates(theDsp, window, theRoot, 0, 0, &x, &y, &rW);
  if (x < 0) { w += x; x = 0; }
  if (y < 0) { h += y; y = 0; }
  if ((x+w) > theRWA.width)  w = theRWA.width - x;
  if ((y+h) > theRWA.height) h = theRWA.height - y;
  *xorigin = x; *yorigin = y; *width = w; *height = h;
  return 0;
}
/***********************************************************************
 *                                                                     *
 * Name: ImgFinalFlash                               Date:    08.11.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised: 01.04.94 *
 *                                                                     *
 * Function: Final flash of the selected rectangle                     *
 *                                                                     *
 * Input: display - pointer to display structure                       *
 *        x, y    - left upper corner                                  *
 *        w, h    - width and height of the rectangle                  *
 *                                                                     *
 ***********************************************************************/
void ImgFinalFlash(display, x, y, w, h)
          Display *display;
              int  x, y, w, h;
{
  time_t       t1, t2;

  theDsp  = display;
  theScr  = DefaultScreen(theDsp);
  theRoot = RootWindow(theDsp, theScr);
  theGC   = DefaultGC(theDsp, theScr);
  XGetWindowAttributes(theDsp, theRoot, &theRWA);

  XFlush(theDsp);

  theRoot = RootWindow(theDsp, DefaultScreen(theDsp));
  XGrabKeyboard(theDsp, theRoot,
                False, GrabModeAsync, GrabModeSync, CurrentTime);
  XSetFunction(theDsp, theGC, GXinvert);  /* start flash rectangle */
  XSetSubwindowMode(theDsp, theGC, IncludeInferiors);

  time(&t1); t2 = t1;
  while(t2-t1 == 0) {
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    ImgFlashRectangle(x, y, w, h, 1);
    time(&t2);
  }
  ImgFlashRectangle(0, 0, 0, 0, 0);       /* turn OFF rectangle */
  XSetFunction(theDsp, theGC, GXcopy);    /* end flash rectangle */
  XSetSubwindowMode(theDsp, theGC, ClipByChildren);
  XSetPlaneMask(theDsp, theGC, AllPlanes);
  XUngrabKeyboard(theDsp, CurrentTime);
  XFlush(theDsp);
}
