/* xstuff.c, the more general X modules, used by ANA and some other packages */
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <X11/cursorfont.h>
#include <Xm/MwmUtil.h>
#include <Xm/Xm.h>
#include <stdio.h>
#include <math.h>
#include <X11/Xmu/WinUtil.h>
//typedef unsigned long Pixel;
#include "X11/XWDFile.h"
/* Include routines to do parsing */
#include "list.h"
#include "wsutils.h"
#include "multiVis.h"
extern	int rmask, gmask, bmask, rshift, gshift, bshift, screen_num;
extern	Visual  *vis;
extern	Display *display;
extern	Screen  *screen;
extern	int screen_num;
#define ABS(x) ((x)>=0?(x):-(x))
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))
 /*------------------------------------------------------------------*/
#define lowbit(x) ((x) & (~(x) + 1))
 /*------------------------------------------------------------------*/
int ReadColors(vis, cmap, colors)
 Visual *vis;
 Colormap cmap;
 XColor **colors;
 {
    int i,ncolors;

    ncolors = vis->map_entries;
    //printf("ncolors in ReadColors = %d\n", ncolors);
    //printf("vis->class = %d\n", vis->class);
    if (!(*colors = (XColor *) malloc (sizeof(XColor) * ncolors)))
      { printf("Out of memory!\n");  return NULL; }

    if (vis->class == DirectColor ||
	vis->class == TrueColor) {
	Pixel red, green, blue, red1, green1, blue1;

	red = green = blue = 0;
	red1 = lowbit(vis->red_mask);
	green1 = lowbit(vis->green_mask);
	blue1 = lowbit(vis->blue_mask);
	for (i=0; i<ncolors; i++) {
	  (*colors)[i].pixel = red|green|blue;
	  (*colors)[i].pad = 0;
	  red += red1;
	  if (red > vis->red_mask)
	    red = 0;
	  green += green1;
	  if (green > vis->green_mask)
	    green = 0;
	  blue += blue1;
	  if (blue > vis->blue_mask)
	    blue = 0;
	}
    } else {
	for (i=0; i<ncolors; i++) {
	  (*colors)[i].pixel = i;
	  (*colors)[i].pad = 0;
	}
    }

    XQueryColors(display, cmap, *colors, ncolors);
    
    return(ncolors);
 }
 /*------------------------------------------------------------------*/
 /*
 * Routine to let user select a window using the mouse
 */

Window Select_Window(dpy)
     Display *dpy;
 {
  int status;
  Cursor cursor;
  XEvent event;
  Window target_win = None, root = RootWindow(dpy,screen_num);
  int buttons = 0;

  /* Make the target cursor */
  cursor = XCreateFontCursor(dpy, XC_crosshair);

  /* Grab the pointer using target cursor, letting it roam all over */
  status = XGrabPointer(dpy, root, False,
			ButtonPressMask|ButtonReleaseMask, GrabModeSync,
			GrabModeAsync, root, cursor, CurrentTime);
  if (status != GrabSuccess) {
  	printf("Can't grab the mouse.");  return NULL; }

  /* Let the user select a window... */
  while ((target_win == None) || (buttons != 0)) {
    /* allow one more event */
    XAllowEvents(dpy, SyncPointer, CurrentTime);
    XWindowEvent(dpy, root, ButtonPressMask|ButtonReleaseMask, &event);
    switch (event.type) {
    case ButtonPress:
      if (target_win == None) {
	target_win = event.xbutton.subwindow; /* window selected */
	//printf("target_win = %d, %#x\n", target_win, target_win);
	//printf("receiving win = %d, %#x\n", event.xbutton.window, event.xbutton.window);
	//printf("root win = %d, %#x\n", event.xbutton.root, event.xbutton.root);
	if (target_win == None) target_win = root;
      }
      buttons++;
      break;
    case ButtonRelease:
      if (buttons > 0) /* there may have been some down before we started */
	buttons--;
       break;
    }
  } 

  XUngrabPointer(dpy, CurrentTime);      /* Done with pointer */

  return(target_win);
 }
 /*---------------------------------------------------------------------*/
