/*
* Modified by Edward Der-Hua Liu for X dosemu 
*/
/*
 *	$XConsortium: misc.c,v 1.92 92/03/13 17:02:08 gildea Exp $
 */

/*
 * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
 *
 *                         All Rights Reserved
 *
 * 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 and that
 * both that copyright notice and this permission notice appear in
 * supporting documentation, and that the name of Digital Equipment
 * Corporation not be used in advertising or publicity pertaining to
 * distribution of the software without specific, written prior permission.
 *
 *
 * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
 * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
 * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
 * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
 * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
 * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#include "ptyx.h"		/* X headers included here. */

#include <X11/Xos.h>
#include <stdio.h>
#include <setjmp.h>
#include <signal.h>
#include <ctype.h>
#include <pwd.h>
#include <errno.h>

#include <X11/Xatom.h>
#include <X11/cursorfont.h>

#include <X11/Shell.h>
#include <X11/Xmu/Error.h>
#include <X11/Xmu/SysUtil.h>
#include <X11/Xmu/WinUtil.h>

#include "data.h"
#include "error.h"
#include "menu.h"

extern jmp_buf Tekend;
extern jmp_buf VTend;

#ifndef X_NOT_STDC_ENV
#include <stdlib.h>
#else
extern char *malloc();
extern char *getenv();
#endif
#if defined(macII) && !defined(__STDC__)  /* stdlib.h fails to define these */
char *malloc();
#endif /* macII */

static void DoSpecialEnterNotify();
static void DoSpecialLeaveNotify();
int X_kbd_state=0,button_state=0;
int m_last_x, m_last_y;
int mouse_x,mouse_y;
int press_count=0;
int po_x, po_y, lpo_x, lpo_y;
int enable_mouse_drv=0;

#include "emu.h"

xevents()
{
	XEvent event,peve;
	register TScreen *screen = &term->screen;
	extern XtAppContext app_con;
	extern int in_signal, ret_imm;
	int mouse_run;

	if(screen->scroll_amt)
		FlushScroll(screen);

	mouse_run=ret_imm=0;
	do {
		if (waitingForTrackInfo)
			return;
#if	0
	{	extern int X_busy;	
		int tt=X_busy;
#if	0
		while (!wait_Xevent());  /* it doen't work. why ? */
#else
		while (XPending(screen->display)<=0);
#endif
		X_busy=1;
		XNextEvent (screen->display, &event);
		if (!tt) X_busy=0;
	}
#else
		XNextEvent (screen->display, &event);
#endif

		if (event.type ==KeyPress || event.type==KeyRelease ) {
			X_kbd_state=event.xkey.state;
#if	0
			printf("st %x %s,\n", X_kbd_state, 
				(event.type ==KeyPress)?"Press":"Release");
#endif
			if (event.type==KeyRelease) {
				extern int int9_eq, int9_fq, int9_key[];
				int ttt, count;
				char tmp[10];
				KeySym keysym;

				count=XLookupString (&event.xkey, 
					tmp, sizeof(tmp), &keysym, NULL);
#if	1
				(void)tran_scancode(keysym, event.type);
				int9_key[int9_eq]|=0x80;
#else
				if (int9_eq > int9_fq) ttt=int9_eq-1;
				else ttt=int9_eq;
				int9_key[int9_eq]=0x80|int9_key[ttt];
				int9_eq++;
#endif
			}
		} else
if (!mouse_run) {
		extern unsigned short me_mask;
		int mouse_event;
		extern int scrollbar_width;

#include "xmouse.h"
		if (event.type==ButtonPress) {
			button_state=event.xbutton.state;
			switch (event.xbutton.button) {
			case Button1:
				button_state|=Button1Mask; 
				mouse_event=MASK_LEFT_PRESS_EVENT;
				break;
			case Button2:
				button_state|=Button2Mask; 
				mouse_event=MASK_MID_PRESS_EVENT;
				break;
			case Button3:
				button_state|=Button3Mask;
				mouse_event=MASK_RIGHT_PRESS_EVENT;
			}
			button_state &= (Button1Mask|Button2Mask|Button3Mask);
			if (button_state ==
				(Button1Mask|Button2Mask|Button3Mask)) {
				enable_mouse_drv ^=1;
				if (enable_mouse_drv) puts("\7");
				return;
			}
			if (enable_mouse_drv && (me_mask &
				(MASK_LEFT_PRESS_EVENT |MASK_RIGHT_PRESS_EVENT |
				 MASK_MID_PRESS_EVENT)) ) {
/*				printf("*****b1 %x\n", button_state); */
				m_last_x=mouse_x;
				m_last_y=mouse_y;
				mouse_x=event.xbutton.x;
				mouse_y=event.xbutton.y;
				press_count++;
				mouse_run=1;
				run_mouse_event(mouse_event);
				return;
			}
		} else
		if (event.type==ButtonRelease) {
			button_state=event.xbutton.state;

			switch (event.xbutton.button) {
			case Button1:
				button_state&=~Button1Mask;
				mouse_event=MASK_LEFT_RELEASE_EVENT;
				break;
			case Button2:
				button_state&=~Button2Mask; 
				mouse_event=MASK_MID_RELEASE_EVENT;
				break;
			case Button3:
				button_state&=~Button3Mask;
				mouse_event=MASK_RIGHT_RELEASE_EVENT;
			}
			if (enable_mouse_drv && (me_mask &
				(MASK_LEFT_RELEASE_EVENT |MASK_RIGHT_RELEASE_EVENT|
				 MASK_MID_RELEASE_EVENT)) ) {
/*				printf("b2 %x\n", button_state); */
				m_last_x=mouse_x;
				m_last_y=mouse_y;
				mouse_x=event.xbutton.x;
				mouse_y=event.xbutton.y;
				mouse_run=1;
				run_mouse_event(mouse_event);
				return;
			}
		} else
		if (event.type==MotionNotify && (me_mask &MASK_MOTION_EVENT) &&
			enable_mouse_drv) {
		
			while (XPending(screen->display)) {
				XPeekEvent(screen->display, &peve);
				if (peve.type==MotionNotify) {
					XNextEvent (screen->display, &event);
				} else break;
			}
			if ((me_mask & MASK_MOTION_EVENT) &&
			event.xmotion.x > scrollbar_width ) {
			mouse_run=1;
			mouse_x=po_x=event.xmotion.x;
			mouse_y=po_y=event.xmotion.y;
/*			printf("%d  %d\n", mouse_x, mouse_y); */
			run_mouse_event(MASK_MOTION_EVENT);
			}
			return;
		}
}

xtfunc:
		/*
		 * Hack to get around problems with the toolkit throwing away
		 * eventing during the exclusive grab of the menu popup.  By
		 * looking at the event ourselves we make sure that we can
		 * do the right thing.
		 */
		if(event.type == EnterNotify &&
		   event.xcrossing.window == XtWindow(XtParent(term)) )
		  DoSpecialEnterNotify (&event.xcrossing);
		else
		if(event.type == LeaveNotify &&
		   event.xcrossing.window == XtWindow(XtParent(term)) )
		  DoSpecialLeaveNotify (&event.xcrossing);

		if (!event.xany.send_event ||
		    screen->allowSendEvents ||
		    ((event.xany.type != KeyPress) &&
		     (event.xany.type != KeyRelease) &&
		     (event.xany.type != ButtonPress) &&
		     (event.xany.type != ButtonRelease)))
		    XtDispatchEvent(&event);
	} while (!ret_imm && QLength(screen->display) > 0);
}

