/* From csx74@pollux.cs.uga.edu Mon May 26 10:23:01 1997 */
/* FILE = xhello.c */
/* to compile: gcc -g -I/usr/openwin/include xhello.c -lX11 -o xhello */
/* from O Jones, INTRO TO THE X WINDOW SYSTEM, P-H 1989, p 470. */
/* QA76.76.W56J66 1989 */

/* As you can see, this code has its roots in the code handed out
   via email. */

/* The modifications were made by me (greear@cs.uga.edu). */

/* This is a fairly simple example of an Xwindow program.  It is writen
   with the Xlib library, so it's very low level stuff.  I'm not yet an 
   expert in X programming, so if you find any glaring style mistakes..let
   me know!

   This program draws boxes, circles, and connects them with dotted lines.

   It also displays text, but, as was requested by my professor, the text
   is backwards.  At any rate, it might be usefull as an example.

   CTRL-C will exit the program btw.
*/


#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "list2.h"
#include <unistd.h>
#include <stdio.h>
#include "string2.h"
#include <iostream.h>


/* other functions in this file */
void do_event(XEvent& myE, GC& myGC, KeySym& myK, int& done, int remember_it);
void do_expose(GC& myGC, KeySym& myK, XEvent& myE);


/* circle and box globals */
int last_circle_y, last_circle_x, last_square_x, last_square_y;
char set_circle, set_square;

/* reversed text globals */
int cs_x, cs_y;
String cur_string;


/* global state, was ctrl the last key pressed?? */
int ctrl;

/* debugging function */
void log(const char* msg) {
  cerr << msg << endl << flush;
}//log


/* used for redraws (expose events) */
List<XEvent*> past_events;


main(int argc, char** argv) {
   Display *myD;
   Window myW;
   GC myGC;
   XEvent myE;
   KeySym myK;
   XSizeHints myH;
   int myS,done;
   unsigned long myFG,myBG;

   myD=XOpenDisplay("");                 /* initialization */
   myS=DefaultScreen(myD);
   set_circle = set_square = FALSE;

   /* set back/fore ground here */
   myFG=WhitePixel(myD,myS);             /* default pixel values */
   myBG=BlackPixel(myD,myS);

   /* fix up hints */
   myH.x=300; myH.y=40; myH.width=400; myH.height=300;
   myH.flags=PPosition | PSize;    /* default window position and size */
   myW=XCreateSimpleWindow(myD,DefaultRootWindow(myD),myH.x,myH.y,
                   myH.width,myH.height,5,myFG,myBG); /* window creation */

   String title(100);
   Sprintf(title, "Graphics Window %s", getenv("LOGNAME"));
   XSetStandardProperties(myD,myW,title,title,None,argv,argc,&myH);
   myGC=XCreateGC(myD,myW,0,0);        /* GC creation and initialization */

   /* cursor modification */
   Cursor curse;
   curse = XCreateFontCursor(myD, (unsigned int)('+'));
   XDefineCursor(myD, myW, curse);

   /* change here for diff background/foreground */
   XSetBackground(myD,myGC,myBG); 
   XSetForeground(myD,myGC,myFG);

   /* input event selection */
   XSelectInput(myD,myW, 
		ButtonPressMask| KeyPressMask| ExposureMask |
		PointerMotionMask);
   XMapRaised(myD,myW);                               /* window mapping */

   for(done=0;(!done); ){                    /* main event-reading loop */
      XNextEvent(myD,&myE);                      /* read the next event */
      do_event(myE, myGC, myK, done, TRUE);
   }
   XFreeGC(myD,myGC); 
   XDestroyWindow(myD,myW); 
   XCloseDisplay(myD);

   exit(0);                                              /* termination */
}


/* redraws the window, from a list of past events */
void do_expose(GC& myGC, KeySym& myK, XEvent& myE) {
  Cell<XEvent*> cll(past_events);
  XEvent* ptr;
  int junk;

  String buf(50);
  Sprintf(buf, "Greetings, Universe from %s", getenv("LOGNAME"));

  XDrawImageString(myE.xexpose.display,
		   myE.xexpose.window,myGC,50,50,buf,buf.Strlen());
   

  while (ptr = Next(cll)) {
    do_event(*ptr, myGC, myK, junk, FALSE);
  }//while
}//do_expose


