/* grOS2_1.c -
 *
 *     ********************************************************************* 
 *     * Copyright (C) 1985, 1990 Regents of the University of California. * 
 *     * Permission to use, copy, modify, and distribute this              * 
 *     * software and its documentation for any purpose and without        * 
 *     * fee is hereby granted, provided that the above copyright          * 
 *     * notice appear in all copies.  The University of California        * 
 *     * makes no representations about the suitability of this            * 
 *     * software for any purpose.  It is provided "as is" without         * 
 *     * express or implied warranty.  Export of this software outside     * 
 *     * of the United States of America may require an export license.    * 
 *     *********************************************************************
 *
 * This file contains primitive functions to manipulate an X window system
 * Included here are initialization and closing
 * functions, and several utility routines used by the other X
 * modules.
 */
#define HIRES
/* #define HIRESDB */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <conio.h>
#define INCL_DOSPROCESS
#define INCL_DOSSEMAPHORES
#define INCL_DOSDATETIME
#define INCL_DOSNMPIPES
#define INCL_DOSQUEUES
#include <os2.h>
#include <io.h>
#include <fcntl.h>
#include <conio.h>
#include <sys/stat.h>
#include <process.h>
#define SIGKILL SIGTERM
#ifdef	SYSV
#include <string.h>
#else
#include <strings.h>
#endif
#include <sys/types.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <signal.h>

#include "misc/magic.h"
#include "misc/magsgtty.h"
#include "utils/geometry.h"
#include "graphics/glyphs.h"
#include "graphics/graphics.h"
#include "windows/windows.h"
#include "graphics/graphicsInt.h"
#include "textio/textio.h"
#include "textio/txcommands.h"
#include "signals/signals.h"
#include "utils/utils.h"
#include "utils/hash.h"
#include "grX11Int.h"
#include "misc/paths.h"

Display *grXdpy;
int	grXscrn;
GR_CURRENT grCurrent= {0,0,0,0,0,0};
GC grGCFill, grGCText, grGCDraw, grGCCopy, grGCGlyph, grGCStipple;
#ifdef HIRES
extern Visual        *grVisual;
extern unsigned int  grClass;
#endif  /* HIRES */
Void grXWStdin(int , ClientData );
int grXuserPC = 0;
char grXdepth[8];
static Atom wm_delete_window;	/* lbl close window patch */

Pixmap grX11Stipples[GR_NUM_STIPPLES];
HashTable	grX11WindowTable;
/* locals */

int pipeRead, pipeWrite;

typedef struct {
    char dashlist[8];
    int  dlen;
} LineStyle;

static LineStyle LineStyleTab[256];

int Xhelper;
HEV hevXhandle;
ULONG pPostcnt;

#define grMagicToXs(n) (DisplayHeight(grXdpy,grXscrn)-(n))
#define grXsToMagic(n) (DisplayHeight(grXdpy,grXscrn)-(n))

/* This is kind of a long story, and very kludgy, but the following
 * things need to be defined as externals because of the way lint
 * libraries are made by taking this module and changing all procedures
 * names "Xxxx" to "Grxxx".  The change is only done at the declaration
 * of the procedure, so we need these declarations to handle uses
 * of those names, which don't get modified.  Check out the Makefile
 * for details on this.
 */
extern Void GrX11Close(), GrX11Flush();
extern Void GrX11Init()
extern bool GrX11Create(MagWindow *);
extern Void GrX11Delete(MagWindow *),GrX11Configure(MagWindow *);
extern Void GrX11Raise(MagWindow *),GrX11Lower(MagWindow *);
extern Void GrX11Lock(MagWindow *, bool),GrX11Unlock(MagWindow *);
extern Void GrX11IconUpdate(MagWindow *, char *);

/*---------------------------------------------------------
 * grxSetWMandC:
 *	This is a local routine that resets the value of the current
 *	write mask and color, if necessary.
 *
 * Results:	None.
 *
 * Side Effects:    None.
 *
 * Errors:		None.
 *---------------------------------------------------------
 */

Void
grx11SetWMandC (mask, c)
    int mask;			/* New value for write mask */
    int c;			/* New value for current color */
{
    static int oldC = -1;
    static int oldM = -1;

    c = grPixels[c];
#ifdef HIRES
    if(grCurrent.depth <= 8) {
      mask = grPlanes[mask];
      if (mask == -65) mask = AllPlanes;
    }
    else {
      mask = AllPlanes;
    }
#else
    mask = grPlanes[mask];
    if (mask == -65) mask = AllPlanes;
#endif  /* HIRES */
    if (oldC == c && oldM == mask) return;

    GR_X_FLUSH_BATCH();
    XSetPlaneMask(grXdpy,grGCFill,mask);
    XSetPlaneMask(grXdpy,grGCDraw,mask);
    XSetPlaneMask(grXdpy,grGCText,mask);
    XSetForeground(grXdpy,grGCFill,c);
    XSetForeground(grXdpy,grGCDraw,c);
    XSetForeground(grXdpy,grGCText,c);
    oldC = c;
    oldM = mask;
}


