text: /* gtw_grid.c: The grid window type
          for GlkTerm, curses.h implementation of the Glk API.
      Designed by Andrew Plotkin <erkyrath@eblong.com>
      http://www.eblong.com/zarf/glk/index.html
  */
  
  #define _XOPEN_SOURCE /* wcwidth */
  #include "gtoption.h"
  #include <stdio.h>
  #include <stdlib.h>
  #include <memory.h>
  #include <wchar.h>
  #include <curses.h>
  #include "glk.h"
  #include "glkterm.h"
  #include "gtw_grid.h"
  
  /* A grid of characters. We store the window as a list of lines (see
      gtw_grid.h); within a line, just store an array of characters and
      an array of styles, the same size.
  */
  
  static void init_lines(window_textgrid_t *dwin, int beg, int end, int linewid);
  static void final_lines(window_textgrid_t *dwin);
  static void export_input_line(void *buf, int unicode, long len, wchar_t *chars);
  static void import_input_line(tgline_t *ln, int offset, void *buf, 
      int unicode, long len);
  
  /* Array of curses.h attribute values, one for each style. */
  int win_textgrid_styleattrs[style_NUMSTYLES];
  
  /* This macro sets the appropriate dirty values, when a single character
      (at px, py) is changed. */
  #define setposdirty(dwn, ll, px, py)   \
      if (dwn->dirtybeg == -1 || (py) < dwn->dirtybeg)   \
          dwn->dirtybeg = (py);   \
      if (dwn->dirtyend == -1 || (py)+1 > dwn->dirtyend)   \
          dwn->dirtyend = (py)+1;   \
      if (ll->dirtybeg == -1 || (px) < ll->dirtybeg)   \
          ll->dirtybeg = (px);   \
      if (ll->dirtyend == -1 || (px)+1 > ll->dirtyend)   \
          ll->dirtyend = (px)+1;   \
      
  
  /* lnoffset could be made inline if the compiler supports it */
  int lnoffset(tgline_t *ln, int pos)
  {
      /* N.B. ln->size only gives us protection against buffer overflow
       * since we don't have access to width, invalid pos will give an
       * invalid return value.
       */
      int x = wcwidth(ln->chars[0]);
      int i = 0;
      
      while ( x <= pos && i < ln->size ) {
          if ( ++i < ln->size )
              x += wcwidth(ln->chars[i]);
      }
      
      return i;
  }
  
  window_textgrid_t *win_textgrid_create(window_t *win)
  {
      window_textgrid_t *dwin = (window_textgrid_t *)malloc(sizeof(window_textgrid_t));
      dwin->owner = win;
      
      dwin->width = 0;
      dwin->height = 0;
      
      dwin->curx = 0;
      dwin->cury = 0;
      
      dwin->linessize = 0;
      dwin->lines = NULL;
      dwin->dirtybeg = -1;
      dwin->dirtyend = -1;
      
      dwin->inbuf = NULL;
      dwin->inunicode = FALSE;
      dwin->inorgx = 0;
      dwin->inorgy = 0;
      
      return dwin;
  }
  
  void win_textgrid_destroy(window_textgrid_t *dwin)
  {
      if (dwin->inbuf) {
          if (gli_unregister_arr) {
              char *typedesc = (dwin->inunicode ? "&+#!Iu" : "&+#!Cn");
              (*gli_unregister_arr)(dwin->inbuf, dwin->inoriglen, typedesc, dwin->inarrayrock);
          }
          dwin->inbuf = NULL;
      }
      
      dwin->owner = NULL;
      if (dwin->lines) {
          final_lines(dwin);
      }
      free(dwin);
  }
  
  void win_textgrid_rearrange(window_t *win, grect_t *box)
  {
      int ix, jx, oldval;
      int newwid, newhgt;
      window_textgrid_t *dwin = win->data;
      dwin->owner->bbox = *box;
      
      newwid = box->right - box->left;
      newhgt = box->bottom - box->top;
      
      if (dwin->lines == NULL) {
          dwin->linessize = (newhgt+1);
          dwin->lines = (tgline_t *)malloc(dwin->linessize * sizeof(tgline_t));
          if (!dwin->lines)
              return;
          init_lines(dwin, 0, dwin->linessize, newwid);
      }
      else {
          if (newhgt > dwin->linessize) {
              oldval = dwin->linessize;
              dwin->linessize = (newhgt+1) * 2;
              dwin->lines = (tgline_t *)realloc(dwin->lines, 
                  dwin->linessize * sizeof(tgline_t));
              if (!dwin->lines)
                  return;
              init_lines(dwin, oldval, dwin->linessize, newwid);
          }
          if (newhgt > dwin->height) {
              for (jx=dwin->height; jx<newhgt; jx++) {
                  tgline_t *ln = &(dwin->lines[jx]);
                  for (ix=0; ix<ln->size; ix++) {
                      ln->chars[ix] = L' ';
                      ln->attrs[ix] = style_Normal;
                  }
              }
          }
          for (jx=0; jx<newhgt; jx++) {
              tgline_t *ln = &(dwin->lines[jx]);
              if (newwid > ln->size) {
                  oldval = ln->size;
                  ln->size = (newwid+1) * 2;
                  ln->chars = (wchar_t *)realloc(ln->chars, 
                      ln->size * sizeof(wchar_t));
                  ln->attrs = (short *)realloc(ln->attrs, 
                      ln->size * sizeof(short));
                  if (!ln->chars || !ln->attrs) {
                      dwin->lines = NULL;
                      return;
                  }
                  for (ix=oldval; ix<ln->size; ix++) {
                      ln->chars[ix] = L' ';
                      ln->attrs[ix] = style_Normal;
                  }
              }
          }
      }
      
      dwin->width = newwid;
      dwin->height = newhgt;
  
      dwin->dirtybeg = 0;
      dwin->dirtyend = dwin->height;
  }
  
  static void init_lines(window_textgrid_t *dwin, int beg, int end, int linewid)
  {
      int ix, jx;
  
      for (jx=beg; jx<end; jx++) {
          tgline_t *ln = &(dwin->lines[jx]);
          ln->size = (linewid+1);
          ln->dirtybeg = -1;
          ln->dirtyend = -1;
          ln->chars = (wchar_t *)malloc(ln->size * sizeof(wchar_t));
          ln->attrs = (short *)malloc(ln->size * sizeof(short));
          if (!ln->chars || !ln->size) {
              dwin->lines = NULL;
              return;
          }
          for (ix=0; ix<ln->size; ix++) {
              ln->chars[ix] = L' ';
              ln->attrs[ix] = style_Normal;
          }
      }
  }
  
  static void final_lines(window_textgrid_t *dwin)
  {
      int jx;
      
      for (jx=0; jx<dwin->linessize; jx++) {
          tgline_t *ln = &(dwin->lines[jx]);
          if (ln->chars) {
              free(ln->chars);
              ln->chars = NULL;
          }
          if (ln->attrs) {
              free(ln->attrs);
              ln->attrs = NULL;
          }
      }
      
      free(dwin->lines);
      dwin->lines = NULL;
  }
  
  static void updatetext(window_textgrid_t *dwin, int drawall)
  {
      int ix, jx, beg;
      int orgx, orgy;
      short curattr;
      
      if (drawall) {
          dwin->dirtybeg = 0;
          dwin->dirtyend = dwin->height;
      }
      else {
          if (dwin->dirtyend > dwin->height) {
              dwin->dirtyend = dwin->height;
          }
      }
      
      if (dwin->dirtybeg == -1)
          return;
      
      orgx = dwin->owner->bbox.left;
      orgy = dwin->owner->bbox.top;
      
      for (jx=dwin->dirtybeg; jx<dwin->dirtyend; jx++) {
          tgline_t *ln = &(dwin->lines[jx]);
          if (drawall) {
              ln->dirtybeg = 0;
              ln->dirtyend = dwin->width;
          }
          else {
              if (ln->dirtyend > dwin->width) {
                  ln->dirtyend = dwin->width;
              }
          }
  
          if (ln->dirtybeg == -1)
              continue;
          
          /* draw one line. */
          move(orgy+jx, orgx+ln->dirtybeg);
          
          ix=ln->dirtybeg;
          while (ix<ln->dirtyend) {
              wchar_t *ucx;
              beg = ix;
              curattr = ln->attrs[lnoffset(ln, beg)];
              for (ix+=wcwidth(ln->chars[lnoffset(ln, ix)]); ix<ln->dirtyend && ln->attrs[lnoffset(ln, ix)] == curattr; ix+=wcwidth(ln->chars[lnoffset(ln, ix)])) { }
              attrset(win_textgrid_styleattrs[curattr]);
              ucx = ln->chars;
              local_addnwstr(ucx + lnoffset(ln, beg), lnoffset(ln, ix) - lnoffset(ln, beg));
          }
          
          ln->dirtybeg = -1;
          ln->dirtyend = -1;
      }
      
      attrset(0);
      
      dwin->dirtybeg = -1;
      dwin->dirtyend = -1;
  }
  
  void win_textgrid_redraw(window_t *win)
  {
      window_textgrid_t *dwin = win->data;
  
      if (!dwin->lines)
          return;
      
      updatetext(dwin, TRUE);
  }
  
  void win_textgrid_update(window_t *win)
  {
      window_textgrid_t *dwin = win->data;
  
      if (!dwin->lines)
          return;
      
      updatetext(dwin, FALSE);
  }
  
  void win_textgrid_putchar(window_t *win, wchar_t ch)
  {
      window_textgrid_t *dwin = win->data;
      tgline_t *ln;
      size_t ch_width = wcwidth(ch);
      int curx_offset;
      size_t target_width;
  
      
      /* Canonicalize the cursor position. That is, the cursor may have been
          left outside the window area, or may be too close to the edge to print
          the next character; wrap it if necessary. */
      if (dwin->curx < 0)
          dwin->curx = 0;
      else if (dwin->curx > 0 && dwin->curx + ch_width > dwin->width) {
          dwin->curx = 0;
          dwin->cury++;
      }
      if (dwin->cury < 0)
          dwin->cury = 0;
      else if (dwin->cury >= dwin->height)
          return; /* outside the window */
      
      if (ch == L'\n') {
          /* a newline just moves the cursor. */
          dwin->cury++;
          dwin->curx = 0;
          return;
      }
      
      ln = &(dwin->lines[dwin->cury]);
      
      /* We will use this repeatedly: */
      curx_offset = lnoffset(ln, dwin->curx);
      
      /* What if we overlap with one or more 2-glyph characters? */
      /* N.B. we are assuming 2-glyph here.  We really should handle arbitrary glyph width */
      
      /* Test for overlapping with the second half of a 2-glyph character */
      if ( dwin->curx > 0 && lnoffset(ln, dwin->curx - 1) == curx_offset ) {
          /* Shift rest of line buffer 1 cell to the right */
          /* We don't have to check for memory boundaries because the 2-glyph 
           * character we are overlapping guarantees us that we are not using
           * the entire chars[] buffer.
           */
          memmove(ln->chars + curx_offset + 2, ln->chars + curx_offset + 1, (ln->size - curx_offset - 1) * sizeof(wchar_t));
              /* obliterate the previous half-character */
          /* N.B. This effectively changes the value of lnoffset(ln, dwin->curx) */
          ln->chars[curx_offset++] = L'?';
          /* obliterate target cell, to make calculations below consistent */
          ln->chars[curx_offset] = L'?';
          setposdirty(dwin, ln, dwin->curx - 1, dwin->cury);
      }
  
      target_width = wcwidth(ln->chars[curx_offset]);
  
      /* Test for overlapping with the first half of a 2-glyph character */
      /* N.B. Because we have already dealt with any overlaps with a previous character
       * we know that we start on a character boundary
       */
      /* N.B. the memmoves below are exclusive cases to the memmove above */
      if ( target_width < ch_width ) {
          /* We can't fit this character in the needed (grid) space. */
          if ( wcwidth(ln->chars[curx_offset + 1]) > 1 ) {
              /* Next character is wide, so it will become garbage. */
              ln->chars[curx_offset + 1] = L'?';
              setposdirty(dwin, ln, dwin->curx + ch_width, dwin->cury);
          }
          else {
              /* Next character is narrow, so we'll cover it entirely. */
              memmove(ln->chars + curx_offset + 1, ln->chars + curx_offset + 2, (ln->size - curx_offset - 2) * sizeof(wchar_t));
              /* We don't need to fill in ln->chars[ln->width - 1], because it will never get printed. */
          }
      }
      else if ( target_width > ch_width ) {
          /* This character can't fill the space we are filling. */
          /* Insert a dummy cell after this character. */
          memmove(ln->chars + curx_offset + 2, ln->chars + curx_offset + 1, (ln->size - curx_offset - 2) * sizeof(wchar_t));
          /* Set next character to ? */
          ln->chars[curx_offset + 1] = L'?';
          setposdirty(dwin, ln, dwin->curx + ch_width, dwin->cury);
      }
      
      ln->chars[curx_offset] = ch;
      ln->attrs[curx_offset] = win->style;
      setposdirty(dwin, ln, dwin->curx, dwin->cury);
      if ( ch_width > 1 )
          setposdirty(dwin, ln, dwin->curx + 1, dwin->cury);
          
      dwin->curx += ch_width;
      
      /* We can leave the cursor outside the window, since it will be
          canonicalized next time a character is printed. */
  }
  
  void win_textgrid_clear(window_t *win)
  {
      int ix, jx;
      window_textgrid_t *dwin = win->data;
      
      for (jx=0; jx<dwin->height; jx++) {
          tgline_t *ln = &(dwin->lines[jx]);
          for (ix=0; ix<dwin->width; ix++) {
              ln->chars[ix] = L' ';
              ln->attrs[ix] = style_Normal;
          }
          ln->dirtybeg = 0;
          ln->dirtyend = dwin->width;
      }
  
      dwin->dirtybeg = 0;
      dwin->dirtyend = dwin->height;
      
      dwin->curx = 0;
      dwin->cury = 0;
  }
  
  void win_textgrid_move_cursor(window_t *win, int xpos, int ypos)
  {
      window_textgrid_t *dwin = win->data;
      
      /* If the values are negative, they're really huge positive numbers -- 
          remember that they were cast from glui32. So set them huge and
          let canonicalization take its course. */
      if (xpos < 0)
          xpos = 32767;
      if (ypos < 0)
          ypos = 32767;
          
      dwin->curx = xpos;
      dwin->cury = ypos;
  }
  
  void win_textgrid_place_cursor(window_t *win, int *xpos, int *ypos)
  {
      window_textgrid_t *dwin = win->data;
      
      /* Canonicalize the cursor position */
      if (dwin->curx < 0)
          dwin->curx = 0;
      else if (dwin->curx >= dwin->width) {
          dwin->curx = 0;
          dwin->cury++;
      }
      if (dwin->cury < 0)
          dwin->cury = 0;
      else if (dwin->cury >= dwin->height) {
          *xpos = dwin->width-1;
          *ypos = dwin->height-1;
          return;
      }
      
      *xpos = dwin->curx;
      *ypos = dwin->cury;
  }
  
  /* Prepare the window for line input. */
  void win_textgrid_init_line(window_t *win, void *buf, int unicode,
      int maxlen, int initlen)
  {
      window_textgrid_t *dwin = win->data;
      
      dwin->inbuf = buf;
      dwin->inunicode = unicode;
      dwin->inoriglen = maxlen;
      if (maxlen > (dwin->width - dwin->curx))
          maxlen = (dwin->width - dwin->curx);
      dwin->inmax = maxlen;
      dwin->inlen = 0;
      dwin->incurs = 0;
      dwin->inorgx = dwin->curx;
      dwin->inorgy = dwin->cury;
      dwin->intermkeys = win->terminate_line_input;
      dwin->origstyle = win->style;
      win->style = style_Input;
      
      if (initlen > maxlen)
          initlen = maxlen;
          
      if (initlen) {
          tgline_t *ln = &(dwin->lines[dwin->inorgy]);
  
          if (initlen) {
              import_input_line(ln, dwin->inorgx, dwin->inbuf, 
                  dwin->inunicode, initlen);
          }        
          
          setposdirty(dwin, ln, dwin->inorgx+0, dwin->inorgy);
          if (initlen > 1) {
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+initlen-1), dwin->inorgy);
          }
              
          dwin->incurs += initlen;
          dwin->inlen += initlen;
          dwin->curx = wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs);
          dwin->cury = dwin->inorgy;
      }
  
      if (gli_register_arr) {
          char *typedesc = (dwin->inunicode ? "&+#!Iu" : "&+#!Cn");
          dwin->inarrayrock = (*gli_register_arr)(dwin->inbuf, dwin->inoriglen, typedesc);
      }
  }
  
  /* Abort line input, storing whatever's been typed so far. */
  void win_textgrid_cancel_line(window_t *win, event_t *ev)
  {
      void *inbuf;
      int inoriglen, inmax, inunicode;
      gidispatch_rock_t inarrayrock;
      window_textgrid_t *dwin = win->data;
      tgline_t *ln = &(dwin->lines[dwin->inorgy]);
  
      if (!dwin->inbuf)
          return;
      
      inbuf = dwin->inbuf;
      inmax = dwin->inmax;
      inoriglen = dwin->inoriglen;
      inarrayrock = dwin->inarrayrock;
      inunicode = dwin->inunicode;
  
      export_input_line(inbuf, inunicode, dwin->inlen, &ln->chars[dwin->inorgx]);
  
      if (win->echostr) {
          if (!inunicode)
              gli_stream_echo_line(win->echostr, inbuf, dwin->inlen);
          else
              gli_stream_echo_line_uni(win->echostr, inbuf, dwin->inlen);
      }
  
      dwin->cury = dwin->inorgy+1;
      dwin->curx = 0;
      win->style = dwin->origstyle;
  
      ev->type = evtype_LineInput;
      ev->win = win;
      ev->val1 = dwin->inlen;
      
      win->line_request = FALSE;
      dwin->inbuf = NULL;
      dwin->inoriglen = 0;
      dwin->inmax = 0;
      dwin->inorgx = 0;
      dwin->inorgy = 0;
      dwin->intermkeys = 0;
  
      if (gli_unregister_arr) {
          char *typedesc = (inunicode ? "&+#!Iu" : "&+#!Cn");
          (*gli_unregister_arr)(inbuf, inoriglen, typedesc, inarrayrock);
      }
  }
  
  static void import_input_line(tgline_t *ln, int offset, void *buf, 
      int unicode, long len)
  {
      int ix;
  
      if (!unicode) {
          for (ix=0; ix<len; ix++) {
              char ch = ((char *)buf)[ix];
              ln->attrs[offset+ix] = style_Input;
              ln->chars[offset+ix] = UCS(ch);
          }
      }
      else {
          for (ix=0; ix<len; ix++) {
              glui32 kval = ((glui32 *)buf)[ix];
              ln->attrs[offset+ix] = style_Input;
              ln->chars[offset+ix] = kval;
          }
      }
  }
  
  /* Clone in gtw_buf.c */
  static void export_input_line(void *buf, int unicode, long len, wchar_t *chars)
  {
      int ix;
  
      if (!unicode) {
          for (ix=0; ix<len; ix++) {
              wchar_t val = chars[ix];
              glui32 kval = gli_input_from_native(val);
              ((char *)buf)[ix] = Lat(kval);
          }
      }
      else {
          for (ix=0; ix<len; ix++) {
              wchar_t val = chars[ix];
              glui32 kval = gli_input_from_native(val);
              ((glui32 *)buf)[ix] = kval;
          }
      }
  }
  
  /* Keybinding functions. */
  
  /* Any key, during character input. Ends character input. */
  void gcmd_grid_accept_key(window_t *win, glui32 arg)
  {
      win->char_request = FALSE; 
      arg = gli_input_from_native(arg);
      gli_event_store(evtype_CharInput, win, arg, 0);
  }
  
  /* Return or enter, during line input. Ends line input.
     Special terminator keys also land here (the special key value
     will be in arg). */
  void gcmd_grid_accept_line(window_t *win, glui32 arg)
  {
      void *inbuf;
      int inoriglen, inmax, inunicode;
      glui32 termkey = arg;
      gidispatch_rock_t inarrayrock;
      window_textgrid_t *dwin = win->data;
      tgline_t *ln = &(dwin->lines[dwin->inorgy]);
      
      if (!dwin->inbuf)
          return;
      
      inbuf = dwin->inbuf;
      inmax = dwin->inmax;
      inoriglen = dwin->inoriglen;
      inarrayrock = dwin->inarrayrock;
      inunicode = dwin->inunicode;
  
      export_input_line(inbuf, inunicode, dwin->inlen, &ln->chars[dwin->inorgx]);
  
      if (win->echostr) {
          if (!inunicode)
              gli_stream_echo_line(win->echostr, inbuf, dwin->inlen);
          else
              gli_stream_echo_line_uni(win->echostr, inbuf, dwin->inlen);
      }
  
      dwin->cury = dwin->inorgy+1;
      dwin->curx = 0;
      win->style = dwin->origstyle;
  
      gli_event_store(evtype_LineInput, win, dwin->inlen, termkey);
      win->line_request = FALSE;
      dwin->inbuf = NULL;
      dwin->inoriglen = 0;
      dwin->inmax = 0;
      dwin->inorgx = 0;
      dwin->inorgy = 0;
      dwin->intermkeys = 0;
  
      if (gli_unregister_arr) {
          char *typedesc = (inunicode ? "&+#!Iu" : "&+#!Cn");
          (*gli_unregister_arr)(inbuf, inoriglen, typedesc, inarrayrock);
      }
  }
  
  /* Any regular key, during line input. */
  void gcmd_grid_insert_key(window_t *win, glui32 arg)
  {
      int ix;
      window_textgrid_t *dwin = win->data;
      tgline_t *ln = &(dwin->lines[dwin->inorgy]);
      
      if (!dwin->inbuf)
          return;
      if (dwin->inlen >= dwin->inmax)
          return;
      
      /* N.B. incurs is a buffer offset. */
      for (ix=dwin->inlen; ix>dwin->incurs; ix--) 
          ln->chars[lnoffset(ln, dwin->inorgx)+ix] = ln->chars[lnoffset(ln, dwin->inorgx)+ix-1];
      ln->attrs[lnoffset(ln, dwin->inorgx)+dwin->inlen] = style_Input;
      ln->chars[lnoffset(ln, dwin->inorgx)+dwin->incurs] = glui32_to_wchar(arg);
      
      setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs), dwin->inorgy);
      if (dwin->incurs != dwin->inlen) {
          setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->inlen), dwin->inorgy);
      }
      
      dwin->incurs++;
      dwin->inlen++;
      dwin->curx = wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs);
      dwin->cury = dwin->inorgy;
      
      updatetext(dwin, FALSE);
  }
  
  /* Delete keys, during line input. */
  void gcmd_grid_delete(window_t *win, glui32 arg)
  {
      int ix;
      window_textgrid_t *dwin = win->data;
      tgline_t *ln = &(dwin->lines[dwin->inorgy]);
      
      if (!dwin->inbuf)
          return;
      
      if (dwin->inlen <= 0)
          return;
                  
      switch (arg) {
          case gcmd_Delete:
              if (dwin->incurs <= 0)
                  return;
              for (ix=dwin->incurs; ix<dwin->inlen; ix++) 
                  ln->chars[lnoffset(ln, dwin->inorgx)+ix-1] = ln->chars[lnoffset(ln, dwin->inorgx)+ix];
              ln->chars[lnoffset(ln, dwin->inorgx)+dwin->inlen-1] = L' ';
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs-1), dwin->inorgy);
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->inlen-1), dwin->inorgy);
              dwin->incurs--;
              dwin->inlen--;
              break;
          case gcmd_DeleteNext:
              if (dwin->incurs >= dwin->inlen)
                  return;
              for (ix=dwin->incurs; ix<dwin->inlen-1; ix++) 
                  ln->chars[lnoffset(ln, dwin->inorgx)+ix] = ln->chars[lnoffset(ln, dwin->inorgx)+ix+1];
              ln->chars[dwin->inorgx+dwin->inlen-1] = L' ';
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs), dwin->inorgy);
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->inlen-1), dwin->inorgy);
              dwin->inlen--;
              break;
          case gcmd_KillInput:
              for (ix=0; ix<dwin->inlen; ix++) 
                  ln->chars[lnoffset(ln, dwin->inorgx)+ix] = L' ';
              setposdirty(dwin, ln, dwin->inorgx+0, dwin->inorgy);
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->inlen-1), dwin->inorgy);
              dwin->inlen = 0;
              dwin->incurs = 0;
              break;
          case gcmd_KillLine:
              if (dwin->incurs >= dwin->inlen)
                  return;
              for (ix=dwin->incurs; ix<dwin->inlen; ix++) 
                  ln->chars[lnoffset(ln, dwin->inorgx)+ix] = L' ';
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs), dwin->inorgy);
              setposdirty(dwin, ln, wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->inlen-1), dwin->inorgy);
              dwin->inlen = dwin->incurs;
              break;
      }
  
      dwin->curx = wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs);
      dwin->cury = dwin->inorgy;
      
      updatetext(dwin, FALSE);
  }
  
  /* Cursor movement keys, during line input. */
  void gcmd_grid_move_cursor(window_t *win, glui32 arg)
  {
      window_textgrid_t *dwin = win->data;
      tgline_t *ln = &(dwin->lines[dwin->inorgy]);
      
      if (!dwin->inbuf)
          return;
  
      switch (arg) {
          case gcmd_Left:
              if (dwin->incurs <= 0)
                  return;
              dwin->incurs--;
              break;
          case gcmd_Right:
              if (dwin->incurs >= dwin->inlen)
                  return;
              dwin->incurs++;
              break;
          case gcmd_LeftEnd:
              if (dwin->incurs <= 0)
                  return;
              dwin->incurs = 0;
              break;
          case gcmd_RightEnd:
              if (dwin->incurs >= dwin->inlen)
                  return;
              dwin->incurs = dwin->inlen;
              break;
      }
  
      dwin->curx = wcswidth(ln->chars, lnoffset(ln, dwin->inorgx)+dwin->incurs);
      dwin->cury = dwin->inorgy;
      
  }
  