/* This is my first attempt at X for JED. */
/*
 *  Copyright (c) 1992, 1994 John E. Davis  (davis@amy.tch.harvard.edu)
 *  All Rights Reserved.
 */

#ifndef VMS
#include <X11/Xlib.h>
#include <X11/Xutil.h>
/* #include <X11/Xos.h> */
#include <X11/Xatom.h>
#include <X11/keysym.h>
#include <X11/cursorfont.h>
#else
#include <decw$include/Xlib.h>
#include <decw$include/Xutil.h>
/* #include <decw$include/Xos.h> */
#include <decw$include/Xatom.h>
#include <decw$include/keysym.h>
#include <decw$include/cursorfont.h>
#endif
#include <stdio.h>

#include "config.h"
#include "display.h"
#include "sysdep.h"
#include "screen.h"
#include "keymap.h"
#include "slang.h"
#include "hooks.h"
#include "jmalloc.h"
#include "ins.h"
#include "ledit.h"
#include "misc.h"
#include "mem.h"
#include "cmds.h"

int Term_Cannot_Insert = 0;
int Output_Rate = 0;
int Use_Ansi_Colors = 1;

/* extern  */


typedef struct 
{
   GC gc;
   unsigned long fg, bg;
   char *fg_name;
   char *bg_name;
} GC_Info_Type;

typedef struct
{
   Window w;
   
   int height, width;
   int border;			       /* inside border */
   int o_border;		       /* outside border */
   Colormap color_map;
   
   /* font information */
   XFontStruct *font;
   char *font_name;
   int font_height, font_width, font_base;
   
   /* GC info */
   GC_Info_Type *text_gc;
   GC current_gc;
   GC_Info_Type pointer_gc;
   
   int vis_curs_row, vis_curs_col;     /* position of VISIBLE cursor */
   
   /* flags */
   int cursor_showing;		       /* true if widow has cursor showing */
   int focus;			       /* true if window has focus */
   int window_mapped;		       /* true if window is mapped */
   
   /* Window tty parameters */
   int insert_mode;		       /* true if inserting */
   int scroll_r1,  scroll_r2;	       /* scrolling region */
   int cursor_row, cursor_col;	       /* row column of cursor (1,1) origin */
   
   int visible;			       /* from visibilitynotify */
   Cursor mouse;
   
} JXWindow_Type;
   
static JXWindow_Type This_X_Window;
static JXWindow_Type *XWin;

static Display *This_XDisplay;
static Window This_XWindow;
static int This_XScreen;
static int Performing_Update;
static int No_XEvents;		       /* if true, do nothing */

typedef struct 
{
   char *name;
   char *name1;
   int type;
   char *value;
   char **dflt;
}
XWindow_Arg_Type;

static char *This_App_Name = "XJed";
static char *This_App_Title = "XJed";
static char *This_Geometry = "=80x24+0-0";
static char *This_Font_Name = "fixed";
static char *This_Border_Width_Name = "0";
static char *This_MFG = "green";
static char *This_MBG = "white";

static GC_Info_Type Default_GC_Info[JMAX_COLORS] = 
{
   {NULL, 0, 0, "black", "white"},     /* NORMAL */
   {NULL, 0, 0, "green", "red"},       /* CURSOR */
   {NULL, 0, 0, "black", "skyblue"},   /* STATUS */
   {NULL, 0, 0, "blue", "white"},      /* REGION */
   {NULL, 0, 0, "black", "white"},     /* operator */
   {NULL, 0, 0, "green", "white"},     /* numbers */
   {NULL, 0, 0, "blue", "white"},      /* strings */
   {NULL, 0, 0, "black", "gray"},      /* comments */
   {NULL, 0, 0, "red", "white"},       /* keyword */
   {NULL, 0, 0, "black", "white"}      /* delimeters */
};