/*---------------------------------------------------------
 * grxSetLineStyle:
 *	This local routine sets the current line style.
 *
 * Results:	None.
 *
 * Side Effects:
 *	A new line style is output to the display.
 *
 *---------------------------------------------------------
 */

Void
grx11SetLineStyle (style)
    int style;			/* New stipple pattern for lines. */
{
    static int oldStyle = -1;
    LineStyle *linestyle;
    int xstyle;

    style &= 0xFF;
    if (style == oldStyle) return;
    oldStyle = style;
    GR_X_FLUSH_BATCH();

    switch (style) {
    case 0xFF:
    case 0x00:
	xstyle = LineSolid;
	break;
    default:
	xstyle = LineOnOffDash;
	linestyle = &LineStyleTab[style];
	if (linestyle->dlen == 0) {

	    /* translate style to an X11 dashlist */

	    char *e;
	    int cnt,offset,cur,new,curnew,i,match;

	    e = linestyle->dashlist;
	    cnt = 0;
	    offset = 1;
	    cur = 0;
	    for (i = 7; i >= 0; i--) {
		new = (style >> i) & 1;
		curnew = (cur << 1) | new;
		switch (curnew) {
		case 0:
		case 3:
		    cnt++;
		    break;
		case 1:
		    if (cnt > 0) *e++ = cnt; else offset = 0;
		    cnt = 1;
		    break;
		case 2:
		    *e++ = cnt;
		    cnt = 1;
		    break;
		}
		cur = new;
	    }
	    *e++ = cnt;
	    cnt = e - linestyle->dashlist;
	    if (offset) {
		cur = e[0];
		for (i = 0; i < cnt-1; i++) e[i] = e[i+1];
		e[cnt-1] = cur;
	    }
	    match = 1;
	    do {
		if (cnt % 2) break;
		for (i = 0; i < cnt/2; i++) {
		    if (e[i] != e[cnt/2 + i]) match = 0;
		}
		if (match == 0) break;
		cnt = cnt/2;
	    } while (match);
	    linestyle->dlen = cnt;
	}
	XSetDashes(grXdpy, grGCDraw, 0,
		   linestyle->dashlist, linestyle->dlen);
    }
#ifdef	OLD_XFREE
    /* Bypass bug in XFree-2.x server */
    XSetLineAttributes(grXdpy, grGCDraw, 1,
		       xstyle, CapNotLast, JoinMiter);
#else
    XSetLineAttributes(grXdpy, grGCDraw, 0,
		       xstyle, CapNotLast, JoinMiter);
#endif
}


/*---------------------------------------------------------
 * grxSetSPattern:
 *	xSetSPattern associates a stipple pattern with a given
 *	stipple number.  This is a local routine called from
 *	grStyle.c .
 *
 * Results:	None.
 *
 * Side Effects:    None.
 *---------------------------------------------------------
 */

Void
grx11SetSPattern (stipple, pattern)
    int stipple;			/* The stipple number, 1-15. */
    int pattern[8];			/* 8 8-bit patterns integers */
{
    Pixmap p;
    int x,y,pat;

    p = XCreatePixmap(grXdpy, XDefaultRootWindow(grXdpy), 8, 8, 1);
    if (grGCStipple == 0) {
	grGCStipple = XCreateGC(grXdpy, p, 0, 0);
    }
    for (y = 0; y < 8; y++) {
	pat = pattern[y];
	for (x = 0; x < 8; x++) {
	    XSetForeground(grXdpy, grGCStipple, pat & 1);
	    XDrawPoint(grXdpy, p, grGCStipple, x, y);
	    pat >>= 1;
	}
    }
    grX11Stipples[stipple] = p;
}


/*---------------------------------------------------------
 * grxSetStipple:
 *	This routine sets the Xs current stipple number.
 *
 * Results: None.
 *
 * Side Effects:
 *	The current clipmask in the X is set to stipple,
 *	if it wasn't that already.
 *---------------------------------------------------------
 */

