/* subroutines for multi-window terminal emulation
 */

#ifdef __GNUC__
#  include <gemfast.h>
#  include <aesbind.h>
#  include <vdibind.h>
#else
#  include <obdefs.h>
#  include <gemdefs.h>
#endif
#include <osbind.h>
#include <stdio.h>
#include "wind.h"
#include "windefs.h"

extern int handle;

/* variables used by various routines
 */

long dummy;				/* dummy return variable */
extern	int	outwind, outport;	/* window selection */
extern	int	scr_x, scr_y;		/* size of the screen */
extern	int	scr_w, scr_h;
extern	int	fast;			/* flag for fast open/close */
extern	int	overstrike;
extern	int	sliders;		/* flag for sliders on new windows */
extern	int	titles;			/* flag for title bars on new windows */
int	tmp;				/* temporary for anything... */
extern	char	alert[300];		/* used for alerts */
extern	FNT	*curfont;		/* current font */
extern	MFDB	screen_mf;		/* screen descriptor */
extern	int	mouse;			/* for mouse on/off */
extern	int	audibell;		/* What happens on BEL? */
extern	int	visibell;
extern	int	toponbel;

struct	wi_str	w[MAX_WIND];

/* the program code...
 */

char *getmem(size)
register long size;
{
  char *got;

  got = (char *) Malloc(size);
#ifdef DEBUG
  printf("alloc returned %lx of %ld size\n", got, size);
#endif
  if (got == NULL)
  {
    sprintf(alert, "[1][Could not get %ld bytes][Ok]", size);
    form_alert(1, alert);
  } else
  {
    bzero(got, size);
  }
  return got;
}

#ifndef __GNUC__
bzero(ptr, size)
register char *ptr;
register long size;
{
  while (size--)
    *ptr++ = 0;
}
#endif

/* find_port maps from window handles to UW port identifiers
 */

find_port(wnd)
{
  return w[wnd].port;
}

/* find_wind maps from port ids to window handles
 */

find_wind(port)
{
int i;

  for (i=1; i<MAX_WIND; i++)
    if (w[i].port == port) return i;
  return 0;
}

/* w_open opens a window with the supplied name and port and returns a port
 * number.  If port was zero, a free port is chosen.
 */

w_open(port, name, xsiz, ysiz)
char *name;
{
  register struct wi_str *wp;
  int wdes;
  int i, cnt, wtyp;
  int tmp_x, tmp_y, tmp_w, tmp_h;

  if (port && find_wind(port)) return port;  /* this window is already open */
  if (!port)
  {
    for (i=1; i<MAX_WIND; i++)
    {
      if (!find_wind(i))
      {
        port = i;		/* a free port */
        break;
      }
    }
  }

  wtyp = (sliders ? WI_WITHSLD : 0) | (titles ? WI_NOSLD : 0);
  wind_calc(0, wtyp, 0, 0, curfont->inc_x*xsiz+2*X0,
    curfont->inc_y*ysiz+2*Y0, &dummy, &dummy, &tmp_w, &tmp_h);
  if (tmp_w>scr_w)
    tmp_w = scr_w;	/* full size <= screen size */
  tmp_x = 10*(port-1);
  if (tmp_h>scr_h)
    tmp_h = scr_h;
  tmp_y = scr_y+16*(port-1);

  wdes = wind_create(wtyp, tmp_x, tmp_y, tmp_w,
    tmp_h);
  if (wdes < 0)
  {
    form_alert(1, "[1][Sorry, GEM has|no more windows|for us...][Ok]");
    return 0;
  }
  wp = &w[wdes];
  wp->wi_w = X0*2 + curfont->inc_x*xsiz;
  wp->wi_h = Y0*2 + curfont->inc_y*ysiz;
  wp->port = port;
  if (!fast)
    graf_growbox(0, 0, 20, 10, tmp_x, tmp_y, tmp_w, tmp_h);
  wind_open(wdes, tmp_x, tmp_y, tmp_w, tmp_h);
  wind_get(wdes, WF_WORKXYWH, &wp->x, &wp->y, &wp->w, &wp->h);
  wp->fulled = 0;
  wp->used = 1;
  wp->x_off = 0;
  wp->y_off = 0;
  wp->px_off = 0;
  wp->py_off = 0;
  wp->m_off = wp->x & 15;
  wp->cur_x = X0;
  wp->cur_y = Y0;
  wp->top_y = Y0;
  wp->font = curfont;
  wp->x_chrs = xsiz;
  wp->y_chrs = ysiz;
  wp->wi_mf.wpix = 2*X0 + xsiz*curfont->inc_x;
  wp->wi_mf.hpix = 2*Y0 + ysiz*curfont->inc_y;
#ifdef	KOPY
  wp->wi_mf.wwords = ((wp->wi_mf.wpix>>5) +1) << 1;
#else
  wp->wi_mf.wwords = (wp->wi_mf.wpix>>4) +1;
#endif
  wp->wi_mf.ptr = getmem(((long)wp->wi_mf.hpix+wp->font->inc_y*MAXSCROLLED)
				*wp->wi_mf.wwords*2);
  wp->wi_mf.format = 0;
  wp->wi_mf.planes = 1;
  wp->ptr_status = LOG_NONE;
  wp->wi_style = wtyp;
  w_rename(wdes, name);
  strcpy(wp->wi_fpath, ".\\*.*");
  wp->top_age = 1;
  for (cnt = 1; cnt < MAX_WIND; cnt++)
    if (w[cnt].port != 0)
      w[cnt].top_age++;
  
  setvslide(wdes);
  sethslide(wdes);
  
  return wp->port;
}