Cursor make_colored_cursor (cursorindex, fg, bg)
	int cursorindex;			/* index into font */
	unsigned long fg, bg;			/* pixel value */
{
	register TScreen *screen = &term->screen;
	Cursor c;
	register Display *dpy = screen->display;
	
	c = XCreateFontCursor (dpy, cursorindex);
	if (c == (Cursor) 0) return (c);

	recolor_cursor (c, fg, bg);
	return (c);
}

/* ARGSUSED */
void HandleKeyPressed(w, event, params, nparams)
    Widget w;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register TScreen *screen = &term->screen;

	Input (&term->keyboard, screen, &event->xkey, False);
}
/* ARGSUSED */
void HandleEightBitKeyPressed(w, event, params, nparams)
    Widget w;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register TScreen *screen = &term->screen;

	Input (&term->keyboard, screen, &event->xkey, True);
}

/* ARGSUSED */
void HandleStringEvent(w, event, params, nparams)
    Widget w;
    XEvent *event;
    String *params;
    Cardinal *nparams;
{
    register TScreen *screen = &term->screen;


    if (*nparams != 1) return;

    if ((*params)[0] == '0' && (*params)[1] == 'x' && (*params)[2] != '\0') {
	char c, *p, hexval[2];
	hexval[0] = hexval[1] = 0;
	for (p = *params+2; (c = *p); p++) {
	    hexval[0] *= 16;
	    if (isupper(c)) c = tolower(c);
	    if (c >= '0' && c <= '9')
		hexval[0] += c - '0';
	    else if (c >= 'a' && c <= 'f')
		hexval[0] += c - 'a' + 10;
	    else break;
	}
	if (c == '\0')
	    StringInput (screen, hexval, 1);
    }
    else {
	StringInput (screen, *params, strlen(*params));
    }
}