/* This is the event handler, does all the real work.. */
void do_event(XEvent& myE, GC& myGC, KeySym& myK, int& done, 
	      int add_to_past_events) {
  char text[10];
  XGCValues myVal;

  switch(myE.type) {
  case Expose: {               /* repaint window on expose events */
    if(myE.xexpose.count==0) {
      do_expose(myGC, myK, myE);
    } //if
    break;
  }
  case MappingNotify: {        /* process keyboard mapping changes */
    XRefreshKeyboardMapping((XMappingEvent *)&myE); 
    if (add_to_past_events) {
      XEvent* s_event;
      s_event = new XEvent;
      *s_event = myE;
      Put(s_event, past_events);
    }//if
    break;
  }
  case ButtonPress: {              /* process mouse-button presses */
    if (add_to_past_events) {
      XEvent* s_event;
      s_event = new XEvent;
      *s_event = myE;
      Put(s_event, past_events);
    }//if
    XButtonEvent* xb = (XButtonEvent*)(&myE);
    if (xb->button == Button1) { //if pressed left button
      XDrawRectangle(myE.xbutton.display, myE.xbutton.window, myGC, 
		     myE.xbutton.x, myE.xbutton.y, 7,7);
      last_square_x = myE.xbutton.x + 3;
      last_square_y = myE.xbutton.y + 3;
      set_square = TRUE;
    }
    else if (xb->button == Button2) { //if pressed left button
      if (set_circle && set_square) {
	/* modify the GC */
	myVal.line_style = LineOnOffDash;
	XChangeGC(myE.xbutton.display, myGC, GCLineStyle, &myVal);

	XDrawLine(myE.xbutton.display, myE.xbutton.window, myGC, 
		  last_circle_x, last_circle_y,
		  last_square_x, last_square_y);
	myVal.line_style = LineSolid;
	XChangeGC(myE.xbutton.display, myGC, GCLineStyle, &myVal);
      }//if
    }
    else if (xb->button == Button3) { //if pressed left button
      XDrawArc(myE.xbutton.display, myE.xbutton.window, myGC, 
		     myE.xbutton.x, myE.xbutton.y, 7, 7, 0, 360 * 64);
      last_circle_x = myE.xbutton.x + 3;
      last_circle_y = myE.xbutton.y + 3;
      set_circle = TRUE;
    }
    else {
      printf("ERROR:  Button unrecognized.\n");
    }//else
    break;
  }//button pressed
  case KeyPress: {                     /* process keyboard presses */
    if (add_to_past_events) {
      XEvent* s_event;
      s_event = new XEvent;
      *s_event = myE;
      Put(s_event, past_events);
    }//if

    int i;
    i = XLookupString((XKeyEvent *)&myE,text,10,&myK,0);

    char* ks = XKeysymToString(myK);

    /* good debugger!!
    printf("Text:  :%s:  Keysym:  :%s:\n", text, ks);
    */

    /* check for exit (ctrl-c) */
    if (strcmp(ks, "Control_L") == 0) {
      ctrl = TRUE;
      break;
    }//if
    else if ((strcmp(ks, "c") == 0) && (ctrl)) {
      exit(0);
    }//if

    /* reset global state */
    if (i) {
      ctrl = FALSE;
    }//if

    if (i == 1) {
      /* action upon a return...move the cursor down a bit. */
      if ((text[0] == '\n') || (text[0] == '\r')) {
	cs_y += 12;
	cur_string.Vclear();
      }//if
      else if (text[0] == '\b') {        /* backspace */
	int len = cur_string.Strlen();
	String tmp(len);
	for (int z = 1; z < len; z++) {
	  tmp += cur_string[z];
	}//for
	tmp += " ";
	cur_string = tmp;
      }//else
      else {                              /* all others */
	cur_string.Prepend(text);
      }//else
    }//if i == 1
    else if (i) { //not sure it'll ever get here
      cur_string.Prepend(text);
    }//if

    XDrawImageString(myE.xexpose.display,
		     myE.xexpose.window,myGC,cs_x,cs_y,cur_string,
		     cur_string.Strlen());
    
    /* the cursor thing doesn't work so well!!
    XFillRectangle(myE.xbutton.display, myE.xbutton.window, myGC, 
		   cs_x - 5, cs_y - 5, 4, 7);
    */

    break;
  }//case
  case MotionNotify: {
    /* catch mouse movements...update posn of write string */
    if (add_to_past_events) {
      XEvent* s_event;
      s_event = new XEvent;
      *s_event = myE;
      Put(s_event, past_events);
    }//if

    cur_string.Vclear();
    XMotionEvent* xm = (XMotionEvent*)(&myE);
    cs_x = xm->x;
    cs_y = xm->y;

    break;
  }//case
  }//switch
}//event handler