/* w_closei removes a window but does not release its storage.
 * This is used if the window contents must be saved for later use.
 */

w_closei(wdes)
{
  int xx, yy, ww, hh;
  register struct wi_str *wp = &w[wdes];

  if (!wp->used) return;
  if (wp->wi_lfd)
    fclose(wp->wi_lfd);
  wind_get(wdes, WF_CURRXYWH, &xx, &yy, &ww, &hh);
  wind_close(wdes);
  if (!fast)
    graf_shrinkbox(0, 0, 20, 10, xx, yy, ww, hh);
  wind_delete(wdes);
}

/* w_close removes a window.  Most work is done by GEM, although w_close
 * does some cleanup functions, too.
 */

w_close(wdes)
{
  register struct wi_str *wp = &w[wdes];

  if (!wp->used) return;
  w_closei(wdes);
  Mfree(wp->wi_mf.ptr);
  bzero(wp, (long)sizeof (struct wi_str));
}

/* w_resize resizes an existing window.
 */

w_resize(wdes, xsiz, ysiz)
{
  register struct wi_str *wp1 = &w[wdes];
  struct wi_str *wp2;
  struct wi_str ws;
  static int c[8];
  int port, wind, i;

  if (!wp1->used) return;
  ws = *wp1;
  port = find_port(wdes);
  w_closei(wdes);
  bzero(wp1, (long)sizeof (struct wi_str));
  port = w_open(port, "", xsiz, ysiz);
  wind = find_wind(port);
  wp2 = &w[wind];
  c[0] = ws.m_off;
  c[1] = ws.top_y + max(0, ws.wi_mf.hpix - wp2->wi_mf.hpix);
  c[2] = c[0] + min(ws.wi_mf.wpix, wp2->wi_mf.wpix);
  c[3] = c[1] + min(ws.wi_mf.hpix, wp2->wi_mf.hpix);
  c[4] = wp2->m_off;
  c[5] = wp2->top_y;
  c[6] = c[4] + min(ws.wi_mf.wpix, wp2->wi_mf.wpix);
  c[7] = c[5] + min(ws.wi_mf.hpix, wp2->wi_mf.hpix);
  /* copy screen */
  vro_cpyfm(handle, FM_COPY, c, &ws.wi_mf, &wp2->wi_mf);
  /* copy parameters */
  wp2->inverse = ws.inverse;
  wp2->insmode = ws.insmode;
  if (wp2->font != ws.font)
  {
    wp2->cur_x = X0;
    wp2->cur_y = (wp2->y_chrs - 1) * wp2->font->inc_y + Y0;
  }
  else
  {
    wp2->cur_x = (wp2->x_chrs - 1) * wp2->font->inc_x + X0;
    if (ws.cur_x < wp2->cur_x)
      wp2->cur_x = ws.cur_x;
    wp2->cur_y = max(0, ws.cur_y - c[1]) + Y0;
  }
  wp2->state = ws.state;
  for (i=0; i<80; i++) wp2->nuname[i] = ws.nuname[i];
  for (i=0; i<80; i++) wp2->wi_fpath[i] = ws.wi_fpath[i];
  for (i=0; i<20; i++) wp2->wi_fname[i] = ws.wi_fname[i];
  wp2->nuptr = ws.nuptr;
  wp2->ptr_status = ws.ptr_status;
  wp2->wi_lfd = ws.wi_lfd;
  wp2->kerm_act = ws.kerm_act;
  w_rename(wind, ws.name);
  
  Mfree(ws.wi_mf.ptr);
  return port;
}

/* w_rename changes the title bar of a window
 */

w_rename(wdes, name)
char *name;
{
  register struct wi_str *wp = &w[wdes];

  if (name)
    strcpy(wp->name, name);
  if (wp->wi_style & NAME)
  {
    sprintf(wp->dname, " %s%s %s ", (wp->ptr_status != LOG_NONE)? "\275": "",
      wp->wi_lfd? "\237": "", wp->name);
    wind_set(wdes, WF_NAME, wp->dname + (wp->dname[1] == ' '), 0, 0);
  }
}

/* w_redraw redraws part of the screen from window contents.
 * The coordinates are screen relative.
 */