Void
grx11SetStipple (stipple)
    int stipple;			/* The stipple number to be used. */
{
    static int oldStip = -1;
    if (stipple == oldStip) return;
    oldStip = stipple;
    GR_X_FLUSH_BATCH();
    if (stipple == 0 || stipple > GR_NUM_STIPPLES) {
	XSetFillStyle(grXdpy, grGCFill, FillSolid);
    } else {
	if (grX11Stipples[stipple] == 0) {
           TxPrintf("Stipple index %d not supported. Correct the dstyle file.\n",
              stipple);
           return; }
	XSetStipple(grXdpy, grGCFill, grX11Stipples[stipple]);
	XSetFillStyle(grXdpy, grGCFill, FillStippled);
    }
}


/*---------------------------------------------------------
 * GrXInit:
 *	GrXInit initializes the graphics display and clears its screen.
 *	Files must have been previously opened with GrSetDisplay();
 *
 * Results: TRUE if successful.
 *---------------------------------------------------------
 */

bool
GrX11Init ()
{

    grXdpy = XOpenDisplay(NULL); 
    if (grXdpy == NULL)
    {
    	 TxError("Couldn't open display %s; check DISPLAY variable\n",
            getenv("DISPLAY"));
	 return false;
    }
    grXscrn = XDefaultScreen(grXdpy);

    grCurrent.depth = XDefaultDepth(grXdpy, grXscrn);
    grCurrent.window = XDefaultRootWindow(grXdpy);
    if (grXuserPC > 0)
    {
    	 if (grXuserPC > grCurrent.depth)
	 {
	      TxError("You cannot specify more planes (%d) than the display has (%d).\n",grXuserPC,grCurrent.depth);
	      return false;
	 }
	 grCurrent.depth = (grXuserPC == 1)?0:grXuserPC;
    }
    else  /* default to nearest depth that we "should" have dstyles and 
    	     colormap for.
	  */
    {
         if      (grCurrent.depth < 4) grCurrent.depth = 0;
         else if (grCurrent.depth < 6) grCurrent.depth = 4;
         else if (grCurrent.depth < 7) grCurrent.depth = 6;
	 else 			       grCurrent.depth = 7;
    }
    /* lbl close-window patch */
    /*
        Prevents termination when Magic window is deleted via the
        window-manager. Same global atom (wm_delete_window) used by
        all windows
    */
    wm_delete_window = XInternAtom(grXdpy, "WM_DELETE_WINDOW", False);
    grDStyleType = grXdepth;
    if (grCurrent.depth) 
    {
    	 sprintf(grXdepth,"%dbit",grCurrent.depth);
	 grCMapType = grXdepth;
    }
    else
    {
    	 sprintf(grXdepth,"bw");
         grCMapType = NULL;
	 /* have to call GrX11SetCmap directly here; with colormap
	       set to NULL, it doesn't get called otherwise.
         */
         GrX11SetCMap((char *)NULL);
    }

    HashInit(&grX11WindowTable,8,HT_WORDKEYS);
    return grx11LoadFont();
}

/*---------------------------------------------------------
 * GrXClose:
 *
 * Results:	None.
 *
 * Side Effects:
 *---------------------------------------------------------
 */

Void
GrX11Close ()
{
    int rc;
    if (grXdpy == NULL) return;
    TxDelete1InputDevice(pipeRead);
    rc = DosClose( pipeRead);
    rc = DosClose( pipeWrite);
    rc = kill( Xhelper, 1);	/* XXX: delete from list! */
    grGCStipple = 0;
    XCloseDisplay(grXdpy);
}


/*---------------------------------------------------------
 * GrXFlush:
 * 	Flush output to display.
 *
 *	Flushing is done automatically the next time input is read,
 *	so this procedure should not be used very often.
 *
 * Results:	None.
 *
 * Side Effects:    None.
 *---------------------------------------------------------
 */

Void
GrX11Flush ()
{
   XFlush(grXdpy);
}


/*
 * ---------------------------------------------------------------------------
 *
 * grXStdin --
 *
 *      Handle the stdin device for the X driver.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      Adds events to the data queue.
 *
 * ---------------------------------------------------------------------------
 */