static XWindow_Arg_Type X_Arg_List[] =
{
   /* These MUST be in this order!!! */
   {"Display",		"d", 	STRING_TYPE,	NULL,	NULL},
   {"Name",		NULL,	STRING_TYPE,	NULL,	&This_App_Name},
   {"Geometry",		NULL, 	STRING_TYPE,	NULL,	&This_Geometry},

   /* These are in arbitrary order */
   {"Font",		"fn",	STRING_TYPE,	NULL,	&This_Font_Name},
   {"Background",	"bg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JNORMAL_COLOR].bg_name},
   {"Foreground",	"fg",	STRING_TYPE,	NULL,	&Default_GC_Info[JNORMAL_COLOR].fg_name},
   {"Status_fg",	"sfg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JSTATUS_COLOR].fg_name},
   {"Status_bg",	"sbg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JSTATUS_COLOR].bg_name},
   {"Region_fg",	"rfg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JREGION_COLOR].fg_name},
   {"Region_bg",	"rbg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JREGION_COLOR].bg_name},
   {"Cursor_fg",	"cfg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JCURSOR_COLOR].fg_name},
   {"Cursor_bg",	"cbg", 	STRING_TYPE,	NULL,	&Default_GC_Info[JCURSOR_COLOR].bg_name},
   {"Mouse_fg",		"mfg", 	STRING_TYPE,	NULL,	&This_MFG},
   {"Mouse_bg",		"mbg", 	STRING_TYPE,	NULL,	&This_MBG},

   {"Borderwidth",	"bw", 	STRING_TYPE,	NULL,	&This_Border_Width_Name},
   {"Title",		NULL,	STRING_TYPE,	NULL,	&This_App_Title},
   {"Bordercolor",	"bd", 	STRING_TYPE,	NULL,	NULL},
   {"Iconic",		NULL,	VOID_TYPE,	NULL,	NULL},
   {"xrm",		NULL,	STRING_TYPE,	NULL,	NULL},
   {NULL,		NULL,	0,		NULL,	NULL}
};


void hide_cursor ()
{
   unsigned short *s;
   char ch;
   int b = XWin->border, col = XWin->vis_curs_col, row = XWin->vis_curs_row;

   if (No_XEvents) return;
   if (XWin->cursor_showing == 0) return;
   XWin->cursor_showing = 0;
   
   s = JScreen[row].old;
   if (s == NULL) return;
   s += col;
   ch = (char) (*s & 0xFF);
   
   XDrawImageString(This_XDisplay, XWin->w,
		    XWin->text_gc[*s >> 8].gc,
		    col * XWin->font_width + b,
		    row * XWin->font_height + b + XWin->font_base,
		    &ch, 1);
   
}

/* This routine assumes that cursor is in the correct location.  The 
   cursor is placed at the end of the string. */
void tt_write(char *s, int n)
{
   int b = XWin->border;
   if (XWin->window_mapped == 0) return;
   if (No_XEvents) return;
   if (XWin->cursor_showing) hide_cursor ();
   XDrawImageString(This_XDisplay, This_XWindow, XWin->current_gc,
		    (XWin->cursor_col - 1) * XWin->font_width + b,
		    (XWin->cursor_row - 1) * XWin->font_height + b + XWin->font_base,
		    s, n);
		   
   XWin->cursor_col += n;
}



static void copy_rect(int x1, int y1, int x2, int y2, int x3, int y3)
{
   int w, h;
   
   if (No_XEvents) return;
   if (XWin->window_mapped == 0) return;

   y1--; y3--; x1--; x3--;
   w = (x2 - x1) * XWin->font_width;
   h = (y2 - y1) * XWin->font_height;
   x3 = XWin->border + x3  * XWin->font_width; 
   x1 = XWin->border + x1 * XWin->font_width; 
   y3 = XWin->border + y3 * XWin->font_height;
   y1 = XWin->border + y1 * XWin->font_height;
   hide_cursor ();
   XCopyArea (This_XDisplay, This_XWindow, This_XWindow, XWin->current_gc,
	      x1, y1, w, h, x3, y3);
}

static void blank_rect (int x1,  int y1, int x2, int y2)
{
   int w, h;
   
   if (No_XEvents) return;
   if (XWin->window_mapped == 0) return;

   x1--;
   w = (x2 - x1) * XWin->font_width;
   y1--;
   h = (y2 - y1) * XWin->font_height;
   
   x1 = XWin->border + x1 * XWin->font_width; 
   y1 = XWin->border + y1 * XWin->font_height;
   hide_cursor ();
   XClearArea (This_XDisplay, This_XWindow, x1, y1, w, h, 0);
}

void set_scroll_region(int r1, int r2)
{
    XWin->scroll_r1 = r1;
    XWin->scroll_r2 = r2;
}

void reset_scroll_region(void)
{
   XWin->scroll_r1 = 1;
   XWin->scroll_r2 = Screen_Height;
}


static void show_cursor ()
{
   unsigned short *s;
   char ch;
   int c, r, b;
   GC gc;
   
   if (No_XEvents) return;
   if (XWin->cursor_showing) hide_cursor ();

   XWin->cursor_showing = 1;
   r = XWin->vis_curs_row = XWin->cursor_row - 1;
   c = XWin->vis_curs_col = XWin->cursor_col - 1;
   b = XWin->border;
   
   s = JScreen[r].old;
   if (s == NULL) return;
   
   s += c;
   ch = (char) (*s & 0xFF);
   gc = XWin->text_gc[JCURSOR_COLOR].gc;
      
   if (XWin->focus)
     {
	XDrawImageString(This_XDisplay, This_XWindow, 
			 gc, 
			 c * XWin->font_width + b,
			 r * XWin->font_height + b + XWin->font_base,
			 &ch, 1);
     }
   else
     {
	XDrawRectangle( This_XDisplay, This_XWindow, 
		       gc,
		       c * XWin->font_width + b,
		       r * XWin->font_height + b,
		       XWin->font_width - 1,
		       XWin->font_height - 1);
     }
   
   XFlush(This_XDisplay);
}


static void toggle_cursor (int on)
{
   if (on)
     {
	if (XWin->focus) return;
	XWin->focus = 1;
     }
   else 
     {
	if (XWin->focus == 0) return;
	XWin->focus = 0;
     }
   show_cursor ();
}

   
void send_string_to_term(char *s)
{
   tt_write(s, strlen(s));
   if (Performing_Update) return;
   show_cursor ();
}
   

void goto_rc(int r, int c)
{
   if (XWin == NULL) return;
   if (XWin->cursor_showing) hide_cursor ();
   if (r > Screen_Height) r = Screen_Height; 
   if (c > Screen_Width) c = Screen_Width; 
   XWin->cursor_row = r + XWin->scroll_r1 - 1;
   XWin->cursor_col = c;
   if (Performing_Update) return;
   show_cursor ();
}

void begin_insert(void)
{
   XEvent ev;
   
   if (No_XEvents) return;
   hide_cursor ();
   copy_rect(XWin->cursor_col, XWin->cursor_row, Screen_Width - 1, XWin->cursor_row,
	     XWin->cursor_col + 1, XWin->cursor_row);
   XWin->insert_mode = 1;
   
   /* The noexpose events really mess up input pending timing.  
      Stop them now. */
   XFlush(This_XDisplay);
   if (!XPeekEvent(This_XDisplay, &ev) || (ev.type != NoExpose)) return;
   XNextEvent(This_XDisplay, &ev);
}

void end_insert(void)
{
   XWin->insert_mode = 0;
}

void tt_delete_char()
{
   copy_rect(XWin->cursor_col + 1, XWin->cursor_row, Screen_Width, XWin->cursor_row,
	     XWin->cursor_col, XWin->cursor_row);
}

void tt_erase_line(void)
{
   if (XWin == NULL) return;
   blank_rect(1, XWin->cursor_row, Screen_Width, XWin->cursor_row);
}

/* Must respect scrolling region */
void tt_delete_nlines(int n)
{
   int r1, r2;
   r1 = XWin->cursor_row;
   r2 = XWin->scroll_r2;
   
   if (r1 <= r2 - n) copy_rect(1, r1 + n, Screen_Width, r2, 
			       1, XWin->cursor_row);
   
   if (Scroll_By_Copying == 0) blank_rect(1, r2 - n + 1, Screen_Width, r2);
}


void reverse_index(int n)
{
   int r1, r2;
   r1 = XWin->scroll_r1;
   r2 = XWin->scroll_r2;
   
   if (r2 >= r1 + n) copy_rect(1, r1, Screen_Width, r2 - n,
			       1, r1 + n);
   
   if (Scroll_By_Copying == 0) blank_rect(1, r1, Screen_Width, r1 + n - 1); 
}

int beep(void)
{
   if (No_XEvents) return 0;
   XBell (This_XDisplay, 50);
   flush_input();
   return 0;
}

void tt_del_eol(void)
{
   blank_rect(XWin->cursor_col, XWin->cursor_row, Screen_Width, XWin->cursor_row);
}

static int Rev_Vid_Flag;
static void tt_reverse_video(int color)
{
   Rev_Vid_Flag = color;
   XWin->current_gc = XWin->text_gc[color].gc;
}

void tt_normal_video(void)
{
   if (XWin == NULL) return;
   Rev_Vid_Flag = JNORMAL_COLOR;
   XWin->current_gc = XWin->text_gc[JNORMAL_COLOR].gc;
}




void send_attr_str(unsigned short *s, unsigned short *smax)
{
   unsigned char out[250], ch, attr, *p;
   register unsigned short sh;
   
   p = out;
   while (s < smax)
     {
	sh = (unsigned short) *s++;
	ch = sh & 0xFF;
	attr = sh >> 8;
	if ((attr == 0) && (Rev_Vid_Flag != 0))
	  {
	     if (p != out)
	       {
		  *p = 0;
		  send_string_to_term((char *) out);
		  p = out;
	       }
	     tt_normal_video();
	     /* Rev_Vid_Flag = 0; */
	  }
	else if ((attr != 0) && (Rev_Vid_Flag != attr))
	  {
	     if (p != out)
	       {
		  *p = 0;
		  send_string_to_term((char *) out);
		  p = out;
	       }
	     tt_reverse_video(attr);
	     /* Rev_Vid_Flag = 1; */
	  }
	*p++ = ch;
     }
   *p = 0;
   if (p != out) send_string_to_term((char *) out);
   /* if (Rev_Vid_Flag) tt_normal_video(); */
}



void smart_puts(unsigned short *neww,unsigned short *oldd, int row, int spc)
{
   unsigned short out[250], *mark;
   register unsigned short *p, ch, ch1;
   register unsigned short *neew = neww, *old = oldd;
   int ii,max_len,i, curs_set = 0;
   unsigned short *new_save;
   int curs = 0;

   if (XWin->window_mapped == 0) return;
    i = 0;
    ii = 0;
    max_len = 6;
   
   goto_rc(row, 1);
   /* many times we are scrolling and line to compare is blank.  Treat this
    case special */
   if (spc == 0)
     {
	p = neew;
	while(*p == ' ') p++;
	
	if (*p == 0) return;
	goto_rc(row, p - neew + 1);
	old = out;
	ch1 = ' '; 
	while (1)
	  {
	     while (ch = *p++, (ch1 != ch) && ch) *old++ = ch;
	     mark = old;
	     if (!ch) break;
	     *old++ = ch1;
	     while(ch = *p++, (ch == ch1) && ch) *old++ = ch;
	     if (old - mark > max_len)
	       {
		  send_attr_str(out, mark);
		  if (ch == 0) return;
		  if (mark != out) goto_rc(row, p - neew);
		  old = out;
	       }
	     if (!ch) break;
	     p--;
	  }
	send_attr_str(out, old);
	return;
     }
   
   
    /* while they match, go on */
   /* Note that neew - new_save is then column of character */
   new_save = neew + 1;
   while (((ch = *neew++) == *old++) && ch);
   i += neew - new_save;

   if (!ch)
    /* we are at the end of the new, so delete eond of old line */
      {
	 if ((ch1 = *(old - 1)) == ' ')
	   {
	      while (*old++ == ch1);
	      ch1 = *(old - 1);
	   }

	 if (ch1 == 0) return;

	 goto_rc(row, i + 1);
	 tt_del_eol();
	 return;
      }


    if (i)
      {
	 curs_set = 1; 
	 curs = i + 1;
      }

    while(1)
      {
	 ch1 = 0;
	 p = out;
	 *p++ = ch;
	 while (ch1 = *old++, ch = *neew++, (ch != ch1) && ch) *p++ = ch;
	 mark = p;
         *p++ = ch;
	 if (ch) while (ch = *neew++, ch1 = *old++, (ch == ch1) && ch)
	   {
	      *p++ = ch;
	   }
	 /* *p = 0; */   /* Value of p at this point is used below. */
	 i = p - mark;
	 if (i > max_len)
	   {
	      /* *mark = 0; */      /* mark is used below */
	      if (curs)
		{
		   XWin->cursor_col = curs;
		   curs = 0;
		}
	      
	      if (!curs_set)
		{
		   XWin->cursor_col = 1;
		   curs_set = 1;
		}

	      send_attr_str(out, mark);
	      if (!ch)
		{
		   if (ch1)
		     {
			old--;  ch = ' ';
			while (ch1 = *old++, (ch1 == ch));
		     }

		   if (ch1 == 0) return;
		   goto_rc(row, 1 + (neew - new_save));
		   
		   tt_del_eol();
		   return;
		}
	      
	      if (i)
		{
		   curs = 1 + (neew - new_save);
		   curs_set = 1;
		}
	   }
	 else
	   {
	      if (curs)
		{
		   goto_rc(row, curs);
		   curs = 0;
		}
	      if (!curs_set)
		{
		   goto_rc(row, 1);
		   curs_set = 1;
		}
	      send_attr_str(out, p);
	      if (!ch)
		{
		   if (ch1 == ' ')
		     {
			while (*old++ == ch1);
			ch1 = *(old - 1);
		     }
		   
		   if (ch1) tt_del_eol();
		   return;
		}
	   }
      }
}


static int push_mouse_event (int type, int x, int y, unsigned int button, unsigned int state )
{
   register unsigned int s = 0, but;
   int ch;
   static int last_button;

   if (button == 0) button = last_button;
   JMouse.type = (type == ButtonPress) ? 1 : 0;
   if (JMouse.type) last_button = button;
   
   JMouse.x = 1 + (x - XWin->border) / XWin->font_width;
   JMouse.y = 1 + (y - XWin->border) / XWin->font_height;
   but = JMouse.button = (int) button;
   if (but == Button1) ch = 'l'; else if (but == Button2) ch = 'm'; else ch = 'r';
   if (state & Button1Mask) s |= 1;
   if (state & Button2Mask) s |= 2;
   if (state & Button3Mask) s |= 4;
   if (state & ShiftMask) 
     {
	s |= 16;
	ch = 'A' + (ch - 'a');
     }
   if (state & ControlMask) 
     {
	s |= 8;
	if (ch >= 'a') ch = ch - 'a' + 1;
     }
   JMouse.state = s;
   return ch;
}

void cover_exposed_area (int x, int y, int width, int height, int count)
{
   unsigned short *s, *smax;
   int row, save_row, save_col, max_col, max_row, col;
   
   Performing_Update++;
   hide_cursor ();
   save_row = XWin->cursor_row;
   save_col = XWin->cursor_col;
   col = (x - XWin->border) / XWin->font_width;
   row = (y - XWin->border) / XWin->font_height;
   if (row == 0) row = 1;
   if (col == 0) col = 1;
   
   max_col = 2 + col + width / XWin->font_width;
   max_row = 2 + row + height / XWin->font_height;
   if (max_col > Screen_Width) max_col = Screen_Width;
   if (max_row > Screen_Height) max_row = Screen_Height;
   
   for (XWin->cursor_row = row; XWin->cursor_row <= max_row; XWin->cursor_row++)
     {
	XWin->cursor_col = col;
	s = JScreen[XWin->cursor_row - 1].old + (XWin->cursor_col - 1);
	smax = JScreen[XWin->cursor_row - 1].old + max_col;
	send_attr_str(s, smax);
     }
   XWin->cursor_row = save_row;
   XWin->cursor_col = save_col;
   Performing_Update--;

   if (count == 0) show_cursor ();
}

#include "xkeys.c"

/* if force is true, wait for an event.  If force is false, only
   process events that exist.  This will return either when there 
   are no more events or a key/mouse event is processed returning
   1 in the process */
int X_process_events (int force, char *buf, int *n_chars)
{
   XEvent report;
   int ch1;
   int block_expose = 0;
   char *bufp;
   KeySym ks;
   int esc = 27;
   

   Window root, child;
   int posx, posy, rootx, rooty;
   unsigned int keys_buttons;
   int last_x, last_y;
   static int last_event, motion_button;
   static unsigned int motion_state;
   
   int width, height;
   
   while (force || XPending(This_XDisplay))
     {
	XNextEvent(This_XDisplay, &report);
	
	switch  (report.type) 
	  {
	   case VisibilityNotify: XWin->visible = report.xvisibility.state;
	     break;
	     
	   case MotionNotify:
	     
	     while (XCheckMaskEvent (This_XDisplay, ButtonMotionMask, &report));
	     if (!XQueryPointer(This_XDisplay, report.xmotion.window,
				&root, &child, &rootx, &rooty, &posx, &posy,
				&keys_buttons)) break;

	     /* This will ensure that modifier keys are not pressed while
	        we are in motion. */

	     if ((last_event == MotionNotify) && (motion_state != keys_buttons)) break;
	     motion_state = keys_buttons;

	     last_x = JMouse.x;
	     last_y = JMouse.y;
	     
	     
	     ch1 = push_mouse_event (ButtonPress,
				     posx, posy,
				     0,
				     keys_buttons);

	     if ((last_x == JMouse.x) && (last_y == JMouse.y)) break;

	     /* return ESC ^@ ^@ .... */
	     *buf++ = esc; *buf++ = 0; *buf++ = 0; *buf++ = ch1;
	     *n_chars = 4;
	     
	     last_event = MotionNotify;
	     motion_button = JMouse.button;
	     return 1;
	     
	     
	   case NoExpose: break;
	   case Expose:
	     
	     if (block_expose == 0) cover_exposed_area (report.xexpose.x,
							report.xexpose.y,
							report.xexpose.width,
							report.xexpose.height,
							report.xexpose.count);
	     else 
	       {
		  if (report.xexpose.count == 0)
		    {
		       redraw_screen (1);
		       block_expose = 0;
		    }
	       }
	     break;
	     
	   case ConfigureNotify:
	     width = report.xconfigure.width;
	     height = report.xconfigure.height;
	     if ((width != XWin->width) || 
		 (height != XWin->height))
	       {
		  XWin->width = width;
		  XWin->height = height;
		  reset_display ();
		  init_display(1);
		  block_expose = -1;
	       }
	     break;
	     
	   case GraphicsExpose: 
	     cover_exposed_area (report.xgraphicsexpose.x,
				 report.xgraphicsexpose.y,
				 report.xgraphicsexpose.width,
				 report.xgraphicsexpose.height,
				 report.xgraphicsexpose.count);
	     break;
	     
	   case ButtonPress:
	     /* Prohibit dragging more than one button at a time. */
	     if (last_event == MotionNotify) break;
	     /* drop */
	     
	   case ButtonRelease:
	     if ((last_event == MotionNotify) && 
		 (report.xbutton.button != motion_button)) break;
	     
	     last_event = 0;
	     
	     /* ESC ^@ U/D is a  mouse prefix */
	     *buf++ = esc; *buf++ = 0;
	     if (report.type == ButtonPress) ch1 = 'D'; else ch1 = 'U';
	     *buf++ = ch1;
	     
	     *buf++ = push_mouse_event (report.xbutton.type,
					report.xbutton.x, report.xbutton.y,
					report.xbutton.button,
					report.xbutton.state);
	     *n_chars = 4;
	     return 1;
	     break;
	     
	   case KeyPress:
	     bufp = buf;
	     *n_chars = XLookupString(&report.xkey, buf, 20, &ks, NULL);
	     ks = ks & 0xFFFF;
	     if (ks >= 0xFF00)
	       {
		  ks = ks & 0xFF;
		  if (report.xkey.state & ShiftMask) bufp = Shift_KeySym_Mapping [ks];
		  else if (report.xkey.state & ControlMask) bufp = Control_KeySym_Mapping [ks];
		  else bufp = KeySym_Mapping [ks];
		  
		  if (0 != (*n_chars = (int) *bufp++)) MEMCPY(buf, bufp, *n_chars);
	       }
	     
	     if (*n_chars == 0) break;
	     return 1;
	     
	   case EnterNotify:
	     toggle_cursor(report.xcrossing.focus);
	     break;
	     
	   case LeaveNotify:
	     /* toggle_cursor(0); */
	     break;
	     
	   case UnmapNotify: 
	     XWin->window_mapped = 0;
	     break;
	   case MapNotify:
	     XWin->window_mapped = 1;
	     break;

	   case FocusIn:
	     toggle_cursor(1);
	     break;	     
	     
	   case FocusOut:
	     toggle_cursor(0);
	     break;
	     
	   default:
	     /* fprintf(stderr, "Event: %d\n", report.type); */
	     break;
	  }   
     }
   return 0;
}

static int X_read_key ()
{
   int nread;
   char buf[30];
   (void) X_process_events (1, buf, &nread);
   if (nread > 1) ungetkey_string(buf + 1, nread - 1);
   return (int) *buf;
}

static int X_input_pending (void)
{
   XEvent ev;
   int n;
   
   if (No_XEvents) return 0;

   n = XPending (This_XDisplay);
   if (!n) return (0);
   while (n--)
     {
	XPeekEvent(This_XDisplay, &ev);
	  {
	     /* fprintf(stderr, "ev #%d\n", ev.type); */
	     if (ev.type != NoExpose) return 1;
	  }
	XNextEvent(This_XDisplay, &ev);
     }
   return 0;
}


static void X_Get_Term_Size(int *cols, int *rows)
{
   *cols = (XWin->width - XWin->border) / XWin->font_width;
   *rows = (XWin->height - XWin->border) / XWin->font_height;
}

void set_term_vtxxx (int *n)
{
}


void narrow_width ()
{
}
void  wide_width ()
{
}

void enable_cursor_keys(void)
{
}

void cls(void)
{
   if (No_XEvents) return;
   if (XWin->window_mapped == 0) return;
   XClearWindow(This_XDisplay, This_XWindow);
}

/* This function is called assuming that cursor is in correct 
   position */
void tt_putchar(char ch)
{
   if (ch == '\b')
     {
	ch = ' ';
	if (XWin->cursor_col == 1) return;
	XWin->cursor_col--;
     }
   
   if (Rev_Vid_Flag != JNORMAL_COLOR) tt_normal_video();
   tt_write(&ch, 1);
   show_cursor ();
}

/* This routine is called from S-Lang inner interpreter.  It serves
   as a poor mans version of an interrupt 9 handler */
static void xjed_check_kbd(void)
{
   char buf[30];
   int n;
   
   if (Batch || No_XEvents) return;
   while (XPending(This_XDisplay))
     {
	if (X_process_events (0, buf, &n) == 0) continue;
	
	if (*buf == Abort_Char) 
	  {
	     SLang_Error = USER_BREAK;
	     return;
	  }
	buffer_keystring (buf, n);
     }
}

static void xjed_suspend (void)
{
   if (No_XEvents) return;
   /* XIconifyWindow (This_XDisplay, XWin->w, This_XScreen); */
   if (XWin->visible == VisibilityUnobscured) XLowerWindow (This_XDisplay, This_XWindow);
   else XRaiseWindow (This_XDisplay, This_XWindow);
   return;
}

static int load_font (char *font)
{   
   static XFontStruct *xfont;

   xfont = XLoadQueryFont(This_XDisplay, font);
   if (xfont == NULL) return 0;
   
   XWin->font = xfont;
   XWin->font_name = font;
   XWin->font_height = XWin->font->ascent + XWin->font->descent;
   XWin->font_width = XWin->font->max_bounds.width;
   XWin->font_base = XWin->font->ascent;
   return 1;
}



void get_xdefaults ()
{
   XWindow_Arg_Type *xargs = X_Arg_List + 2;  /* skip display, name, etc */
   
   while (xargs->name != NULL)
     {
	if ((xargs->type != VOID_TYPE) && (xargs->dflt != NULL))
	  {
	     if (xargs->value == NULL)
	       {
		  xargs->value = XGetDefault(This_XDisplay, This_App_Name, xargs->name);
	       }

	     if (xargs->value != NULL) 
	       {
		  *xargs->dflt = xargs->value;
	       }
	  }
	xargs++;
     }
}



static void set_window_name (char *s)
{
   if (Batch) return;
   XStoreName (This_XDisplay, XWin->w, s);
   XSetIconName(This_XDisplay, XWin->w, s);
}

static void set_wm_hints (JXWindow_Type *w, int xpos,  int ypos, int orflags)
{
   XSizeHints h;
   XWMHints h1;
   
   h.width_inc = w->font_width;
   h.height_inc = w->font_height;
   h.min_width = 5 * w->font_width + w->border;
   h.min_height = 5 * w->font_height + w->border;
   h.base_height = 0;
   h.base_width = 0;
   h.x = xpos; h.y = ypos;
   h.height = w->height;
   h.width = w->width;
   
   h.flags = PMinSize | PResizeInc | PBaseSize;
   h.flags |= orflags;
   
   XSetWMNormalHints(This_XDisplay, w->w, &h);
   
   /* This bit allows me to track the focus.  It is not at all clear from 
      the documentation. */
   h1.input = 1;
   h1.flags = InputHint;
   XSetWMHints(This_XDisplay, w->w, &h1);
}

/* This parses the colors in the XWin structure and setting 
   defaults to fg, bg upon failure */
static void setup_ith_color (int i, unsigned long fg, unsigned long bg)
{
   XColor xcol;
   
   if (Term_Supports_Color &&
       XParseColor(This_XDisplay, XWin->color_map, XWin->text_gc[i].fg_name, &xcol)
       && XAllocColor(This_XDisplay, XWin->color_map, &xcol))
     {
	fg = xcol.pixel;
     }
   
   XWin->text_gc[i].fg = fg;
   
   if (Term_Supports_Color &&
       XParseColor(This_XDisplay, XWin->color_map, XWin->text_gc[i].bg_name, &xcol)
       && XAllocColor(This_XDisplay, XWin->color_map, &xcol))
     {
	bg = xcol.pixel;
     }
   
   XWin->text_gc[i].bg = bg;
}


/* This is used to set the colors in the Win structure and if f is non-zero,
   the previous definitions are freed.  f is 0 when the colors correspond to the 
   default. */

static void x_set_color_free (int i, char *fgcolor, char *bgcolor, int do_free)
{
   char *save_fg, *save_bg, *fg, *bg;
   unsigned long old_fg, old_bg;
   
   if (NULL == (fg = (char *) MALLOC(strlen(fgcolor) + 1)))
     {
	SLang_Error = SL_MALLOC_ERROR;
	return;
     }
   strcpy (fg, fgcolor);
   if (NULL == (bg = (char *) MALLOC(strlen(bgcolor) + 1)))
     {
	SLang_Error = SL_MALLOC_ERROR;
	FREE (fg);
	return;
     }
   strcpy (bg, bgcolor);
   
   old_fg = XWin->text_gc[i].fg;
   old_bg = XWin->text_gc[i].bg;
   
   save_fg = XWin->text_gc[i].fg_name;
   XWin->text_gc[i].fg_name = fg;
   save_bg = XWin->text_gc[i].bg_name;
   XWin->text_gc[i].bg_name = bg;
   
   setup_ith_color (i, old_fg, old_bg);
   if (do_free)
     {
	if (save_fg != NULL) FREE (save_fg);
	if (save_bg != NULL) FREE (save_bg);
     }
}



static void setup_and_parse_colors ()
{
   unsigned long fg, bg, tmp;
   char *fg_name, *bg_name;

   int i;
   GC_Info_Type *d;
   
   /* Check to see if this is a color display */
   
   bg = WhitePixel (This_XDisplay, This_XScreen); bg_name = "white";
   fg = BlackPixel (This_XDisplay, This_XScreen); fg_name = "black";

   XWin->color_map = DefaultColormap (This_XDisplay, This_XScreen);

   if (DisplayCells (This_XDisplay, This_XScreen) > 2) 
     {
	Term_Supports_Color = 1;
     }
   else Term_Supports_Color = 0;
   
   for (i = 0; i < JMAX_COLORS; i++)
     {
	d = Default_GC_Info + i;
	/* The assumption here is that ALL colors beyond JNORMAL_COLOR (0)
	 * take reversed fg, bgs.  I really ought to have flags if this is
	 * not the case. */
	d->fg = fg;
	d->bg = bg;
	if (d->fg_name == NULL) d->fg_name = fg_name;
	if (d->bg_name == NULL) d->bg_name = bg_name;
	
	if (i == JNORMAL_COLOR)
	  {
	     tmp = fg; fg = bg; bg = tmp;
	     fg_name = bg_name;
	     bg_name = "black";
	  }
	
	x_set_color_free (i, d->fg_name, d->bg_name, 0);
     }
}

static void set_mouse_color (char *fgc, char *bgc)
{
   XColor xfg, xbg;
   
   if (!Term_Supports_Color) return;
   if (!XParseColor(This_XDisplay, XWin->color_map, fgc, &xfg)
       || !XAllocColor(This_XDisplay, XWin->color_map, &xfg))
     {
	return;
     }

   if (!XParseColor(This_XDisplay, XWin->color_map, bgc, &xbg)
       || !XAllocColor(This_XDisplay, XWin->color_map, &xbg))
     {
	return;
     }
   XRecolorCursor (This_XDisplay, XWin->mouse, &xfg, &xbg);
}



static void create_needed_gcs ()
{
   int i;
   XGCValues xgcv;
   xgcv.font = XWin->font->fid;
   for (i = 0; i < JMAX_COLORS; i++)
     {
	xgcv.foreground = XWin->text_gc[i].fg;
	xgcv.background = XWin->text_gc[i].bg;
	
	XWin->text_gc[i].gc = XCreateGC(This_XDisplay, This_XWindow,
					GCForeground | GCBackground | GCFont,
					&xgcv);
     }
}



static Window create_XWindow (JXWindow_Type *win, char *geom, int orflags)
{
   int bdr, xpos, ypos;
   int width, height;
   Window w;
   
   bdr = atoi(This_Border_Width_Name);
   
   width = Screen_Width; height = Screen_Height;   
   xpos = 0, ypos = 0;
   if (geom != NULL)
     {
	(void) XGeometry (This_XDisplay, This_XScreen,
			  geom, This_Geometry,
			  bdr,
			  win->font_width, win->font_height,
			  win->border, win->border,
			  &xpos, &ypos, 
			  &width, &height);
	
     }
   
   win->width = width * win->font_width + 2 * win->border;
   win->height = height * win->font_height + 2 * win->border;

   /* create and display window */
   w = XCreateSimpleWindow(This_XDisplay, RootWindow(This_XDisplay, This_XScreen),
			   xpos, ypos,     /* xpos, ypos */
			   win->width,     /* width, height */
			   win->height,     /* width, height */
			   bdr,	       /* border width */
			   win->text_gc[JNORMAL_COLOR].fg,
			   win->text_gc[JNORMAL_COLOR].bg 
			   );
   
   win->w = w;
   
   set_wm_hints (win, xpos, ypos, orflags);
   
   /* select event types */
   XSelectInput(This_XDisplay, w, 
		ExposureMask | KeyPressMask 
		| ButtonPressMask | ButtonReleaseMask
		| StructureNotifyMask
		| PointerMotionHintMask | ButtonMotionMask
		| EnterWindowMask
		/* | LeaveWindowMask */
		| FocusChangeMask
		| VisibilityChangeMask
		);
   
   if (XWin->mouse) XDefineCursor(This_XDisplay, w, XWin->mouse);
   return w;
}



static int x_err_handler (Display *d, XErrorEvent *ev)
{
   char errmsg[256];
   No_XEvents = 1;
   XGetErrorText (d, ev->error_code, errmsg, 255);
   exit_error (errmsg, 0);
   return 1;
}

static int x_ioerr_handler (Display *d)
{
   No_XEvents = 1;
   exit_error("XWindows IO error", 0);
   return d == NULL;  /* just use d to avoid a warning */
}

/* returns socket descriptor */
int init_Xdisplay ()
{
   int orflags = 0;
   char dname[80], *n;
   
   n = X_Arg_List[0].value;
   if (n != NULL)
     {
	strncpy (dname, X_Arg_List[0].value, 70);
	dname[70] = 0;
	n = dname;
	while (*n && (*n != ':')) n++;
	if (*n == 0) strcpy(n, ":0.0");
	n = dname;
     }
   
   XSetIOErrorHandler (x_ioerr_handler);

   if ( (This_XDisplay = XOpenDisplay(n)) == NULL )
     {
  	(void) fprintf( stderr, "XJED: cannot connect to X server %s\n", 
		       XDisplayName(n));
	exit( -1 );
     }
   
   XSetErrorHandler (x_err_handler);

   if (X_Arg_List[1].value != NULL) This_App_Name = X_Arg_List[1].value;
   XWin = &This_X_Window;
   get_xdefaults ();
   
   XWin->font_name = This_Font_Name;

   This_XScreen = DefaultScreen(This_XDisplay);

   if (!load_font(XWin->font_name))
     {
	(void) fprintf( stderr, "XJED: cannot load font %s, using fixed.\n", XWin->font_name);
	if (!load_font("fixed"))
	  {
	     (void) fprintf( stderr, "XJED: cannot load fixed font.\n");
	     exit( -1 );
	  }
     }
   
   
   XWin->text_gc = Default_GC_Info;
   setup_and_parse_colors ();		       /* This allocs and parses colors */
   
   XWin->mouse = XCreateFontCursor (This_XDisplay, XC_xterm);
   set_mouse_color (This_MFG, This_MBG);
   
   if (X_Arg_List[2].value != NULL) orflags = USPosition | USSize;
   This_XWindow = create_XWindow(XWin, This_Geometry, orflags);
   
   set_window_name (This_App_Title);
   
   /* GCs and their colors */
   create_needed_gcs ();		       /* This uses info from previous call */
   
   XWin->current_gc = XWin->text_gc[JNORMAL_COLOR].gc;
   
   XSetFont (This_XDisplay, XWin->current_gc, XWin->font->fid);

   /* display window */
   
   XMapWindow(This_XDisplay, This_XWindow);
   
   return ConnectionNumber (This_XDisplay);
}


static void reset_Xdisplay (void)
{
   if (This_XDisplay != NULL) XCloseDisplay(This_XDisplay);
}


#define UPCSE(x)  (((x) <= 'z') && ((x) >= 'a') ? (x) - 32 : (x))
static int myXstrcmp(char *a, char *b)
{
   register char cha, chb;
   /* do simple comparison */
   
   cha = *a++;  chb = *b++;
   if ((cha != chb) && (UPCSE(cha) != UPCSE(chb))) return 0;
   while ((cha = *a++), (chb = *b++), (cha && chb) != 0)
     {
	if (cha != chb) return 0;
     }
   
   return (cha == chb);
}

#define STREQS(a, b) myXstrcmp(a, b)

static int X_eval_command_line (int argc, char **argv)
{
   char *arg;
   int i;
   XWindow_Arg_Type *opt;
   
   for (i = 1; i < argc; i++)
     {
	arg = argv[i];
	if (*arg != '-') break;
	arg++;
	opt = X_Arg_List;
	while (opt->name != NULL)
	  {
	     if (STREQS(opt->name, arg) 
		 || ((opt->name1 != NULL) && STREQS(opt->name1, arg))) break;
	     opt++;
	  }
	
	if (opt->name == NULL) break;
	
	if (opt->type == VOID_TYPE) opt->value = "on";
	else if (i + 1 < argc)
	  {
	     i++;
	     opt->value = argv[i];
	  }
	else break;
     }
   
   /* Out of this loop, argv[i] is the last unprocessed argument */
   return i;
}



static void x_set_color (int i, char *what, char *fg, char *bg)
{
   if (!Term_Supports_Color) return;

   if (i == -1)
     {
	if (!strcmp("mouse", what))
	  {
	     set_mouse_color (fg, bg);
	  }
	
	return;
     }
   
   x_set_color_free (i, fg, bg, 1);
   XSetForeground(This_XDisplay, XWin->text_gc[i].gc, XWin->text_gc[i].fg);
   XSetBackground(This_XDisplay, XWin->text_gc[i].gc, XWin->text_gc[i].bg);
}

static int X_Warp_Pending = 0;
static void x_warp_pointer ()
{
   X_Warp_Pending = 1;
}

static void x_region_2_cutbuffer ()
{
   int nbytes;
   char *dat;

   dat = make_buffer_substring(&nbytes);
   if (dat == NULL) return;

   XStoreBytes (This_XDisplay, dat, nbytes);
   FREE (dat);
}

static int x_insert_cutbuffer ()
{
   int nbytes;
   char *dat;

   CHECK_READ_ONLY
   dat = XFetchBytes (This_XDisplay, &nbytes);
   if (nbytes && (dat != NULL)) ins_chars ((unsigned char *) dat, nbytes);
   if (dat != NULL) XFree (dat);
   return nbytes;
}

static void x_set_keysym (int *np, int *shift, char *str)
{
   unsigned int n = (unsigned int) *np;
   KeySym_Mapping_Type *map;
   
   n &= 0xFF;
   str = process_keystring (str);
   if (*shift == '$') map = Shift_KeySym_Mapping;
   else if (*shift == '^') map = Control_KeySym_Mapping;
   else map = KeySym_Mapping;
   MEMCPY (map[n], str, MAX_KEYSYM_STRING_LEN);
}

  
   
static SLang_Name_Type sl_x_table[] =
{
   MAKE_INTRINSIC(".x_set_window_name", set_window_name, VOID_TYPE, 1),
   MAKE_INTRINSIC(".x_warp_pointer", x_warp_pointer, VOID_TYPE, 0),
   MAKE_INTRINSIC(".x_insert_cutbuffer", x_insert_cutbuffer, INT_TYPE, 0),
   /*Prototype: Integer x_insert_cut_buffer ();
     Inserts cutbuffer into the current buffer and returns the number
     of characters inserted.
     */
   MAKE_INTRINSIC(".x_copy_region_to_cutbuffer", x_region_2_cutbuffer, VOID_TYPE, 0),
   /*Prototype: Void x_copy_region_to_cutbuffer();
    */
   MAKE_INTRINSIC(".x_set_keysym", x_set_keysym, VOID_TYPE, 3),
   /*Prototype Void x_set_keysym (Integer keysym, Integer shift, String str);

    This function may be used to assocate a string 'str' with a key 'keysym'
    modified by mask 'shift'. Pressing the key associated with 'keysym' will
    then generate the keysequence given by 'str'. The function keys are
    mapped to integers in the range 0xFF00 to 0xFFFF.  On most systems, the
    keys that these mappings refer to are located in the file
    /usr/include/X11/keysymdef.h. For example, on my system, the keysyms for
    the function keys XK_F1 to XK_F35 fall in the range 0xFFBE to 0xFFE0.
    So to make the 'F1' key correspond to the string given by the two
    characters Ctrl-X Ctrl-C, simply use:

         x_set_keysym (0xFFBE, 0, "^X^C");
	 
    The 'shift' argument is an integer with the following meanings:
    
        0   : unmodified key
       '$'  : shifted
       '^'  : control
       
    Any other value for shift will default to 0 (unshifted).
	 
     */

     
   /*Prototype: Void x_copy_region_to_cutbuffer();
    */
      
   SLANG_END_TABLE
};


static int X_init_slang (void)
{
   return (SLang_add_table(sl_x_table, "XJed") && SLdefine_for_ifdef("XWINDOWS"));
}


static void X_update_open ()
{
   hide_cursor ();
   Performing_Update = 1;
}

static void X_update_close ()
{
   Performing_Update = 0;
   if (JWindow->trashed) return;
   show_cursor ();
   if (X_Warp_Pending) XWarpPointer (This_XDisplay, None, XWin->w, 0, 0, 0, 0,
				     XWin->vis_curs_col * XWin->font_width + XWin->border,
				     (XWin->vis_curs_row + 1) * XWin->font_height + XWin->border);
   X_Warp_Pending = 0;
}

static void x_define_xkeys (Key_Type *map)
{
   define_key1("^[[^D", (VOID *) scroll_right, F_INTRINSIC, Global_Map);
   define_key1("^[[d", (VOID *) scroll_right, F_INTRINSIC, Global_Map);
   define_key1("^[[^C", (VOID *) scroll_left, F_INTRINSIC, Global_Map);
   define_key1("^[[c", (VOID *) scroll_left, F_INTRINSIC, Global_Map);
   define_key1("^[[a", (VOID *) bob, F_INTRINSIC, Global_Map);
   define_key1("^[[^A", (VOID *) bob, F_INTRINSIC, Global_Map);
   define_key1("^[[b", (VOID *) eob, F_INTRINSIC, Global_Map);
   define_key1("^[[^B", (VOID *) eob, F_INTRINSIC, Global_Map);
}


void get_terminfo ()
{
   Screen_Width = 80;
   Screen_Height = 24;
   Scroll_By_Copying = 1;
   
   /* init hooks */
   X_Read_Hook = X_read_key;
   X_Input_Pending_Hook = X_input_pending; 
   X_Get_Term_Size_Hook = X_Get_Term_Size;
   X_Update_Open_Hook = X_update_open;
   X_Update_Close_Hook = X_update_close;
   X_Suspend_Hook = xjed_suspend;
   X_Argc_Argv_Hook = X_eval_command_line;
   X_Init_SLang_Hook = X_init_slang;
   X_Init_Term_Hook = init_Xdisplay;
   X_Reset_Term_Hook = reset_Xdisplay;
   X_Set_Color_Hook = x_set_color;
   X_Define_Keys_Hook = x_define_xkeys;
   SLang_Interrupt = xjed_check_kbd;   
}