w_redraw(wdes, logic, xx, yy, ww, hh)
{
  static int c[8];
  static GRECT t1, t2;
  register struct wi_str *wp = &w[wdes];

  if (xx+ww > scr_w)
    ww = scr_w - xx;
  if (yy+hh > scr_h+scr_y)
    hh = scr_h+scr_y - yy;
  t2.g_x = xx; t2.g_y = yy;
  t2.g_w = ww; t2.g_h = hh;
  t1.g_x = wp->x; t1.g_y = wp->y;
  t1.g_w = wp->w; t1.g_h = wp->h;
  if (!rc_intersect(&t2, &t1)) return;	/* nothing to do... */
  wind_update(TRUE);
  wind_get(wdes, WF_FIRSTXYWH, &t1.g_x, &t1.g_y, &t1.g_w, &t1.g_h);
  while (t1.g_w && t1.g_h)
  {
    if (rc_intersect(&t2, &t1))
    {
      if (mouse)
      {
	/* we have to do graphics, so switch the mouse off.
	 * mouse will be switched on again in main loop.
	 * this is ugly, but it improves speed a bit...
	 */
	mouse = 0;
	graf_mouse(M_OFF, NULL);
      }
#ifdef	KCOPY
      tfbmr(wp->wi_mf.ptr, t1.g_x - wp->x + wp->x_off + wp->m_off,
	t1.g_y - wp->y + wp->y_off + wp->top_y - Y0, wp->wi_mf.wwords >> 1,
	screen_mf.ptr, t1.g_x, t1.g_y, screen_mf.wwords >> 1,
	t1.g_w, t1.g_h, logic);
#else
      c[0] = t1.g_x - wp->x + wp->x_off + wp->m_off;
      c[1] = t1.g_y - wp->y + wp->y_off + wp->top_y - Y0;
      c[2] = c[0] + t1.g_w - 1;
      c[3] = c[1] + t1.g_h - 1;
      c[4] = t1.g_x;
      c[5] = t1.g_y;
      c[6] = c[4] + t1.g_w - 1;
      c[7] = c[5] + t1.g_h - 1;
      vro_cpyfm(handle, logic, c, &wp->wi_mf, &screen_mf);
#endif
    }
    wind_get(wdes, WF_NEXTXYWH, &t1.g_x, &t1.g_y, &t1.g_w, &t1.g_h);
  }
  if (!fast && !mouse)
  {
    mouse = 1;
    graf_mouse(M_ON, NULL);
  }
  wind_update(FALSE);
}

/* w_update copies a portion of the window to the screen.  Coordinates
 * are window-relative
 */

w_update(wdes, logic, xx, yy, ww, hh)
{
  register struct wi_str *wp = &w[wdes];

  w_redraw(wdes, logic, xx + wp->x - wp->x_off, 
  		yy + wp->y - wp->y_off - wp->top_y + Y0, ww, hh);
}

/* w_move sets the window's idea of its own position on the screen
 */

w_move(wdes, xx, yy, ww, hh)
{
  register struct wi_str *wp = &w[wdes];
  int flag = 0;
  int m_w, m_h;
  int x1, x2;
  int tmp;

  wind_calc(1, wp->wi_style, xx, yy, ww, hh, &dummy, &dummy, &m_w, &m_h);
  if (tmp = (m_w-2*X0)%wp->font->inc_x)
  {
    ww -= tmp;
    m_w -= tmp;
  }
  if (tmp = (m_h-2*Y0)%wp->font->inc_y)
  {
    hh -= tmp;
    m_h -= tmp;
  }
  if (m_w>wp->wi_w) ww = ww-(m_w-wp->wi_w);
  if (m_h>wp->wi_h) hh = hh-(m_h-wp->wi_h);
  wind_set(wdes, WF_CURRXYWH, xx, yy, ww, hh);
  wind_get(wdes, WF_WORKXYWH, &wp->x, &wp->y, &wp->w, &wp->h);
  if (wp->x_off+wp->w > wp->wi_w)
  {
    register int inc_x = wp->font->inc_x;
    flag = 1;
    wp->x_off = (wp->wi_w - wp->w)/inc_x*inc_x;
  }
  if (wp->y_off+wp->h > wp->wi_h)
  {
    register int inc_y = wp->font->inc_y;
    flag = 1;
    wp->y_off = (wp->wi_h - wp->h)/inc_y*inc_y;
  }
  x1 = wp->m_off;
  x2 = (wp->x - wp->x_off) & 15;
  if (x1 != x2)
  {
#ifdef	KOPY
    int top = wp->top_y;
    int cnt = wp->wi_mf.wwords >> 1;
    cpbmr(wp->wi_mf.ptr, x1, top, cnt, wp->wi_mf.ptr, x2, top, cnt,
      wp->wi_w, wp->wi_h);
#else
  int c[8];
    c[0] = x1;
    c[1] = wp->top_y;	/* displayed part of memory form starts here */
    c[2] = x1 + wp->wi_w - 1;
    c[3] = wp->wi_h - 1 + c[1];
    c[4] = x2;
    c[5] = c[1];
    c[6] = x2 + wp->wi_w - 1;
    c[7] = c[3];
    vro_cpyfm(handle, 3, c, &wp->wi_mf, &wp->wi_mf);
#endif
    wp->m_off = x2;
  }
  if (flag)
    w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
  setvslide(wdes);
  sethslide(wdes);
}
 
/*
 * w_top makes win the top window.
 */
w_top(win)
int win;
{
  int cnt;

  wind_set(win, WF_TOP, 0, 0, 0, 0);
  outport = find_port(win);
  outwind = win;
  w[outwind].top_age = 0;
  for (cnt = 1; cnt < MAX_WIND; cnt++)
    if (find_port(cnt))	/* if window is in use */
      w[cnt].top_age++;
  printer_mark(outwind);
}