int xerr(display, err)	/* our own non-fatal X window error routine */
 /* essentially copied from manual */
 Display *display;
 XErrorEvent     *err;
 {
 char    msg[80];
 extern	int xerrors;
 XGetErrorText( display, err->error_code, msg, 80);
 printf("X window error: %s\n", msg);
 //printf("out of range %d\n", msg[3000000000]);
 xerrors += 1;
 }
 /*-------------------------------------------------------------------------*/
void draw_root_box_inverted(gc, ix, iy, dx, dy)
 GC gc;
 int	ix, iy, dx, dy;
 {
 XSetForeground(display, gc, WhitePixel(display, screen_num) ^ BlackPixel(display, screen_num));
 XDrawRectangle(display, RootWindow(display,screen_num), gc, ix, iy, dx, dy);
 }
 /*-------------------------------------------------------------------------*/
int get_box_root(box_decr)
 int	*box_decr;
 /* arguments are pointers since we return the results */ 
 {
 static int first_time = True;
 static GC gc;
 static int box_drawn = False;
 static Cursor cursor[4];
 XEvent report;
 XWindowAttributes win_attr;
 Window	qroot, qchild, confine_to, root;
 int pointer_mode, keyboard_mode, win_x, win_y, current_cursor, new_cursor;
 int ix2, iy2, ix1, iy1, kb, xlast1, ylast1, xlast2, ylast2;
 int ix, iy, dx, dy;

 /* straight rectangle but may want to upgrade for rotation some day */
 /* this gets the coordinates of a box on the root window interactively */
 
 /* the code to draw the box on the root screen is modified from the winman
 example, lots of mods actually */

 if (first_time) {
   gc = XCreateGC(display, RootWindow(display,screen_num), 0, NULL);  
   XSetSubwindowMode(display, gc, IncludeInferiors); 
   XSetForeground(display, gc, BlackPixel(display, screen_num));
   XSetFunction(display, gc, GXxor);
   cursor[0] = XCreateFontCursor(display, XC_bottom_right_corner);
   cursor[1] = XCreateFontCursor(display, XC_bottom_left_corner);
   cursor[2] = XCreateFontCursor(display, XC_top_right_corner);
   cursor[3] = XCreateFontCursor(display, XC_top_left_corner);
   first_time = False;
 }
 root = RootWindow(display,screen_num);
 XSelectInput(display, root, None);
 /* we don't want pointer or keyboard events frozen in the server */
 pointer_mode = GrabModeAsync;
 keyboard_mode = GrabModeAsync;

 /* we don't want to confine the cursor */
 confine_to = None;
 XGrabPointer(display, root, True, 
   PointerMotionHintMask | ButtonPressMask | ButtonReleaseMask |
   ButtonMotionMask, pointer_mode, keyboard_mode, confine_to, 
   cursor[0], CurrentTime);

 current_cursor = 0;
 /* don't allow other display operations during move,
    because Xor won't work properly otherwise */
 XGrabServer(display);

 box_drawn = False;	/* use as a flag for conditional response also */
 /* move outline of window until button release */
 while  (1) {
 XNextEvent(display, &report); 
 switch (report.type) {
  case ButtonPress:
   //printf("press\n");
   /* initialize our box at this position */
   xlast1 = ix1 = report.xbutton.x;
   ylast1 = iy1 = report.xbutton.y;
   kb = report.xbutton.state;
   ylast2 = iy2 = iy1+1;	xlast2 = ix2 = ix1+1;
   ix = MIN(ix1, ix2);
   iy = MIN(iy1, iy2);
   dx = ABS(ix1 - ix2);
   dy = ABS(iy1 - iy2);
   draw_root_box_inverted(gc, ix, iy, dx, dy);
   box_drawn = True;
   break;

  case MotionNotify:
   if (box_drawn) {

   /* first undraw the old box */
   ix = MIN(xlast1, xlast2);
   iy = MIN(ylast1, ylast2);
   dx = ABS(xlast1 - xlast2);
   dy = ABS(ylast1 - ylast2);
   draw_root_box_inverted(gc, ix, iy, dx, dy);

   /* can get rid of all MotionNotify events in queue, 
   * since otherwise the round-trip  delays caused by 
   * XQueryPointer may cause a backlog
   * of MotionNotify events, which will cause additional
   * wasted XQueryPointer calls. */
   while (XCheckTypedEvent(display, MotionNotify, 
         &report));

   /* get current mouse position and keystate (in kb) */
   XQueryPointer(display, RootWindow(display,screen_num), 
   &qroot, &qchild, &ix2, &iy2, &win_x, &win_y, &kb);
   /* the root window coords in ix2 and iy2 are what we want */
   //printf("keystate = %d, ix2, iy2 = %d %d\n", kb, ix2, iy2);

   if ( kb & 1) {
   /* shift key is down, move rather than re-size */
   dx = ix2 - xlast2;	dy = iy2 - ylast2;
   ix1 += dx;
   iy1 += dy;
   }

   /* draw the new box */
   ix = MIN(ix1, ix2);
   iy = MIN(iy1, iy2);
   dx = ABS(ix1 - ix2);
   dy = ABS(iy1 - iy2);
   draw_root_box_inverted(gc, ix, iy, dx, dy);
   xlast2 = ix2;	ylast2 = iy2;	/* save the current points */
   xlast1 = ix1;	ylast1 = iy1;
   box_drawn = True;
   /* check if we have the right cursor */
   new_cursor = 0;
   if (ix2 < ix1) new_cursor++;
   if (iy2 < iy1) new_cursor +=2;
   if (new_cursor != current_cursor) {
    XChangeActivePointerGrab(display, PointerMotionHintMask | ButtonPressMask
    | ButtonReleaseMask |ButtonMotionMask, cursor[new_cursor], CurrentTime);
   current_cursor = new_cursor;
   }
   }
   break;
	
  case ButtonRelease:
   //printf("release\n");
   if (box_drawn) {
   ix = MIN(xlast1, xlast2);
   iy = MIN(ylast1, ylast2);
   dx = ABS(xlast1 - xlast2);
   dy = ABS(ylast1 - ylast2);
   draw_root_box_inverted(gc, ix, iy, dx, dy);

   XUngrabServer(display);
   XUngrabPointer(display, CurrentTime);
   /* get final box parameters */
   box_decr[0] = ix;  box_decr[1] = iy;
   box_decr[2] = dx;  box_decr[3] = dy;
   box_drawn = False;
   while (XCheckMaskEvent(display, ButtonReleaseMask | ButtonPressMask, 
                            &report));
   XSelectInput(display, root, None);
   return 1;
   }
  break;
  default:
    /* StructureNotify events shouldn't appear here,
     * because of the ChangeActivePointerGrab
     * call, but they do for some reason. */
    /* Anyway, it doesn't matter */
    //printf("unexpected event type %d\n", report.type);
    break;
 } /* end switch */
 } /* end outer while */
 /* we should never get here */
 return 1;
 }
 /*-------------------------------------------------------------------------*/