static void DoSpecialEnterNotify (ev)
    register XEnterWindowEvent *ev;
{
    register TScreen *screen = &term->screen;
      if (((ev->detail) != NotifyInferior) &&
	  ev->focus &&
	  !(screen->select & FOCUS)) 
	selectwindow(screen, INWINDOW);
}

/*ARGSUSED*/
void HandleEnterWindow(w, eventdata, event)
Widget w;
register XEnterWindowEvent *event;
caddr_t eventdata;
{
    /* NOP since we handled it above */
}


static void DoSpecialLeaveNotify (ev)
    register XEnterWindowEvent *ev;
{
    register TScreen *screen = &term->screen;

      if (((ev->detail) != NotifyInferior) &&
	  ev->focus &&
	  !(screen->select & FOCUS))
	unselectwindow(screen, INWINDOW);
}


/*ARGSUSED*/
void HandleLeaveWindow(w, eventdata, event)
Widget w;
register XEnterWindowEvent *event;
caddr_t eventdata;
{
    /* NOP since we handled it above */
}


/*ARGSUSED*/
void HandleFocusChange(w, eventdata, event)
Widget w;
register XFocusChangeEvent *event;
caddr_t eventdata;
{
        register TScreen *screen = &term->screen;

        if(event->type == FocusIn)
                selectwindow(screen,
			     (event->detail == NotifyPointer) ? INWINDOW :
								FOCUS);
        else {
                unselectwindow(screen,
			       (event->detail == NotifyPointer) ? INWINDOW :
								  FOCUS);
		if (screen->grabbedKbd && (event->mode == NotifyUngrab)) {
		    XBell(screen->display, 100);
		    screen->grabbedKbd = FALSE;
		}
	}
}

int dos_active=1;

selectwindow(screen, flag)
register TScreen *screen;
register int flag;
{
	if(screen->cursor_state &&
	   (screen->cursor_col != screen->cur_col ||
	    screen->cursor_row != screen->cur_row))
	    HideCursor();
	screen->select |= flag;
	if(screen->cursor_state) {
		ShowCursor(/*"selectw"*/);
	}
	dos_active=1;
}

unselectwindow(screen, flag)
register TScreen *screen;
register int flag;
{
/*    if (screen->always_highlight) return; */

	screen->select &= ~flag;
	if(screen->cursor_state &&
	   (screen->cursor_col != screen->cur_col ||
	    screen->cursor_row != screen->cur_row)) {
	      HideCursor();
	}
	if(screen->cursor_state) {
	  ShowCursor(/*"unselect"*/);
	}
	dos_active=0;
}

static long lastBellTime;	/* in milliseconds */

Bell()
{
    extern XtermWidget term;
    register TScreen *screen = &term->screen;
    struct timeval curtime;
    long now_msecs;

    /* has enough time gone by that we are allowed to ring
       the bell again? */
    if(screen->bellSuppressTime) {
	if(screen->bellInProgress) {
	    if (QLength(screen->display) > 0 ||
		GetBytesAvailable (ConnectionNumber(screen->display)) > 0)
		xevents();
	    if(screen->bellInProgress) { /* even after new events? */
		return;
	    }
	}
	gettimeofday(&curtime, NULL);
	now_msecs = 1000*curtime.tv_sec + curtime.tv_usec/1000;
	if(lastBellTime != 0  &&  now_msecs - lastBellTime >= 0  &&
	   now_msecs - lastBellTime < screen->bellSuppressTime) {
	    return;
	}
	lastBellTime = now_msecs;
    }

    if (screen->visualbell)
	VisualBell();
    else
	XBell(screen->display, 0);

    if(screen->bellSuppressTime) {
	/* now we change a property and wait for the notify event to come
	   back.  If the server is suspending operations while the bell
	   is being emitted (problematic for audio bell), this lets us
	   know when the previous bell has finished */
	Widget w = screen->TekEmu ? (Widget) tekWidget : (Widget) term;
	XChangeProperty(XtDisplay(w), XtWindow(w),
			XA_NOTICE, XA_NOTICE, 8, PropModeAppend, NULL, 0);
	screen->bellInProgress = TRUE;
    }
}


VisualBell()
{
    extern XtermWidget term;
    register TScreen *screen = &term->screen;
    register Pixel xorPixel = screen->foreground ^ term->core.background_pixel;
    XGCValues gcval;
    GC visualGC;

    gcval.function = GXxor;
    gcval.foreground = xorPixel;
    visualGC = XtGetGC((Widget)term, GCFunction+GCForeground, &gcval);
    {
	XFillRectangle(
		       screen->display,
		       VWindow(screen), 
		       visualGC,
		       0, 0,
		       (unsigned) FullWidth(screen),
		       (unsigned) FullHeight(screen));
	XFlush(screen->display);
	XFillRectangle(
		       screen->display,
		       VWindow(screen), 
		       visualGC,
		       0, 0,
		       (unsigned) FullWidth(screen),
		       (unsigned) FullHeight(screen));
    }
}