Void
grX11Stdin ()
{
    TxInputEvent *event;
    XEvent	xevent;
    HashEntry	*entry;
    int rc;
    ULONG BytesRead;
    struct _AVAILDATA BytesAvail;
    ULONG PipeState;

    rc = DosPeekNPipe(pipeRead, &xevent, sizeof(XEvent), &BytesRead,
                 &BytesAvail, &PipeState);
    if(BytesAvail.cbpipe < sizeof(XEvent)) {  return; }
/* printf("         BytesAvail %d\n", BytesAvail.cbpipe); */
    rc = _read(pipeRead, &xevent, sizeof(XEvent));
    if(rc < sizeof(XEvent)){printf("Pipe read length error: %d\n", rc);
         return;}
    switch (xevent.type) 
    {
	case ButtonPress:
	case ButtonRelease:
	    {
		XButtonEvent *ButtonEvent = (XButtonEvent *) &xevent;

	        event = TxNewEvent();
		switch (ButtonEvent->button) {
		case Button1:
		    event->txe_button = TX_LEFT_BUTTON;
		    break;
		case Button2:
		    event->txe_button = TX_MIDDLE_BUTTON;
		    break;
		case Button3:
		    event->txe_button = TX_RIGHT_BUTTON;
		    break;
		}
		switch(xevent.type) {
		case ButtonRelease:
		    event->txe_buttonAction = TX_BUTTON_UP;
		    break;
		case ButtonPress:
		    event->txe_buttonAction = TX_BUTTON_DOWN;
		    break;
		}

	        grCurrent.window = ButtonEvent->window;
		entry = HashLookOnly(&grX11WindowTable,grCurrent.window);
	        grCurrent.mw= (entry)?(MagWindow *)HashGetValue(entry):0;

		event->txe_p.p_x = ButtonEvent->x;
		event->txe_p.p_y = grXToMagic(ButtonEvent->y);
		event->txe_wid = grCurrent.mw->w_wid;
		TxAddEvent(event);
	    }
	    break;
	case KeyPress:
	    {
		XKeyPressedEvent *KeyPressedEvent = (XKeyPressedEvent *) &xevent;
		int c;

	        event = TxNewEvent();

	        grCurrent.window = KeyPressedEvent->window;
		entry = HashLookOnly(&grX11WindowTable,grCurrent.window);
	        grCurrent.mw= (entry)?(MagWindow *)HashGetValue(entry):0;

    		_read(pipeRead, &c, sizeof(int));
		if (c == (int)'\r') c = (int)'\n';
		event->txe_button = TX_CHARACTER;
		event->txe_ch = c;
		event->txe_buttonAction = TX_KEY_DOWN;
		event->txe_p.p_x = KeyPressedEvent->x;
		event->txe_p.p_y = grXToMagic(KeyPressedEvent->y);
		event->txe_wid = grCurrent.mw->w_wid;
		TxAddEvent(event);
	    } 
	    break;
	case Expose:
	    {
		    XExposeEvent *ExposeEvent = (XExposeEvent*) &xevent;
		    Rect screenRect;
		    MagWindow	*w;
		    
	            grCurrent.window = ExposeEvent->window;
		    entry = HashLookOnly(&grX11WindowTable,grCurrent.window);
	            w = (entry)?(MagWindow *)HashGetValue(entry):0;
/* The line above is defintely NOT a good idea. w == 0 causes address
   exception. Why X11 is generating an expose event for a non-existent
   window is another question... ***mdg***                             */
                    if(w == 0) {printf("Expose: w = %d.\n", w); break;}
	            grCurrent.mw=w;

		    screenRect.r_xbot = ExposeEvent->x;
            	    screenRect.r_xtop = ExposeEvent->x+ExposeEvent->width;
            	    screenRect.r_ytop = 
			 	w->w_allArea.r_ytop-ExposeEvent->y;
            	    screenRect.r_ybot = w->w_allArea.r_ytop - 
		    		(ExposeEvent->y + ExposeEvent->height);
		    SigDisableInterrupts();
                    WindAreaChanged( w, &screenRect);
                    WindUpdate();
		    SigEnableInterrupts();
            }
	    break;
	case ConfigureNotify:
	    {
		    XConfigureEvent *ConfigureEvent = (XConfigureEvent*) &xevent;
		    Rect screenRect;
		    MagWindow	*w;
		    
	            grCurrent.window = ConfigureEvent->window;
		    entry = HashLookOnly(&grX11WindowTable,grCurrent.window);
	            w = (entry)?(MagWindow *)HashGetValue(entry):0;
/* The line above is defintely NOT a good idea. w == 0 causes address
   exception. Why X11 is generating an event for a non-existent
   window is another question... ***mdg***                             */
                    if(w == 0) {printf("ConfigureNotify: w = %d.\n", w); break;}
	            grCurrent.mw=w;

		    screenRect.r_xbot = ConfigureEvent->x;
            	    screenRect.r_xtop = ConfigureEvent->x+
			 		ConfigureEvent->width;
            	    screenRect.r_ytop = grXsToMagic(ConfigureEvent->y);
            	    screenRect.r_ybot = 
			 	grXsToMagic(ConfigureEvent->y+
					    ConfigureEvent->height);
		 
/*    Configure events do not seem to require complete window repaint. */
		    SigDisableInterrupts();
		    WindReframe(w,&screenRect,FALSE,FALSE);
                    WindUpdate(w);
		    SigEnableInterrupts();
            }

	default:
	    break;

     }
}