/*
 * w_bottom finds the bottom window and puts it on top
 */
w_bottom()
{
  int i;
  int highwin, highcnt;
  
  highcnt = 0;
  highwin = 1;
  for (i = 1; i < MAX_WIND; i++)
    if (w[i].top_age > highcnt)
    {
      highcnt = w[i].top_age;
      highwin = i;
    }
  if (w[highwin].used)
    w_top(highwin);
}

/*
 * w_hide puts the top window on the bottom.  The new top window is returned.
 */
int w_hide()
{
  int i, j;
  int newtop, botwin, highcnt;
  int oldtop, dum;
  
  wind_get(0, WF_TOP, &oldtop, &dum, &dum, &dum);
  if (!oldtop)
    return (0);
  j = -1;
  botwin = 0;
  for (j = -1; ; j--)
  {
    newtop = botwin;
    botwin = oldtop;
    highcnt = 0;
    for (i = 1; i < MAX_WIND; i++)
    {
      if (w[i].top_age > highcnt)
      {
        highcnt = w[i].top_age;
        botwin = i;
      }
    }
    if (botwin == oldtop)
      break;
    wind_set(botwin, WF_TOP, 0, 0, 0, 0);
    w[botwin].top_age = j; /*top_age less than untouched windows (top_age < 0)*/
  }
  for (j = 1; j < MAX_WIND; j++)
    if (find_port(j)) /* if window is in use */
      w[j].top_age += MAX_WIND + 3; /* correct top_age to reflect new order */
  return(newtop ? newtop : oldtop);
}

#define TINYX 80
#define TINYY 70
/*
 * w_shrink saves current size and location and shrinks to standard tiny size.
 * The second from the top non-shrunk window is placed on top.
 */
w_shrink(wdes)
int wdes;
{
  register struct wi_str *wp = &w[wdes];
  int curw, dummy;
  
  /*
   * Don't shrink a window that is currently shrunk
   */
  wind_get(wdes, WF_CURRXYWH, &dummy, &dummy, &curw, &dummy);
  if (curw <= TINYX)
    return;

  /*
   * Set up fulled and previous size and location window variables to tiny
   * size.  Then call w_full
   */
  wp->fulled = 1;
  wp->px = scr_x + scr_w - TINYX + 2;
  wp->py = scr_y + (wdes - 1) * (TINYY - 2);
  wp->pw = TINYX;
  wp->ph = TINYY;
  if (wp->py + 10 > scr_y + scr_h)
  {
    wp->py = wp->py - scr_h + 10;
    wp->px = scr_x + scr_w - TINYY * 2;
  }
  
  return (w_full(wdes));
}

/* w_full toggles size and location
 */
w_full(wdes)
{
  register struct wi_str *wp = &w[wdes];
  int x1, y1, w1, h1;
  int x2, y2, w2, h2;
  int full, topwin;

  full = wp->fulled;
  if (full) {
    x1 = wp->px;
    y1 = wp->py;
    w1 = wp->pw;
    h1 = wp->ph;
  } else
    wind_get(wdes, WF_FULLXYWH, &x1, &y1, &w1, &h1);

  wind_get(wdes, WF_CURRXYWH, &x2, &y2, &w2, &h2);
  wp->px = x2;
  wp->py = y2;
  wp->pw = w2;
  wp->ph = h2;
  if (!fast)
  {
    if (w2>=w1 && h2>=h1)
      graf_growbox(x2, y2, w2, h2, x1, y1, w1, h1);
    else if (w2<=w1 && h2<=h1)
      graf_shrinkbox(x1, y1, w1, h1, x2, y2, w2, h2);
  }

  x2 = wp->x_off;
  y2 = wp->y_off;
  wp->x_off = wp->px_off;
  wp->y_off = wp->py_off;
  wp->px_off = x2;
  wp->py_off = y2;

  w_move(wdes, x1, y1, w1, h1);
  w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
  wp->fulled = 1;
  topwin = wdes;
  if (h1 < TINYY + 10)
  {
    int lowcnt, i;
  
    lowcnt = 32000;
    for (i = 1; i< MAX_WIND; i++)
    {
      if (find_port(i))
      {
        wind_get(i, WF_CURRXYWH, &x2, &y2, &w2, &h2);
      
        if (w[i].top_age < lowcnt && h2 > TINYY + 10 )
        { /* find top non-tiny open window */
          lowcnt = w[i].top_age;
          topwin = i;
	}
      }
    }
    wind_set(topwin, WF_TOP, 0, 0, 0, 0);
  }
  return(topwin);
}

