/*
 * Get graphical coordinates from screen
 * 
 * This contains bits of code hacked from the
 * X Consortium and from Octave. Please see the
 * appropriate licences. The rest is mine, and
 * you can do what you want with that part.
 * 
 * Copyright (C) 1997 Andy Adler <adler@ncf.ca>
 * 
 * Compile like this
 * mkoctfile -L/usr/X11R6/lib -lX11 -I/usr/X11R6/include/ ginput.cc 
 *
 * Please excuse the ugly code. I wrote while I was learning C.
 */

/*
 * Copyright (C) 2001 Laurent Mazet <mazet@crm.mot.com>
 *
 * Fix error handler to avoid octave core-dump.
 * Change to avoid the input limit.
 * Minimize the number of cliks for full x-y axis definitions.
 * Make the code a bit less ugly.
 */

#include <string>

#include <octave/oct.h>
#include <octave/toplev.h>
#include <octave/pager.h>

extern "C" {
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
}
using namespace std;

#define maxpoints 10

DEFUN_DLD (grab, args, nargout,
           "[...] = grab (...)\n"
           "\n"
           "grab: grab points mouse clicks on the screen\n"
           " \n"
           "[x,y]= grab(axis)\n"
           " x -> x coordinates of the points\n"
           " y -> y coordinates of the points\n"
           "\n"
           " axis -> if specified then the first 2 clicks\n"
           "      must be on the appropriate axes. x and y (or just x\n"
           "      if only 2 points specified ) will then be normalised.\n"
           "\n"
           "for example: x=grab([1 10]) \n"
           "   the first two clicks should correspond to x=1 and x=10 \n"
           "   subsequent clicks will then be normalized to graph units.  \n"
           "\n"
           "for example: [x,y]=grab; \n"
           "   gives x and y in screen pixel units (upper left = 0,0 ) \n"
           "\n"
           "select points with button #1. Buttons #2 and #3 quit. ") {

  ColumnVector axis;
  ColumnVector xaxis(2);
  ColumnVector yaxis(2);
  int nc;
  
  switch (args.length()) {
  case 0:
    nc = 0;
    break;
  case 1:
      { // we need to do this to allow arbitrary orientation
         ColumnVector tmp( args(0).vector_value() );
	 if (error_state) return octave_value_list();
         axis = tmp;
      }
      nc = axis.length ();
      if ((nc == 2) || (nc == 4))
        break;
  default:
    print_usage ("grab");
    return octave_value_list();
  }
    
  switch (nc) {
  case 2:
    octave_stdout << "First click on x-axis " << axis(0) << endl;
    octave_stdout << "Then click on x-axis " << axis(1) << endl;
    flush_octave_stdout();
    break;
  case 4:
    octave_stdout << "First click on point "
                  << "(" << axis(0) << "," << axis(2) << ")" << endl;
    octave_stdout << "Then click on point "
                  << "(" << axis(1) << "," << axis(3) << ")" << endl;
    flush_octave_stdout();
    break;
  }

  char *displayname = NULL;
  Display *dpy = XOpenDisplay (displayname);

  if (!dpy) {
    error ("grab: unable to open display %s.", XDisplayName(displayname));
    return octave_value_list();
  }

  Cursor  cursor = XCreateFontCursor(dpy, XC_crosshair);

  /* Grab the pointer using target cursor, letting it room all over */
  Window root = RootWindow(dpy,0);
  int done = XGrabPointer(dpy, root, False, ButtonPressMask,
                          GrabModeSync, GrabModeAsync, root,
                          cursor, CurrentTime);
  if (done != GrabSuccess) {
    error ("grab: Can't grab the mouse.");
    return octave_value_list();
  };

  XEvent event;
  XButtonEvent *e = NULL;

  if (nc != 0)
    for (int i=0; i<2; i++) {
      XAllowEvents(dpy, SyncPointer, CurrentTime);
      XWindowEvent(dpy, root, ButtonPressMask, &event);
      
      e = (XButtonEvent *) &event;
      
      if (e->button != 1) {
        XUngrabPointer(dpy, CurrentTime);      /* Done with pointer */
        XCloseDisplay (dpy);
        error ("grab: Not enough points selected.");
        return octave_value_list();
      }

      xaxis (i) = double(e->x_root);
      yaxis (i) = double(e->y_root);
    }


  /* Wait for a click */
  MArray<int> xc(maxpoints);
  MArray<int> yc(maxpoints);

  int nb_elements = 0;
  while (1) {
    XAllowEvents(dpy, SyncPointer, CurrentTime);
    XWindowEvent(dpy, root, ButtonPressMask, &event);
    
    e = (XButtonEvent *) &event;
    
    if (e->button != 1)
      break;
    
    xc (nb_elements) = e->x_root;
    yc (nb_elements) = e->y_root;
    
    nb_elements++;
    
    if (nb_elements == xc.length()) {
      xc.resize (xc.length()+maxpoints);
      yc.resize (yc.length()+maxpoints);
    }
  }

  XUngrabPointer(dpy, CurrentTime);      /* Done with pointer */
  XCloseDisplay (dpy);
  
  double xb=0, xm=1, yb=0, ym=1;
  if ((nc == 2) || (nc == 4)) {
    double xdiff = xaxis(1) - xaxis(0);
    xm = -(axis(0)-axis(1)) / xdiff;
    xb = (xaxis(1)*axis(0)-xaxis(0)*axis(1)) / xdiff;
    if (nc == 4) {
      double ydiff = yaxis(1) - yaxis(0);
      ym = -(axis(2)-axis(3)) / ydiff;
      yb = (yaxis(1)*axis(2)-yaxis(0)*axis(3)) / ydiff;
    }
  }

  ColumnVector x(nb_elements), y(nb_elements);
  for(int i=0; i<nb_elements; i++) {
    x(i) = xc(i)*xm + xb;
    y(i) = yc(i)*ym + yb;
  }

  octave_value_list retval;
  retval (0) = x;
  if (nargout == 2) 
      retval(1) = y;
  
  return retval;
}


syntax highlighted by Code2HTML, v. 0.9.1