/*---------------------------------------------------------
 * x11SetDisplay:
 *	This routine sets the appropriate parameters so that
 *	Magic will work with the X display.
 *
 *      Under Xlib, all input events (mouse and keyboard) are
 *	sent to one queue which has to be polled to discover
 *	whether there is any input or not.  To fit the Magic
 *	interrupt-driven input model, a helper process is
 *	spawned which reads and blocks on the event queue,
 *	sending SIGIO's to Magic when it detects input.  The
 *	input read in the helper process is then sent to Magic
 *	via a communication pipe.
 *
 * Results:  success / fail
 *
 * Side Effects:	Sets up the pipe.
 *---------------------------------------------------------
 */

bool
x11SetDisplay (dispType, outFileName, mouseFileName)
    char *dispType;		/* arguments not used by X */
    char *outFileName;
    char *mouseFileName;
{
    int fildes[2],fildes2[2];
    char readPipeN[60], writePipeN[60], SemName[60], x11commandl[200];
    char *tmp;
    int rc, parentID;
    ULONG BytesRead;
    struct _AVAILDATA BytesAvail;
    ULONG PipeState;
    char	*planecount;
    char *fullname;
    FILE* f;
    bool execFailed = false;

    WindPackageType = WIND_X_WINDOWS;

    grCursorType = "bw";
    
    if ((int)strlen(dispType) > 5)  /* 5 == strlen("XWIND") */
    {
    	 planecount = dispType+5;
	 grXuserPC = atoi(planecount);
    }
    
    WindScrollBarWidth = 14;
/* OS/2 setup of x11helper requires:
    Setup OS/2 style pipes.
    Start x11helper by: system() call.
    Provide arguments to x11helper so that it can open pipes at client end.
     Required arguments are: server process ID and pipe names.
*/
    strcpy(SemName, "\\SEM32\\MagicS");
    tmp = strrchr(tmpnam(NULL), '\\'); tmp++;
    strcat(SemName, tmp);
    rc = pipe(fildes, readPipeN, writePipeN);
    if (rc != 0) { printf("Pipe create failed: %d\n", rc); exit(-1); }
    parentID = getpid();
/* For production use insert /C and maybe /MIN after start below */
    sprintf(x11commandl, "start /C /MIN \"Magic X11helper\" x11helper.exe \"%d %d %d\" %s %s %s",
       fildes[0], fildes[1], parentID, readPipeN, writePipeN, SemName);
    rc = system(x11commandl);
    if ( rc != 0) {printf("system call to start x11helper failed: %d\n", rc);
                exit(-1);}
    DosCreateEventSem(SemName, &hevXhandle, 0, FALSE);
    rc = DosConnectNPipe( fildes[0]); /* Waits for DosOpen from client! */
    rc = DosConnectNPipe( fildes[1]);
    pipeRead = fildes[0];
    pipeWrite = fildes[1];
    rc = DosPeekNPipe(pipeRead, &Xhelper, sizeof(Xhelper), &BytesRead,
                 &BytesAvail, &PipeState);
    while(BytesAvail.cbpipe < sizeof(Xhelper)) {DosSleep(1L);
       rc = DosPeekNPipe(pipeRead, &Xhelper, sizeof(Xhelper), &BytesRead,
                         &BytesAvail, &PipeState); }
    rc = _read(pipeRead, (char *)&Xhelper, sizeof(Xhelper));
    printf("X11helper PID received: %d\n", Xhelper);
    TxAdd1InputDevice(pipeRead, grX11Stdin, (ClientData) NULL);

    /* Set up the procedure values in the indirection table. */

    GrLockPtr = GrX11Lock;
    GrUnlockPtr = GrX11Unlock;
    GrInitPtr = GrX11Init;
    GrClosePtr = GrX11Close;
    GrSetCMapPtr = GrX11SetCMap;

    GrEnableTabletPtr = GrX11EnableTablet;
    GrDisableTabletPtr = GrX11DisableTablet;
    GrSetCursorPtr = GrX11SetCursor;
    GrTextSizePtr = GrX11TextSize;
    GrDrawGlyphPtr = GrX11DrawGlyph;
    GrReadPixelPtr = GrX11ReadPixel;
    GrFlushPtr = GrX11Flush;

    GrCreateWindowPtr = GrX11Create;
    GrDeleteWindowPtr = GrX11Delete;
    GrConfigureWindowPtr = GrX11Configure;
    GrOverWindowPtr = GrX11Raise;
    GrUnderWindowPtr = GrX11Lower;
    GrUpdateIconPtr = GrX11IconUpdate; 

    /* local indirections */
    grSetSPatternPtr = grx11SetSPattern;
    grPutTextPtr = grx11PutText;
    grDefineCursorPtr = grx11DefineCursor;
    GrBitBltPtr = GrX11BitBlt;
    grDrawGridPtr = grx11DrawGrid;
    grDrawLinePtr = grx11DrawLine;
    grSetWMandCPtr = grx11SetWMandC;
    grFillRectPtr = grx11FillRect;
    grSetStipplePtr = grx11SetStipple;
    grSetLineStylePtr = grx11SetLineStyle;
    grSetCharSizePtr = grx11SetCharSize;
    grMaxStipples = 32;
#ifdef NONMANHATTAN
    grFillPolygonPtr = grx11FillPolygon;
#endif
    
    if (execFailed) {
	TxError("Execution failed!\n");
	return false;
    }

    TxAdd1InputDevice(fileno(stdin), grXWStdin, (ClientData) NULL);

    if(!GrX11Init()){
	return false;
    };
    GrScreenRect.r_xtop = DisplayWidth(grXdpy,grXscrn);
    GrScreenRect.r_ytop = DisplayHeight(grXdpy,grXscrn);

    return true;
}