w_arrow(wdes, arrow)
{
  register struct wi_str *wp = &w[wdes];
  int inc_x = wp->font->inc_x;
  int inc_y = wp->font->inc_y;

  switch (arrow)
  {
  case 0:	/* page up */
    wp->y_off -= wp->h/inc_y*inc_y;
    goto y_upd;

  case 1:	/* page down */
    wp->y_off += wp->h/inc_y*inc_y;
    goto y_upd;

  case 2:	/* row up */
    wp->y_off -= inc_y;
    goto y_upd;

  case 3:	/* row down */
    wp->y_off += inc_y;
    goto y_upd;

  case 4:	/* page left */
    wp->x_off -= wp->w/inc_x*inc_x;
    goto x_upd;

  case 5:	/* page right */
    wp->x_off += wp->w/inc_x*inc_x;
    goto x_upd;

  case 6:	/* column left */
    wp->x_off -= inc_x;
    goto x_upd;

  case 7:	/* column right */
    wp->x_off += inc_x;
    goto x_upd;
  }

x_upd:
  if (wp->x_off<0) wp->x_off = 0; else
  if (wp->x_off+wp->w > wp->wi_w) wp->x_off = (wp->wi_w - wp->w)/inc_x*inc_x;
  sethslide(wdes);
  goto upd;

y_upd:
  if (wp->y_off<0) wp->y_off = 0; else
  if (wp->y_off+wp->h > wp->wi_h) wp->y_off = (wp->wi_h - wp->h)/inc_y*inc_y;
  setvslide(wdes);
  
upd:
  w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
}

w_slide(wdes, hor, val)
{
  register struct wi_str *wp = &w[wdes];

  if (hor)
  {
    tmp = wp->font->inc_x;
    wp->x_off = ((long)val*(wp->wi_w-wp->w)/1000)/tmp*tmp;
    sethslide(wdes);
  } else
  {
    tmp = wp->font->inc_y;
    wp->y_off = ((long)val*(wp->wi_h-wp->h)/1000)/tmp*tmp;
    setvslide(wdes);
  }
  w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
}

sethslide(wdes)
{
  register struct wi_str *wp = &w[wdes];

  if (wp->wi_style & HSLIDE)
  {
    tmp = (long)1000*wp->x_off/(wp->wi_w-wp->w);
    wind_set(wdes, WF_HSLIDE, tmp, 0, 0, 0);
    tmp = (long)1000*wp->w/wp->wi_w;
    wind_set(wdes, WF_HSLSIZE, tmp, 0, 0, 0);
  }
}

setvslide(wdes)
{
  register struct wi_str *wp = &w[wdes];

  if (wp->wi_style & VSLIDE)
  {
    tmp = (long)1000*wp->y_off/(wp->wi_h-wp->h);
    wind_set(wdes, WF_VSLIDE, tmp, 0, 0, 0);
    tmp = (long)1000*wp->h/wp->wi_h;
    wind_set(wdes, WF_VSLSIZE, tmp, 0, 0, 0);
  }
}

w_flash(wdes, state)
{
  register struct wi_str *wp = &w[wdes];
  static int wdes_last;
#ifdef	KOPY
  int x, y, cnt;
#else
  int t[8];
#endif

  if (wdes != wdes_last) w_flash(wdes_last, 1);
  wdes_last = wdes;
  if (!wp->used || wp->curstate == state) return;
  if (state == 2)
    wp->curstate = !wp->curstate;
  else
    wp->curstate = state;
#ifdef	KOPY
  x = wp->cur_x + wp->m_off;
  y = wp->cur_y;
  cnt = wp->wi_mf.wwords >> 1;
  tfbmr(wp->wi_mf.ptr, x, y, cnt, wp->wi_mf.ptr, x, y, cnt,
    wp->font->inc_x, wp->font->inc_y, 12);
#else
  t[0] = t[4] = wp->cur_x + wp->m_off;
  t[1] = t[5] = wp->cur_y;
  t[2] = t[6] = t[0]+wp->font->inc_x-1;
  t[3] = t[7] = t[1]+wp->font->inc_y-1;
  vro_cpyfm(handle, 12, t, &wp->wi_mf, &wp->wi_mf);
#endif
  w_update(wdes, FM_COPY, wp->cur_x, wp->cur_y, wp->font->inc_x, wp->font->inc_y);
}
  
/* w_output prints a string onto the window.  The string may
 * contain control chars and escape sequences.  It ends with \0.
 */