/* ARGSUSED */
void HandleBellPropertyChange(w, data, ev, more)
    Widget w;
    XtPointer data;
    XEvent *ev;
    Boolean *more;
{
    register TScreen *screen = &term->screen;

    if (ev->xproperty.atom == XA_NOTICE) {
	screen->bellInProgress = FALSE;
    }
}

Redraw()
{
	extern XtermWidget term;
	register TScreen *screen = &term->screen;
	XExposeEvent event;

	event.type = Expose;
	event.display = screen->display;
	event.x = 0;
	event.y = 0;
	event.count = 0; 
	
	if(VWindow(screen)) {
	        event.window = VWindow(screen);
		event.width = term->core.width;
		event.height = term->core.height;
		(*term->core.widget_class->core_class.expose)((Widget)term, (XEvent *)&event, NULL);
		if(screen->scrollbar) 
			(*screen->scrollWidget->core.widget_class->core_class.expose)(screen->scrollWidget, (XEvent *)&event, NULL);
		}

}

static ChangeGroup(attribute, value)
     String attribute;
     XtArgVal value;
{
	extern Widget toplevel;
	Arg args[1];

	XtSetArg( args[0], attribute, value );
	XtSetValues( toplevel, args, 1 );
}

#ifndef DEBUG
/* ARGSUSED */
#endif
Panic(s, a)
char	*s;
int a;
{
}

char *SysErrorMsg (n)
    int n;
{
    extern char *sys_errlist[];
    extern int sys_nerr;

    return ((n >= 0 && n < sys_nerr) ? sys_errlist[n] : "unknown error");
}


SysError (i)
int i;
{
	int oerrno;

	oerrno = errno;
	/* perror(3) write(2)s to file descriptor 2 */
	fprintf (stderr, "%s: Error %d, errno %d: ", xterm_name, i, oerrno);
	fprintf (stderr, "%s\n", SysErrorMsg (oerrno));
	Cleanup(i);
}

Error (i)
int i;
{
	fprintf (stderr, "%s: Error %d\n", xterm_name, i);
	Cleanup(i);
}


/*
 * cleanup by sending SIGHUP to client processes
 */
Cleanup (code)
int code;
{
	Exit (code);
}

/*
 * returns a pointer to the first occurrence of s2 in s1,
 * or NULL if there are none.
 */
char *strindex (s1, s2)
register char	*s1, *s2;
{
	register char	*s3;
	int s2len = strlen (s2);

	while ((s3=index(s1, *s2)) != NULL) {
		if (strncmp(s3, s2, s2len) == 0)
			return (s3);
		s1 = ++s3;
	}
	return (NULL);
}

/*ARGSUSED*/
xerror(d, ev)
Display *d;
register XErrorEvent *ev;
{
    fprintf (stderr, "%s:  warning, error event receieved:\n", xterm_name);
    (void) XmuPrintDefaultErrorMessage (d, ev, stderr);
    Exit (ERROR_XERROR);
}

/*ARGSUSED*/
xioerror(dpy)
Display *dpy;
{
    (void) fprintf (stderr, 
		    "Fatal IO error %d (%s) or KillClient on X server \"%s\"\r\n",
		    errno, SysErrorMsg (errno),
		    DisplayString (dpy));

     Exit(0);
}


static void withdraw_window (dpy, w, scr)
    Display *dpy;
    Window w;
    int scr;
{
    (void) XmuUpdateMapHints (dpy, w, NULL);
    XWithdrawWindow (dpy, w, scr);
    return;
}


void set_vt_visibility (on)
    Boolean on;
{
    register TScreen *screen = &term->screen;

    if (on) {
	if (!screen->Vshow && term) {
	    VTInit ();
	    XtMapWidget (term->core.parent);
	    screen->Vshow = TRUE;
	}
    } else {
	if (screen->Vshow && term) {
	    withdraw_window (XtDisplay (term), 
			     XtWindow(XtParent(term)),
			     XScreenNumberOfScreen(XtScreen(term)));
	    screen->Vshow = FALSE;
	}
    }
    set_vthide_sensitivity();
#if	0
    set_tekhide_sensitivity();
    update_vttekmode();
    update_tekshow();
    update_vtshow();
#endif
    return;
}
				
extern Atom wm_delete_window;	/* for ICCCM delete window */

void end_tek_mode ()
{
}

void end_vt_mode ()
{
}

void switch_modes (tovt)
    Bool tovt;				/* if true, then become vt mode */
{
}

void hide_vt_window ()
{
}