int load_to_triplet(XImage *image, char *pout, int nx, int ny)
 {
  int	pad, r_bias, g_bias, b_bias, bits, m, n, nbl;
  unsigned char	*ptr, *ptr_r, *ptr_g, *ptr_b, *pline_r, *pline_g, *pline_b;

  //printf("in the 24/32 bit section\n");
  /* just a sanity check it case we made a bad call, check nx,ny against
  the XImage entries, the former should be the size allocated to pout */
  if (nx != image->width || ny != image->height) {
  	printf("internal error in load_to_triplet\n");
  	return -1; }
  ptr = (unsigned char *) image->data;
  rmask = image->red_mask;
  gmask = image->green_mask;
  bmask = image->blue_mask;
  //printf("rmask = %#x, gmask = %#x, bmask = %#x\n", rmask, gmask, bmask);
  /* we assume these are 8 bit masks but allow any order, though the normal
  case seems to be rgb */
  if (rmask < gmask)
   if (rmask < bmask) { r_bias = 0;
     if (gmask < bmask) { g_bias=1; b_bias=2;} else { g_bias=2; b_bias=1;}
    } else { b_bias = 0; r_bias = 1; g_bias=2; }
   else
   if (gmask < bmask) { g_bias = 0;
     if (rmask < bmask) { r_bias=1; b_bias=2;} else { r_bias=2; b_bias=1;}
    } else { b_bias = 0; g_bias = 1; r_bias=2; }
  pad = 0;
  bits = image->bits_per_pixel;
  //printf("bits/pixel = %d\n", bits);
  if (bits == 24) pad = 3;
  if (bits == 32) pad = 4;
  if (!pad) { printf("can't handle %d bits per pixel\n", bits);
  	return -1; }
  //printf("pad = %d\n", pad);
  /* that is the little endian result, have to flip for big endian */
#if LITTLEENDIAN
#else
  r_bias = pad - 1 - r_bias;
  g_bias = pad - 1 - g_bias;
  b_bias = pad - 1 - b_bias;
#endif
  ptr_r = pline_r = ptr + r_bias;
  ptr_g = pline_g = ptr + g_bias;
  ptr_b = pline_b = ptr + b_bias;
  //printf("r_bias, g_bias, b_bias = %d %d %d\n",r_bias, g_bias, b_bias);
  //printf("ptr_r, ptr_g, ptr_b = %d %d %d\n",ptr_r, ptr_g, ptr_b);
  nbl = image->bytes_per_line;
  //printf("bytes/line = %d\n", nbl);
  m = ny;
  while (m--) {
   n = nx;
   ptr_r = pline_r;  ptr_g = pline_g;  ptr_b = pline_b;
   pline_r += nbl;   pline_g += nbl;   pline_b += nbl;
   while (n--) {
    *pout++ = *ptr_r;
    *pout++ = *ptr_g;
    *pout++ = *ptr_b;
    ptr_r += pad; ptr_g += pad; ptr_b += pad;
  }  }

 return 1;
 }
 /*-------------------------------------------------------------------------*/