w_output(wdes, ptr)
char *ptr;
{
  register struct wi_str *wp = &w[wdes];
  static char ch;
  static int inc_x, cur_x;
  static int state;
  static int inc_y, cur_y;
#ifdef	KOPY
  int x, w, cnt;
#else
  static int t[8];
#endif
  static int f_x, f_y, f_mod;
  static int scrolled;	/* Number of scrolling operations delayed */
  static int xsiz, ysiz;/* Size in chars of terminal emulation for this window*/
  register unsigned char *sptr;
  register unsigned long *dptr;
  register unsigned long mask;
  register int shift;
  register unsigned long val;
  static int count;
  static char * fdata;
  static long width;
  static char * wimfptr;
  static int moffincx;
  static char * savptr;
  

#ifdef DEBUG
  printf("entered w_output\n");
#endif
  if (!wp->font) return;
  state = wp->state;
  inc_x = wp->font->inc_x;
  inc_y = wp->font->inc_y;
  xsiz = wp->x_chrs;
  ysiz = wp->y_chrs;
  f_x = cur_x = wp->cur_x;
  f_y = cur_y = wp->cur_y;
  scrolled = wp->top_y/inc_y;
  fdata = wp->font->f_data;
  width = 2 * wp->wi_mf.wwords;
  wimfptr = ((char *) (wp-> wi_mf.ptr)) - 2;
  moffincx = wp->m_off + inc_x - 1;
  f_mod = 0;
  savptr = ptr;
  
  if (wp->curstate) w_flash(wdes, 0);

  while (ch = *ptr++)
  {
    switch (state)
    {
    case S_NORMAL:
      if (ch >= ' ')
      {
	if (wp->insmode) /* open space for character */
	{
#ifdef	KOPY
	  x = cur_x + wp->m_off;
	  cnt = wp->wi_mf.wwords >> 1;
	  cpbmr(wp->wi_mf.ptr, x, cur_y, cnt,
	    wp->wi_mf.ptr, x + inc_x, cur_y, cnt,
	    (xsiz - 1) * inc_x + wp->m_off - x, inc_y);
#else
	  t[0] = cur_x + wp->m_off;
	  t[1] = t[5] = cur_y;
	  t[2] = (xsiz - 1) * inc_x + wp->m_off - 1;
	  t[3] = t[7] = cur_y + inc_y - 1;
	  t[4] = t[0] + inc_x;
	  t[6] = t[2] + inc_x;
	  vro_cpyfm(handle, 3, t, &wp->wi_mf, &wp->wi_mf);
#endif
	}
	/* paint the character */
	sptr = fdata+ch*16;
	dptr = wimfptr + cur_y*width
		+ (((moffincx + cur_x)>>4)<<1);
	shift = 15 - ((moffincx + cur_x)&15);
	if (overstrike)
	  mask = -1L;
	else
	  mask = (-1L<<(shift+inc_x)|(1<<shift)-1);
	if (wp->inverse)
	{
	  for (count = inc_y; count; count--)
	  {
	    val = ((long)(*sptr++))<<shift ^ ~mask;
	    *dptr = (*dptr&mask)|val;
	    ((char *)dptr) += width;
	  }
	} else
	{
	  for (count = inc_y; count; count--)
	  {
	    val = ((long)(*sptr++))<<shift;
	    *dptr = (*dptr&mask)|val;
	    ((char *)dptr) += width;
	  }
	}
        cur_x += inc_x;
	f_mod = 1;
	if (cur_x > inc_x * xsiz) /* autowrap */
	{
	  cur_y += inc_y;
	  if (cur_y >= wp->top_y + inc_y * ysiz) {
	    if (wp->ptr_status & LOG_TOP)
	      dump_line(wdes, 0);
	    wp->top_y += inc_y;
	    ++ scrolled;
	  }
	  if (! scrolled)
	  {
	    w_update(wdes, FM_COPY, f_x, f_y, cur_x-f_x, inc_y);
	    f_mod = 0;
	  }
	  cur_x = X0;

	  f_x = cur_x;
	  f_y = cur_y;
	}
      } else /* not printable character */
      {
	if (f_mod && !scrolled)
	{
	  if (!wp->insmode)
	    w_update(wdes, FM_COPY, f_x, f_y, cur_x - f_x, inc_y);
	  else
	    w_update(wdes, FM_COPY, f_x, f_y, xsiz * inc_x-f_x, inc_y);
	  f_mod = 0;
	}
	switch (ch)
	{
	case '\007':	/* Bell */
	  if (audibell)
	    Bconout(2, '\007');
	  if (toponbel)
	    /* put this window on top but don't make it the current window.
	     * should we set a flag so that the first input from the current
	     * window puts it back on top?
	     */
	    wind_set(wdes, WF_TOP, wdes, 0, 0, 0);
	  if (visibell)
	  {
	    w_redraw(wdes, FM_INVERT, wp->x, wp->y, wp->w, wp->h);
	    w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
	    /* Should clear flag to prevent need for next update? */
	  }
	  break;

	case '\r':	/* Carriage Return */
	  cur_x = X0;
	  break;

	case '\b':	/* Backspace */
	  if (cur_x>X0) cur_x -= inc_x;
	  break;

	case '\n':	/* Newline */
	  cur_y += inc_y;
	  if (cur_y >= inc_y * ysiz + wp->top_y)
	  {
	    if (wp->ptr_status & LOG_TOP)
	      dump_line(wdes, 0);
	    wp->top_y += inc_y;
	    ++ scrolled;
	  }
	  break;

	case '\036':	/* Home */
	  cur_x = X0;
	  cur_y = wp->top_y;
	  break;

	case '\013':	/* Cursor Up */
	  if (cur_y!=wp->top_y) {
	    cur_y -= inc_y;
	  }
	  break;

	case '\014':	/* Cursor Right */
	  if (cur_x < (xsiz - 1) * inc_x) cur_x += inc_x;
	  break;

	case '\032':	/* Clear Screen */
	  wp->inverse = wp->insmode = 0;
	  cur_x = X0;
	  cur_y = Y0;
	  wp->top_y = Y0;
	  lineerase(wp, 0, ysiz - 1 + MAXSCROLLED);
	  ++ scrolled;
	  break;

	case '\t':	/* Tab */
	  cur_x = ((cur_x/inc_x/8+1))*inc_x*8+X0;
	  break;

	case '\033':	/* ESC */
	  state = S_ESC;
	  count = 0; /* count is used for insert or delete line */
	  break;
	}
	f_x = cur_x;
	f_y = cur_y;
      }
      break;

    case S_ESC:
      if (ch >= '0' && ch <= '9') {
        count = count * 10 + ch - '0';
	break;
      }
      switch (ch)
      {
      case '*':		/* Clear Screen */
	f_x = cur_x = X0;
	wp->top_y = f_y = cur_y = Y0;
	wp->inverse = wp->insmode = 0;
	lineerase(wp, 0, ysiz - 1 + MAXSCROLLED);
	++ scrolled;
	state = S_NORMAL;
	break;

      case 'Y':		/* Clear to End of Screen */
	lineerase(wp, cur_y / inc_y + 1, ysiz - 1 + wp->top_y / inc_y);
	if (! scrolled)
	  w_update(wdes, FM_COPY, X0, cur_y + inc_y, xsiz*inc_x, ysiz*inc_y);
	/* fall through */
      case 'T':		/* Clear to End of Line */
#ifdef	KOPY
	x = cur_x + wp->m_off;
	cnt = wp->wi_mf.wwords >> 1;
	tfbmr((short *) 0, 0, 0, 0, wp->wi_mf.ptr, x, cur_y, cnt,
	  X0 + xsiz * inc_x + wp->m_off - x, inc_y, 0);
#else
	t[0] = t[4] = cur_x + wp->m_off;
	t[1] = t[5] = cur_y;
	t[2] = t[6] = X0-1 + xsiz*inc_x +wp->m_off;
	t[3] = t[7] = cur_y+inc_y-1;
	vro_cpyfm(handle, 0, t, &wp->wi_mf, &wp->wi_mf);
#endif
	if (! scrolled)
	  w_update(wdes, FM_COPY, cur_x, cur_y, xsiz * inc_x - cur_x, inc_y);
	state = S_NORMAL;
	break;

      case 'E':		/* Add Line */
      case 'R':		/* Delete Line */
        if (count == 0) {
	  count = 1;
	  /* Look ahead for contiguous insert/delete line operations */
	  while (*ptr == '\033' && ptr[1] == ch) {
	    ptr +=2;
	    count ++;
	  }
	}
	if (ch == 'E')
	  scrolldn(wp, cur_y/inc_y, ysiz-(cur_y-wp->top_y+Y0)/inc_y-count, count);
	else
	  scrollup(wp, cur_y / inc_y,
	    ysiz - (cur_y - wp->top_y + Y0)/inc_y - count, count);
	if (! scrolled)
	  w_update(wdes, FM_COPY, X0, cur_y, xsiz * inc_x,
	    ysiz * inc_y - cur_y + wp->top_y - Y0);
	state = S_NORMAL;
	break;

      case 'q':		/* Insert Mode */
	wp->insmode = 1;
	state = S_NORMAL;
	break;

      case 'r':		/* End Insert */
	wp->insmode = 0;
	state = S_NORMAL;
	break;

      case 'W':		/* Delete Character */
#ifdef	KOPY
	x = cur_x + wp->m_off;
	w = X0 + xsiz * inc_x + wp->m_off - x;
	cnt = wp->wi_mf.wwords >> 1;
	cpbmr(wp->wi_mf.ptr, x + inc_x, cur_y, cnt,
	  wp->wi_mf.ptr, x, cur_y, cnt, w, inc_y);
	tfbmr((short *) 0, 0, 0, 0,
	  wp->wi_mf.ptr, X0 + (xsiz - 1) * inc_x + wp->m_off, cur_y, cnt,
	  inc_x, inc_y, 0);
#else
	t[0] = cur_x + inc_x + wp->m_off;
	t[1] = t[5] = cur_y;
	t[2] = X0 - 1 + xsiz * inc_x + wp->m_off;
	t[3] = t[7] = cur_y+inc_y - 1;
	t[4] = t[0] - inc_x;
	t[6] = t[2] - inc_x;
	vro_cpyfm(handle, 3, t, &wp->wi_mf, &wp->wi_mf);
	t[0] = t[4] = X0 + (xsiz - 1) * inc_x + wp->m_off;
	t[2] = t[6] = t[0] + inc_x - 1;
	vro_cpyfm(handle, 0, t, &wp->wi_mf, &wp->wi_mf);
#endif
	if (! scrolled)
	  w_update(wdes, FM_COPY, cur_x, cur_y, xsiz * inc_x - (cur_x - X0),
	    inc_y);
	state = S_NORMAL;
	break;

      case '=':		/* Cursor Movement */
	state = S_ESC1;
	break;

      case 'G':		/* Switch Inverse/Normal */
	state = S_ESC2;
	break;

      case 'S':		/* Change Status Line */
	state = S_STATUS;
	wp->nuptr = 0;
	break;
	
      case '[':		/* ascii escape */
        state = S_ESCA;
	wp->nuptr = 0;
	break;

      default:		/* Unknown escape sequence */
	state = S_NORMAL;
      }
      break;

    case S_ESC1:	/* get line number */
      if (ch < ' ' || ch >= ' ' + ysiz) ch = ' ';
      f_y = cur_y = (ch-' ')*inc_y + wp->top_y;
      state = S_ESC3;
      break;

    case S_ESC3:	/* get column number */
      if (ch < ' ' || ch >= ' ' + xsiz) ch = ' ';
      f_x = cur_x = (ch-' ')*inc_x +X0;
      state = S_NORMAL;
      break;

    case S_ESC2:	/* get normal/inverse state */
      wp->inverse = ch&1;
      state = S_NORMAL;
      break;

    case S_STATUS:
      if (ch == '\r')
      {
	wp->nuname[wp->nuptr] = '\0';
	w_rename(wdes, wp->nuname);
	state = S_NORMAL;
      } else if (wp->nuptr < 72)
      {
	wp->nuname[wp->nuptr++] = ch;
      }
      break;
    case S_ESCA:
      if (ch >= '0' && ch <= '9')
        wp->nuptr = ch - '0' + 10 * wp->nuptr;
      else if (ch == 't')
      {
        if (wp-> nuptr == 18)
	{
	  char posstr[20];
	  
	  sprintf(posstr, "\033[8;%d;%dt", ysiz, xsiz);
	  proto_out(find_port(wdes), posstr, (int)strlen(posstr));
	}
	state = S_NORMAL;
      }
      else state = S_NORMAL;
      break;
    } /* end switch on state */

    if (scrolled >= MAXSCROLLED)
    {
      if (wp->top_y != Y0) {
        scrollup(wp, 0, ysiz, wp->top_y/inc_y);
	wp->top_y = Y0;
        f_y = cur_y = Y0 + (ysiz - 1) * inc_y;
      }
      w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
      scrolled = 0;
    }
  } /* end while loop for each character */

  if (wp->ptr_status & LOG_BOTOM) {
    ptr = savptr;
    while ((ch = *ptr++) && (wp->ptr_status & LOG_BOTOM))
#if 0
      if (Cprnout(ch) == 0) wp->ptr_status = LOG_NONE;
#else
    ;
#endif
  }

  if (wp->wi_lfd) {
    while (ch = *savptr++)
      putc(ch, wp->wi_lfd);
  }

  if (scrolled) {
    if (scrolled >= MAXSCROLLED) {
      if (wp->top_y != Y0) {
        scrollup(wp, 0, ysiz, wp->top_y/inc_y);
        wp->top_y = Y0;
        cur_y = Y0 + (ysiz - 1) * inc_y;
      }
    }
    w_redraw(wdes, FM_COPY, wp->x, wp->y, wp->w, wp->h);
  }
  else
    if (f_mod)
    {
      if (!wp->insmode)
        w_update(wdes, FM_COPY, f_x, f_y, cur_x - f_x, inc_y);
      else
        w_update(wdes, FM_COPY, f_x, f_y, xsiz * inc_x-f_x, inc_y);
    }
  wp->cur_x = cur_x;
  wp->cur_y = cur_y;
  wp->state = state;
#ifdef DEBUG
  printf ("calling w_flash\n");
#endif
  w_flash(wdes, 1);
}

