/***********************************************************************
 *                                                                     *
 * xwpick.c - main routine for xwpick                                  *
 *                                                                     *
 * Author: Evgeni Chernyaev (chernaev@mx.ihep.su)                      *
 *                                                                     *
 * 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 <stdio.h>
#include <stdlib.h>
#include <string.h>

char ProgName[] = "xwpick";
char ProgVers[] = "Version 2.10  Rev: 15/08/94 - by Evgeni Chernyaev";
char FileName[80];

#ifdef __STDC__
#define ARGS(alist) alist
#else 
#define ARGS(alist) ()
#endif

#ifdef __VMS
#define VMS_STYLE   1
#else 
#ifdef VMS
#define VMS_STYLE   1
#else 
#define VMS_STYLE   0
#endif
#endif

#define MAX_ARG    10
#define TIMEOUT    30
#define MIN_WIDTH   8
#define MIN_HEIGHT  8

typedef unsigned char byte;
typedef void (*vfp)();

static Display *dsp;
static XImage  *image;
static FILE    *out;

   /*    G L O B A L   P R E D E F I N I T I O N S   */

int     ImgCheckDisplay     ARGS((Display *));
void    ImgGetRectangle     ARGS((Display *,Window *,int *,int *,int *,int *));
void    ImgGetCurrentWindow ARGS((Display *,Window *,int *,int *,int *,int *));
int     ImgGetWindowById    ARGS((Display *,Window,int *,int *,int *,int *));
int     ImgPause            ARGS((Display *,int)); 
XImage *ImgPickImage        ARGS((Display *,int,int,int,int));
void    ImgPickPalette      ARGS((Display *,Window,int *,int *,int *,int *));
void    ImgFinalFlash       ARGS((Display *,int,int,int,int));
void    ImgFreeImage        ARGS((XImage *));

long PS_LZWencode ARGS((int,int,int,int,byte *,byte *,byte *,byte *,vfp,vfp));
long PSencode     ARGS((int,int,int,byte *,byte *,byte *,byte *,vfp,vfp));
long GIFencode    ARGS((int,int,int,byte *,byte *,byte *,byte *,vfp,vfp));
long PCXencode    ARGS((int,int,int,byte *,byte *,byte *,byte *,vfp,vfp));
long PPMencode    ARGS((int,int,int,byte *,byte *,byte *,byte *,vfp,vfp));
long PICTencode   ARGS((int,int,int,byte *,byte *,byte *,byte *,vfp,vfp));

   /*   L O C A L   F U N C T I O N S   */

static void get_scline(y, width, scline)
                   int y, width;
                  byte scline[];
{
  int i;
  for (i=0; i<width; i++) scline[i] = XGetPixel(image, i, y);
}

static void put_byte(b)
                byte b;
{
  if (ferror(out) == 0) fputc(b, out);
}   

static void usage()
{
  fprintf(stderr,"%s: usage: %s [options...] [<file>]\n",ProgName,ProgName); 
  fprintf(stderr,"%s: possible options:\n",ProgName);
  fprintf(stderr,"  -local       select window under the mouse pointer\n");
  fprintf(stderr,"  -window id   select window by id\n");
  fprintf(stderr,"  -reverse     transfer image to reverse colors\n");
  fprintf(stderr,"  -gray        transfer image to grayscale\n");
  fprintf(stderr,
    "%s: possible file names: *.ps,*.eps,*.epsi,*.gif,*.pcx,*.pict,*.ppm\n",
    ProgName);
  exit(1);
}  

static void quit(message)
           char *message;
{
  fprintf(stderr,"%s: %s\n",ProgName,message);
  XBell(dsp, 0);
  XBell(dsp, 0);
  XBell(dsp, 0);
  XCloseDisplay(dsp);
  fclose(out);
  exit(1);
}  