/*
 * ----------------------------------------------------------------------------
 *
 * grXWStdin --
 *      Handle the stdin device for X window interface.
 *
 * Results:
 *      None.
 *
 * Side Effects:
 *      Adds events to the event queue.
 *
 * ----------------------------------------------------------------------------
 */
 /*ARGSUSED*/

Void
grXWStdin(fd, cdata)
    int fd;
    ClientData cdata;
{
    int ch;
    TxInputEvent *event;

    if(_kbhit()) {
        ch = _getch();
        event = TxNewEvent();
        if (ch == 0xd){ ch = 0xa; printf("\n"); fflush(stdout); }
        }
    else {  return;}
    if (ch == EOF)
	event->txe_button = TX_EOF;
    else
	event->txe_button = TX_CHARACTER;
    event->txe_ch = ch;
    event->txe_buttonAction = 0;
    event->txe_wid = WIND_NO_WINDOW;
    event->txe_p.p_x = GR_CURSOR_X;
    event->txe_p.p_y = GR_CURSOR_Y;
    TxAddEvent(event);
}

/* This separate thread scans for input -- non-blocking   */
void _Optlink Input_monitor(void *arg)
{
    XEvent	xevent;
    int         rc, pass = 0;
    ULONG       BytesRead;
    struct     _AVAILDATA BytesAvail;
    ULONG       PipeState;

   while (true) {
      DosSleep(8L);   /* time in ms */
      if (_kbhit() ) { sigOnInterrupt(); }
      if(grXdpy != NULL) {
         rc = DosPeekNPipe(pipeRead, &xevent, sizeof(XEvent), &BytesRead,
                 &BytesAvail, &PipeState);
         if(BytesAvail.cbpipe >= sizeof(XEvent)) {
/*          printf("bytes %d size %d\n", BytesAvail.cbpipe, sizeof(XEvent)); */
/*  The reason for the test below for queued input is to better handle the
    double button sequence when the button is on the borders (indicating scrolling
    or zoom). It is not good to set sigInterruptPending on the button release.  */
            if((pass >= 1) && (BytesAvail.cbpipe > sizeof(XEvent))) {
                  sigOnInterrupt(); pass = 0; } else pass++;}
         else pass = 0;
      }
   }
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrX11Create --
 *      Create a new window under the X window system.
 *	Bind X window to Magic Window w.
 *
 * Results:
 *      Window created, window ID send to Xhelper.
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */
static int firstWindow = 1;

bool
GrX11Create(w)
    MagWindow *w;
{
    Window wind;
    Window w_focus;
    int revert_to;
    XSizeHints	*xsh;
    HashEntry	*entry;
    char	*windowplace;
    char        *option = (firstWindow == 1)?"window":"newwindow";
    int k;
    XGCValues grvalues;
    int		x      = w->w_frameArea.r_xbot;
    int		y      = grMagicToXs(w->w_frameArea.r_ytop);
    unsigned int	width  = w->w_frameArea.r_xtop - w->w_frameArea.r_xbot;
    unsigned int	height = w->w_frameArea.r_ytop - w->w_frameArea.r_ybot;
    unsigned long       event_mask = SubstructureNotifyMask;
    bool                propagate = 0;
    XEvent              opn_event;
    int		sentr;
    unsigned long        attribmask = CWBackPixel | CWBorderPixel | CWColormap;
    XSetWindowAttributes grAttributes;
    int     grDepth;

    WindSeparateRedisplay(w);
    xsh = XAllocSizeHints();
    /* ASSERT(xsh!=0, "failed XAllocSizeHints"); */
    if (windowplace=XGetDefault(grXdpy,"magic",option))
    {
	 XParseGeometry(windowplace,&x,&y,
       (unsigned int *)&width,(unsigned int *)&height);
	 w->w_frameArea.r_xbot = x;
	 w->w_frameArea.r_xtop = x+width;
	 w->w_frameArea.r_ytop = grXsToMagic(y);
	 w->w_frameArea.r_ybot = grXsToMagic(y+height);
	 WindReframe(w,&(w->w_frameArea),FALSE,FALSE);
	 xsh->flags = USPosition | USSize;
    }
    else
    {
    	 xsh->flags = PPosition|PSize;
    }
#ifdef HIRES
    grAttributes.background_pixel = WhitePixel(grXdpy,grXscrn);
    grAttributes.border_pixel = BlackPixel(grXdpy,grXscrn);
    grAttributes.colormap = grXcmap;
    grDepth = grCurrent.depth;
    if(grClass == 3) grDepth = 8;  /* Needed since grCurrent.depth is reset
				     to 7 if Pseudocolor      */
#ifdef HIRESDB
    TxPrintf("x %d y %d width %d height %d depth %d class %d mask %d\n",
      x,y,width,height, grDepth, grClass, attribmask);
#endif  /* HIRESDB */
    if ( wind = XCreateWindow(grXdpy,  XDefaultRootWindow(grXdpy),
    		x, y, width, height, 0, grDepth, InputOutput, grVisual,
                attribmask, &grAttributes))
#else
    if ( wind = XCreateSimpleWindow(grXdpy, XDefaultRootWindow(grXdpy),
    		x,y,width,height,0, BlackPixel(grXdpy,grXscrn),
		    WhitePixel(grXdpy,grXscrn)))
#endif  /* HIRES */
    {
#ifdef	sun
	/* Hint's for Sun's implementation of X11 (News/X11) */
        {
	    XWMHints wmhints;
	    wmhints.flags = InputHint;
	    wmhints.input = True;
	    XSetWMHints(grXdpy, wind, &wmhints);
        }
#endif	sun

	/*
	 * Signal xhelper to poll window.
	 */
	grCurrent.window = wind; 
	/*
	 * Define window cursor and complete initialization.
	 */
	xsh->x = w->w_frameArea.r_xbot;
	xsh->y = grMagicToXs(w->w_frameArea.r_ytop);
	xsh->width = w->w_frameArea.r_xtop - w->w_frameArea.r_xbot;
	xsh->height= w->w_frameArea.r_ytop - w->w_frameArea.r_ybot;
	XSetStandardProperties(grXdpy, wind, "magic", "magic", None,
			       0, 0, xsh);
#ifndef HIRES
	XSetWindowColormap(grXdpy,grCurrent.window,grXcmap);
#endif  /* HIRES */
        XMapWindow(grXdpy, grCurrent.window);
	XSync(grXdpy,0);
	XFree(xsh);
       /*
            Prevents termination when a window is deleted via the
              window-manager
       */
       (void) XSetWMProtocols(grXdpy, grCurrent.window, &wm_delete_window, 1);


	if (firstWindow == 1)
	{
             grGCFill = XCreateGC(grXdpy, grCurrent.window, 0, 0);
             grGCDraw = XCreateGC(grXdpy, grCurrent.window, 0, 0);
             grGCText = XCreateGC(grXdpy, grCurrent.window, 0, 0);
             grGCCopy = XCreateGC(grXdpy, grCurrent.window, 0, 0);
             grGCGlyph = XCreateGC(grXdpy, grCurrent.window, 0, 0);
        }
	XSetPlaneMask(grXdpy,grGCGlyph,AllPlanes);
	grCurrent.window = wind; 
	grCurrent.mw = w;
	w->w_grdata = (ClientData) wind;
	
	entry = HashFind(&grX11WindowTable,grCurrent.window);
	HashSetValue(entry,w);

        XDefineCursor(grXdpy, grCurrent.window,grCurrent.cursor);
	GrX11IconUpdate(w,w->w_caption); 
	XSync(grXdpy,0);

        _write( pipeWrite, (char *) &wind, sizeof(Window));
        DosPostEventSem(hevXhandle);
	event_mask =  ExposureMask|SubstructureNotifyMask|StructureNotifyMask;
        opn_event.type = CreateNotify;
        if (firstWindow != 1){
	/* This may look like a kludge, but it was the only way I could find
	   to get XSendEvent to send an event that x11helper would see before the
	   window that is currently being created had been processed in x11helper. */
	   XLowerWindow(grXdpy, wind);
           XGetInputFocus(grXdpy, &w_focus, &revert_to);
           sentr = XSendEvent(grXdpy, w_focus, propagate, event_mask, &opn_event);
	}
	firstWindow++;
        DosQueryEventSem(hevXhandle, &pPostcnt);
        while (pPostcnt) {
           DosQueryEventSem(hevXhandle, &pPostcnt);
           DosSleep(10L); }
	XRaiseWindow(grXdpy, wind); 
/*  XSync(grXdpy, 0); */
/*        printf("Create call completed.\n");  */
	return 1;
    }
    else
	TxError("Could not open new X window\n");

    return 0;
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrXDelete --
 *      Destroy an X window.
 *
 * Results:
 *      Window destroyed.
 *
 *
 * ----------------------------------------------------------------------------
 */

GrX11Delete(w)
    MagWindow *w;
{
    Window xw;
    HashEntry	*entry;

     if(firstWindow == 2) return;
    xw = (Window) w->w_grdata;
    entry = HashLookOnly(&grX11WindowTable,xw);
    HashSetValue(entry,NULL);
    
    XDestroyWindow(grXdpy,xw);
    firstWindow--;
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrXConfigure --
 *      Resize/ Move an existing X window.
 *
 * Results:
 *      Window reconfigured to w->w_frameArea.
 *
 * Side Effects:
 *      None.
 *
 * ----------------------------------------------------------------------------
 */
 /*ARGSUSED*/

GrX11Configure(w)
    MagWindow *w;
{
    XMoveResizeWindow(grXdpy,(Window) w->w_grdata,
	    w->w_frameArea.r_xbot, grMagicToXs(w->w_frameArea.r_ytop),
		w->w_frameArea.r_xtop - w->w_frameArea.r_xbot,
		    w->w_frameArea.r_ytop - w->w_frameArea.r_ybot);
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrXRaise --
 *      Raise a window to the top of the screen such that nothing
 *	obscures it.
 *
 * Results:
 *      Window raised.
 *
 * Side Effects:
 *      None.
 *
 * ----------------------------------------------------------------------------
 */

GrX11Raise(w)
    MagWindow *w;
{
    XRaiseWindow(grXdpy, (Window) w->w_grdata );
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrXLower --
 *      Lower a window below all other X windows.
 *	obscures it.
 *
 * Results:
 *      Window lowered.
 *
 * Side Effects:
 *      None.
 *
 * ----------------------------------------------------------------------------
 */

GrX11Lower(w)
    MagWindow *w;
{
    XLowerWindow(grXdpy, (Window) w->w_grdata );
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrX11Lock --
 *      Lock a window and set global variables "grCurrent.window"
 *	and "grCurrent.mw" to reference the locked window.
 *
 * Results:
 *      Window locked.
 *
 * Side Effects:
 *	None.
 *
 * ----------------------------------------------------------------------------
 */

GrX11Lock(w, flag)
    MagWindow *w;
    bool flag;
{
    grSimpleLock(w, flag);
    if ( w != (MagWindow *) GR_LOCK_SCREEN )
    {
	grCurrent.mw = w;
	grCurrent.window = (Window) w->w_grdata;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrX11Unlock --
 *      Unlock a window, flushing stuff out to the display.
 *
 * Results:
 *      Window unlocked.
 *
 * Side Effects:
 *	Display update.
 *
 * ----------------------------------------------------------------------------
 */

GrX11Unlock(w)
    MagWindow *w;
{
    GR_X_FLUSH_BATCH();
    grSimpleUnlock(w);
}

/*
 *-------------------------------------------------------------------------
 *
 * GrX11IconUpdate -- updates the icon text with the window script
 *
 * Results: none
 *
 * Side Effects: changes the icon text
 *
 *-------------------------------------------------------------------------
 */
Void GrX11IconUpdate(w,text)
	MagWindow	*w;
	char		*text;

{
     Window	wind = (Window)(w->w_grdata);
     XClassHint	class;
     char	*brack;
     
     if (w->w_grdata == NULL) return;
     class.res_name = "magic";
     class.res_class = "magic";
     XSetClassHint( grXdpy, wind, &class);
     if (brack = index(text,'['))
     {
     	  brack--;
	  *brack = 0;
          XSetIconName(grXdpy,wind,text);
	  XStoreName(grXdpy,wind,text);
     	  *brack = ' ';
	  return;
     }
     if (brack = rindex(text,' ')) text = brack+1;
     XSetIconName(grXdpy,wind,text);
     XStoreName(grXdpy,wind,text);
}