lineerase(wp, first, last)
register struct wi_str *wp;
{
  register int *p;
  long count;
  long linespace = wp->wi_mf.wwords*wp->font->inc_y;

  p = wp->wi_mf.ptr + first*linespace + Y0*wp->wi_mf.wwords - 1;
  count = (last-first+1)*linespace;
  while (count > 7)
  {
    *(((long *)p)++) = 0;
    *(((long *)p)++) = 0;
    *(((long *)p)++) = 0;
    *(((long *)p)++) = 0;
    count -= 8;
  }
  while (--count >= 0)
    *++p = 0;
}

scrollup(wp, first, nlines, amount)
register struct wi_str *wp;
{
  register int *p1, *p2;
  register long count;
  int linespace = wp->wi_mf.wwords*wp->font->inc_y;

  p1 = wp->wi_mf.ptr + first*linespace + Y0*wp->wi_mf.wwords;
  p2 = p1 + linespace * amount;
  count = (long)(nlines)*linespace;
  while (count > 15)
  {
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    *(((long *)p1)++) = *(((long *)p2)++);
    count -= 16;
  }
  while (--count >= 0)
    *(p1++) = *(p2++);
  count = linespace * amount;
  while (count > 7)
  {
    *(((long *)p1)++) = 0;
    *(((long *)p1)++) = 0;
    *(((long *)p1)++) = 0;
    *(((long *)p1)++) = 0;
    count -= 8;
  }
  while (--count >= 0)
    *(p1++) = 0;
}

scrolldn(wp, first, nlines, amount)
register struct wi_str *wp;
{
  register int *p1, *p2;
  register long count;
  long linespace = wp->wi_mf.wwords*wp->font->inc_y;

  p1 = wp->wi_mf.ptr + (nlines+first+amount)*linespace + Y0*wp->wi_mf.wwords;

  p2 = p1 - linespace * amount;
  count = (long)(nlines)*linespace;
  while (count > 15)
  {
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    *--(long *)p1 = *--(long *)p2;
    count -= 16;
  }
  while (--count >= 0)
    *--p1 = *--p2;
  count = linespace * amount;
  while (count > 7)
  {
    *--(long *)p1 = 0L;
    *--(long *)p1 = 0L;
    *--(long *)p1 = 0L;
    *--(long *)p1 = 0L;
    count -= 8;
  }
  while (--count >= 0)
    *--p1 = 0;
}