static int vms_parser(argc,argv,strng)
                  int argc;
                 char *argv[], strng[];
{
  int i, j, k, n;
  char c;
  
  if (argc == 1) return 1;

  for (strng[0]='\0', k=i=1; i<argc; i++) {
    j = 0;
    do {
      c = argv[i][j++];
      switch (c) {
      case '/':
	if (strng[k-1] != '\0') strng[k++] = '\0';
	strng[k++] = '-';
	break;
      case '=': case '\0':
	if (strng[k-1] != '\0') strng[k++] = '\0';
        break;
      default:
	strng[k++] = c;
	break;
      }
    } while (c != '\0');
  }

  for (n=i=0; i<k; i++) {
    if (strng[i] == '\0') argv[++n] = &strng[i+1];
  }
  return n;
}

/***********************************************************************
 *                                                                     *
 * Name: xwpick                                      Date:    16.02.93 *
 * Author: E.Chernyaev (IHEP/Protvino)               Revised: 01.04.94 *
 *                                                            01.08.94 *
 * Function: Pick X Window image from screen                           *
 *                                                                     *
 * FileName: '*.ps,*.eps,*epsi'   - PostScript                         *
 *           '*.gif'  - GIF-format  (Graphics Interchange Format)      *
 *           '*.pcx'  - PCX-format  (IBM PC)                           *
 *           '*.pict' - PICT-format (Macintosh)                        *
 *           '*.ppm'  - PPM-format  (see PBM Plus library)             *
 *                                                                     *
 ***********************************************************************/