int xwd_root(ix, iy, nx, ny, pout)
 int ix, iy;
 unsigned int  nx, ny;
 char *pout;
 {
 extern char *vis_class_str[];
 Window	src;
 int	transparentOverlays , multiVis, numVisuals, numImageVisuals;
 int	numOverlayVisuals, allImage = 0;
 int	format = ZPixmap;
 int	nbytes, bytes_per_line;
 XVisualInfo         *pVisuals;
 XVisualInfo         **pImageVisuals;
 XImage	*image;
 list_ptr            vis_regions;    /* list of regions to read from */
 list_ptr            vis_image_regions ;
 OverlayInfo         *pOverlayVisuals;

 src = RootWindow(display,screen_num);
 
 /* check if mulit-visual */
 multiVis = GetMultiVisualRegions(display, src, 
     ix, iy, nx, ny, &transparentOverlays, &numVisuals, &pVisuals,
     &numOverlayVisuals, &pOverlayVisuals, &numImageVisuals,
     &pImageVisuals, &vis_regions, &vis_image_regions, &allImage) ;
 //printf("multiVis = %d\n", multiVis);
 //printf("numVisuals = %d, numOverlayVisuals = %d, numImageVisuals = %d\n",
 //	numVisuals, numOverlayVisuals, numImageVisuals);

 /* the multi-visual case always puts everything into a 24 bit image */
 /* force a ReadAreaToImage since the visual for a single window case
 usually isn't the root visual */
 //if(!multiVis)
 //  image = XGetImage(display, src, ix, iy, nx, ny,
 //             AllPlanes, format);
 // else
   image = ReadAreaToImage(display, src, ix, iy, nx, ny,
    	      numVisuals, pVisuals, numOverlayVisuals, pOverlayVisuals,
              numImageVisuals, pImageVisuals, vis_regions,
              vis_image_regions, format, allImage);
 if (!image) {
   fprintf (stderr, "xwd_root: unable to get image at %dx%d+%d+%d\n",
	nx, ny, ix, iy);
	return -1; }
 //printf("\n final image: depth, nx, ny = %d %d %d\n",
 //	image->depth, image->width,image->height);
 //printf("bits per pixel = %d,", image->bits_per_pixel);
 //printf("  bytes_per_line = %d\n", image->bytes_per_line);
 /* load image into our array, may be some differences in storage */
 nbytes = 3*nx*ny;	/* for pout */
 if (image->bytes_per_line == nx*3)  bcopy(image->data, pout, nbytes);
 /* but usually not that lucky */
 load_to_triplet(image, pout, nx, ny);
 
 XDestroyImage(image);
 return 1;
 }
 /*------------------------------------------------------------------------*/