main(ArgC, ArgV)
 int ArgC;
 char *ArgV[];
{
  int           iflocal, ifinver, ifgray, ifwin, iffile, ifcolor;
  int           i, ifrm, maxcol, argc;
  int           x, y, width, height, ncol, R[256], G[256], B[256];
  long          igray, irep;
  byte          r[256], g[256], b[256], scline[4096];
  char         *format, mess[80], *argv[MAX_ARG];
  Window        win;

  /*   G E T   P A R A M E T E R S   */

  iflocal = 0;
  ifinver = 0;
  ifgray  = 0;
  ifwin   = 0;
  iffile  = 0;

  argc = ArgC;
  for (i=0; i<argc; i++) argv[i] = ArgV[i];
  if (VMS_STYLE) argc = vms_parser(argc,argv,mess);
    
  for (i=1; i<argc; i++) {
    if (argv[i][0] != '-') {
      strcpy(FileName,argv[i]);
      iffile  = 1;
    }
    else if (strncmp(argv[i],"-local",  2)==0 || strncmp(argv[i],"-L",2)==0) {
      iflocal = 1;
    }
    else if (strncmp(argv[i],"-reverse",2)==0 || strncmp(argv[i],"-R",2)==0) {
      ifinver = 1;
    }
    else if (strncmp(argv[i],"-gray",   2)==0 || strncmp(argv[i],"-G",2)==0) {
      ifgray  = 1;
    }
    else if (strncmp(argv[i],"-window", 2)==0 || strncmp(argv[i],"-W",2)==0) {
      if (++i < argc) {
	if (strcmp(argv[i],"root") == 0 || strcmp(argv[i],"ROOT") == 0) {
	 ifwin = 2;
        }else{
         win   = (Window) strtol(argv[i],(char **) NULL,0);
	 if (win != (Window) 0) ifwin = 1;
        }
      }
      if (ifwin == 0) {
	fprintf(stderr,"%s: missing id or 'root' on -window\n",ProgName);
        exit(1);
      }
    }
    else usage();
  }

  if (iffile == 0) {
    printf("**************************************************************\n");
    printf("*                                                            *\n");
    printf("*               XWPICK 2.10 - Pick X Window Image            *\n");
    printf("*  Possible file names: *.ps   (PostScript)                  *\n");
    printf("*                       *.eps  (Encapsulated PS)             *\n");
    printf("*                       *.epsi (EPS with preview)            *\n");
    printf("*                       *.gif  (Graphics Interchange Format) *\n");
    printf("*                       *.pcx  (IBM PC)                      *\n");
    printf("*                       *.pict (Macintosh)                   *\n");
    printf("*                       *.ppm  (PBM+ library)                *\n");
    printf("*                                                            *\n");
    printf("**************************************************************\n");
    printf("\nFile name: ");
    scanf("%s",FileName);
  }

  /*   C H E C K   F I L E   N A M E   */
  
  format = strrchr(FileName,'.'); 
  ifrm   = 0;
  if (format) {
    format++;
    if (strncmp(format,"ps",  2)== 0 || strncmp(format,"PS",  2)== 0) ifrm = 1;
    if (strncmp(format,"eps", 3)== 0 || strncmp(format,"EPS", 3)== 0) ifrm = 2;
    if (strncmp(format,"epsi",4)== 0 || strncmp(format,"EPSI",4)== 0) ifrm = 3;
    if (strncmp(format,"gif", 3)== 0 || strncmp(format,"GIF", 3)== 0) ifrm = 4;
    if (strncmp(format,"pcx", 3)== 0 || strncmp(format,"PCX", 3)== 0) ifrm = 5;
    if (strncmp(format,"pict",3)== 0 || strncmp(format,"PICT",3)== 0) ifrm = 6;
    if (strncmp(format,"ppm", 3)== 0 || strncmp(format,"PPM", 3)== 0) ifrm = 7;
    if (strncmp(format,"psrl",3)== 0 || strncmp(format,"PSRL",3)== 0) ifrm = 8;
  }

  if (ifrm == 0) usage();

  /*  O P E N   F I L E  */

  out = fopen(FileName,"w+");
  if (!out) {
    fprintf(stderr,"%s: unable to open file: %s\n",ProgName,FileName);
    exit(1);
  }

  /*   O P E N   D I S P L A Y   */

  dsp = XOpenDisplay("");
  if (dsp == NULL) {
    fprintf(stderr,"%s: unable to open display '%s'\n",
                   ProgName,XDisplayName(""));
    fclose(out);
    exit(1);
  }
  i = ImgCheckDisplay(dsp);
  if (i == -1) quit("too many colors");
  if (i == -2) quit("unable to get RootWindow attributes");
  if (i == -3) quit("can not work with DirectColor visual");
  if (i == -4) quit("can not work with TrueColor visual");

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

  if (ifwin != 0) {
    if (ifwin == 2)
      win = RootWindow(dsp, DefaultScreen(dsp));
    if (ImgGetWindowById(dsp, win, &x, &y, &width, &height) != 0)
      quit("no window with specified id exists");
    if (width <= MIN_WIDTH || height <= MIN_HEIGHT) {
      sprintf(mess,"no image picked: too narrow rectangle %dx%d",width,height);
      quit(mess);
    }
  }
  else if (iflocal != 0) {
    XBell(dsp, 0);
    fprintf(stderr,"press SPACE to pick image ... "); 
    if (ImgPause(dsp, TIMEOUT) != 0) {
      fprintf(stderr,"time out\n");
      quit("no image picked");
    }
    ImgGetCurrentWindow(dsp, &win, &x, &y, &width, &height);
    if (width <= MIN_WIDTH || height <= MIN_HEIGHT) {
      sprintf(mess,"no image picked: too narrow rectangle %dx%d",width,height);
      quit(mess);
    }
  }
  else {
    XBell(dsp, 0);
    ImgGetRectangle(dsp, &win, &x, &y, &width, &height);
    if (width <= MIN_WIDTH || height <= MIN_HEIGHT) {
      sprintf(mess,"no image picked: too narrow rectangle %dx%d",width,height);
      quit(mess);
    }
    XBell(dsp, 0);
    fprintf(stderr,"press SPACE to pick image ... ");
    if (ImgPause(dsp, TIMEOUT) != 0) {
      fprintf(stderr,"time out\n"); 
      quit("no image picked"); 
    }
  }

  /*   P I C K   I M A G E   */

  XBell(dsp, 0);
  XBell(dsp, 0);
  if (ifwin == 0) fprintf(stderr,"image picking\n"); 
  image = ImgPickImage(dsp, x, y, width, height);
  if (!image) quit("unable to pick image"); 

  /*   P I C K   P A L E T T E   */

  ImgPickPalette(dsp, win, &ncol, R, G, B);

  /*   N O R M A L I S E   C O L O R S   */

  maxcol = 0;
  for (i=0; i<ncol; i++) {
    if (maxcol < R[i]) maxcol = R[i];
    if (maxcol < G[i]) maxcol = G[i];
    if (maxcol < B[i]) maxcol = B[i];
    r[i] = 0; 
    g[i] = 0; 
    b[i] = 0; 
  }
  if (maxcol != 0) {
    for (i=0; i<ncol; i++) {
      r[i] = R[i] * 255 / maxcol;
      g[i] = G[i] * 255 / maxcol;
      b[i] = B[i] * 255 / maxcol;
    }
  }

  /*  I N V E R T   C O L O R S ,   I F   N E E D E D  */

  if (ifinver != 0) {
    maxcol = 0;
    for (i=0; i<ncol; i++) {
      r[i] = 255 - r[i];
      g[i] = 255 - g[i];
      b[i] = 255 - b[i];
      if (maxcol < r[i]) maxcol = r[i];
      if (maxcol < g[i]) maxcol = g[i];
      if (maxcol < b[i]) maxcol = b[i];
    }
    if (maxcol != 0) {
      for (i=0; i<ncol; i++) {
        r[i] = r[i] * 255 / maxcol;
        g[i] = g[i] * 255 / maxcol;
        b[i] = b[i] * 255 / maxcol;
      }
    }
  }

  /*   G R A Y S C A L E   ?   */

  if (ifgray == 0) {
    ifcolor = 0;
    for (i=0; i<ncol; i++) {
      if (r[i] != g[i] || r[i] != b[i]) ifcolor = 1;
    }
    ifgray = 1 - ifcolor;
  }
  
  /*  E N C O D E   I M A G E   */

  if (ifrm < 4) {
    i    = ifgray*10 + ifrm - 1; 
    irep = PS_LZWencode(i,width,height,ncol,r,g,b,scline,get_scline,put_byte);
  }
  if (ifgray != 0) {
    for (i=0; i<ncol; i++) {
      igray = (r[i]*299L + g[i]*587L + b[i]*114L) / 1000L;      
      r[i]  = igray;
      g[i]  = igray;
      b[i]  = igray;
    }
  }
  if (ifrm == 4)
    irep = GIFencode(width,height,ncol,r,g,b,scline,get_scline,put_byte);
  if (ifrm == 5)
    irep = PCXencode(width,height,ncol,r,g,b,scline,get_scline,put_byte);
  if (ifrm == 6)
    irep = PICTencode(width,height,ncol,r,g,b,scline,get_scline,put_byte);
  if (ifrm == 7)
    irep = PPMencode(width,height,ncol,r,g,b,scline,get_scline,put_byte);
  if (ifrm == 8)
    irep = PSencode(width,height,ncol,r,g,b,scline,get_scline,put_byte);

  ImgFinalFlash(dsp,x,y,width,height);
  XBell(dsp, 0);
  XBell(dsp, 0);
  XBell(dsp, 0);
  ImgFreeImage(image);
  XCloseDisplay(dsp);

  if (ferror(out) == 0) {
    fprintf(stderr,"%s %d colors %dx%d size %d bytes\n",
                    FileName,ncol,width,height,irep); 
    fclose(out);
    exit(0);
  }else{  
    fprintf(stderr,"%s: problems during file writing\n",ProgName); 
    fclose(out);
    exit(1);
  }
}
