/*
 * rmswin.ri - Microsoft Windows-specific graphics interface code.
 *
 * Todo:
 *   geticonpos
 *   seticonimage
 *   free_mutable
 *   freecolor
 *
 * Untested:
 *   toggle_fgbg
 *   rebind
 *   geticonic
 *   getimstr
 *   getfntnam
 *   dumpimage
 *   getpointername
 *
 * Blown off:
 *   getvisual
 *   getdefault
 */
#ifdef Graphics

void wfreersc();
int alc_rgb(wbp w, SysColor rgb);
/*
 * check_and_get_msg retreives the next message in *pMsg;
 * returns 1 if regular message was retreived, 0 if quit message,
 * -1 if there was an error.
 */
static int check_and_get_msg( MSG *pMsg );
int numRealized;

#ifndef min
   #define min(x,y) (((x) < (y))?(x):(y))
   #define max(x,y) (((x) > (y))?(x):(y))
#endif					/* min */
#define PALCLR(c) (c | 0x2000000L)

int winInitialized = 0;
int BORDHEIGHT;
int BORDWIDTH;
/*
 * check for double-byte character set versions of Windows
 */
CPINFO cpinfo;
int MAXBYTESPERCHAR;

wclrp scp;
HPALETTE palette;
int numColors = 0;

char szAppName[] = "Icon";

/*
 * pattern symbols
 */
stringint siPatternSyms[] = {
  {0,           16},
  { "black",     0},
  { "checkers",  12},
  { "darkgray",  2},
  { "diagonal",  8},
  { "grains",    13},
  { "gray",      3},
  { "grid",      10},
  { "horizontal",9},
  { "lightgray", 4},
  { "scales",    14},
  { "trellis",   11},
  { "vertical",  7},
  { "verydark",  1},
  { "verylight", 5},
  { "waves",     15},
  { "white",     6},
};

/*
 * draw ops
 */
stringint drawops[] = {
   { 0, 16},
   {"and",              R2_MASKPEN},
   {"andInverted",      R2_MASKPENNOT},
   {"andReverse",       R2_MASKNOTPEN},
   {"clear",            R2_BLACK},
   {"copy",             R2_COPYPEN},
   {"copyInverted",     R2_NOTCOPYPEN},
   {"equiv",            R2_NOTXORPEN},
   {"invert",           R2_NOT},
   {"nand",             R2_NOTMASKPEN},
   {"noop",             R2_NOP},
   {"nor",              R2_NOTMERGEPEN},
   {"or",               R2_MERGEPEN},
   {"orInverted",       R2_MERGEPENNOT},
   {"orReverse",        R2_MERGENOTPEN},
   {"set",              R2_WHITE},
   {"xor",              R2_XORPEN},
};

/*
 * line types
 */
stringint siLineTypes[] = {
  {0, 6},
  {"dashdotted",        PS_DASHDOT},
  {"dashdotdotted",     PS_DASHDOTDOT},
  {"dashed",            PS_DOT},
  {"longdashed",        PS_DASH},
  {"solid",             PS_SOLID},
  {"striped",           PS_DOT}
};

HINSTANCE mswinInstance;
int ncmdShow;

int FoundIt, FoundNew;
HWND NewWin;
char *lookingfor;
struct WNDlist {
   HWND w;
   struct WNDlist *next;
   } * wlhead;

struct WNDlist *wlinsert(HWND w)
{
   struct WNDlist *x = malloc(sizeof (struct WNDlist));
   x->w = w;
   x->next = wlhead;
   wlhead = x;
}

int wlsearch(HWND w)
{
   struct WNDlist *x;
   for(x=wlhead;x;x=x->next) if (x->w == w) return 1;
   return 0;
}

void wlfree()
{
   struct WNDlist *x = wlhead;
   while (wlhead) {
      x = wlhead->next;
      free(wlhead);
      wlhead = x;
      }
}

BOOL_CALLBACK myenumproc(HWND w, LPARAM l)
{
   wlinsert(w);
   return 1;
}

BOOL_CALLBACK myenumproc2(HWND w, LPARAM l)
{
   if (!wlsearch(w)) {
      FoundNew++;
      NewWin = w;
      }
   return 1;
}

char *strcasestr(const char *haystack, const char *needle)
{
   int len = strlen(needle);
   while (*haystack) {
      if (strncasecmp(haystack, needle, len) == 0) return (char *)haystack;
      haystack++;
      }
   return 0;
}

BOOL_CALLBACK myenumproc3(HWND w, LPARAM l)
{
   char s[64], s2[64];
   GetWindowText(w, s2, 63);
   /*
    * Conditions to find a window:
    * 1) wasn't in the list of windows already present when we launched.
    * 2) either contains the argv[0] program name, or
    *    was first window to appear after we called WinExec().
    */
   if (!wlsearch(w)) {
      FoundNew++;
      if ((strcasestr(s2, lookingfor) != NULL) || (NewWin && (NewWin == w))) {
         FoundIt++;
         }
      }
   return 1;
}

char *lookcmdname(char *buf, char *s)
{
  char *t = buf;
  while (*s) {
    *t++ = *s;
    if (*s == '\\') t = buf;
    s++;
    }
  *t++ = '\0';
  s = buf;
  while (*s) {
    if (*s == '.') *s = '\0';
    s++;
    }
  return buf;
}


/*
 * wopen
 */
FILE *wopen(char *name, struct b_list *lp, dptr attr, int n, int *err_idx)
   {
   wbp w;
   wsp ws;
   wcp wc;
   struct imgdata *imd;
   char answer[256];
   int i, r;
   tended struct b_list *tlp;
   tended struct descrip attrrslt;
   HDC hdc, hdc2, hdc3;
   TEXTMETRIC	metrics;
   LOGPALETTE logpal[4]; /* really 1 + space for an extra palette entry */
   HBRUSH brush;
   HBITMAP oldpix, oldpix2;
   HFONT oldfont;

   if (! winInitialized++) {
      BORDWIDTH = FRAMEWIDTH * 2;
      BORDHEIGHT = TITLEHEIGHT + FRAMEHEIGHT * 2;
      GetCPInfo(CP_ACP, &cpinfo);
      MAXBYTESPERCHAR = cpinfo.MaxCharSize;
      }

   tlp = lp;

   /*
    * allocate a binding, a window state, and a context
    */
   Protect(w = alc_wbinding(), return NULL);
   Protect(w->window = alc_winstate(), { free_binding(w); return NULL; });
   Protect(w->context = alc_context(w), { free_binding(w); return NULL; });
   ws = w->window;
   ws->listp.dword = D_List;
   BlkLoc(ws->listp) = (union block *)lp;
   ws->width = ws->height = 0;
   wc = w->context;

   /*
    * process the passed in attributes - by calling wattrib
    */
   for(i = 0; i < n; i++)
      switch (wattrib(w, StrLoc(attr[i]), StrLen(attr[i]), &attrrslt,
			answer)) {
      case Failed:
	 wclose(w);
	 return NULL;
      case Error:
	 /* think of something to do here */
	 break;
	 }

   /*
    * set the title, defaulting to the "filename" supplied to open()
    */
   if (ws->windowlabel == NULL) ws->windowlabel = salloc(name);
   if (ws->iconlabel == NULL) ws->iconlabel = salloc(name);

   if (ws->posx < 0) ws->posx = 0;
   if (ws->posy < 0) ws->posy = 0;

   /*
    * create the window
    */
   ws->iconwin = CreateWindow( "iconx", ws->windowlabel, WS_OVERLAPPEDWINDOW,
		   ws->posx, ws->posy,
		   ws->width == 0 ? 400 : ws->width + BORDWIDTH,
		   ws->height == 0 ? 400: ws->height + BORDHEIGHT,
		   NULL, NULL, mswinInstance, NULL);
   hdc = GetDC(ws->iconwin);
   if ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) && (numColors == 0)){
      /* This window is on a device that supports palettes */
      numColors = 2;
      logpal[0].palNumEntries = 2;
      logpal[0].palVersion = 0x300;
      logpal[0].palPalEntry[0].peFlags = 0;
      logpal[0].palPalEntry[0].peRed = 0;
      logpal[0].palPalEntry[0].peGreen = 0;
      logpal[0].palPalEntry[0].peBlue = 0;
      logpal[0].palPalEntry[1].peFlags = 0;
      logpal[0].palPalEntry[1].peRed = 255;
      logpal[0].palPalEntry[1].peGreen = 255;
      logpal[0].palPalEntry[1].peBlue = 255;
      palette = CreatePalette(logpal);
      if ((scp = malloc(2 * sizeof (struct wcolor))) == NULL) return NULL;
      scp[0].c = RGB(0,0,0);
      scp[0].type = CSHARED;
      strcpy(scp[0].name, "black");
      scp[1].c = RGB(255,255,255);
      scp[1].type = CSHARED;
      strcpy(scp[1].name, "white");
      }
   oldfont = SelectObject(hdc, wc->font->font);
   GetTextMetrics(hdc, &metrics);
   wc->font->charwidth = dc_maxcharwidth(hdc);
   SelectObject(hdc, oldfont);
   ReleaseDC(ws->iconwin, hdc);
   wc->font->ascent = metrics.tmAscent;
   wc->font->descent = metrics.tmDescent;
   /*   wc->font->charwidth = metrics.tmMaxCharWidth; buggy */
   wc->font->height = metrics.tmHeight;
   wc->leading = metrics.tmHeight;
   ws->x = 0;
   ws->y = ASCENT(w);
   ws->y += w->context->dy;
   ws->x += w->context->dx;
   /*
    * set the generic window's true default sizes
    */
   if (!ws->width || !ws->height) {
      if (!ws->width) ws->width = FWIDTH(w) * 80;
      if (!ws->height) ws->height = FHEIGHT(w) * 12;
      SetWindowPos(ws->iconwin, ws->iconwin,
		   ws->posx,
		   ws->posy,
		   ws->width + BORDWIDTH, ws->height + BORDHEIGHT,
		   SWP_NOZORDER);
      }
   if (!ws->pix) {
      hdc = GetDC(ws->iconwin);
      ws->pix = CreateCompatibleBitmap(hdc, ws->width, ws->height);
      ReleaseDC(ws->iconwin, hdc);
      }

   if (alc_rgb(w, wc->fg) == Failed) {
      return 0;
      }
   if (alc_rgb(w, wc->bg) == Failed) {
      return 0;
      }
   ws->pixwidth = ws->width;
   ws->pixheight = ws->height;
   if (!ISTOBEHIDDEN(ws)) {
      ws->win = ws->iconwin;
      ShowWindow(ws->win, ncmdShow);
      }
   else ws->win = 0;

   if (ws->initialPix) {
      hdc = GetDC(ws->iconwin);
      hdc2 = CreateCompatibleDC(hdc);
      oldpix = SelectObject(hdc2, ws->pix);
      hdc3 = CreateCompatibleDC(hdc);
      oldpix2 = SelectObject(hdc3, ws->initialPix);
      BitBlt(hdc2, 0, 0, ws->width, ws->height, hdc3, 0, 0, SRCCOPY);
      if (ws->win)
         BitBlt(hdc, 0, 0, ws->width, ws->height, hdc3, 0, 0, SRCCOPY);
      SelectObject(hdc2, oldpix);
      SelectObject(hdc3, oldpix2);
      DeleteDC(hdc2);
      DeleteDC(hdc3);
      DeleteObject(ws->initialPix);
      ws->initialPix = (HBITMAP) NULL;
      }
   else {
      /*
       * initialize the image with the background color
       */
      RECT rec;
      hdc = GetDC(ws->iconwin);
      hdc2 = CreateCompatibleDC(hdc);
      oldpix = SelectObject(hdc2, ws->pix);
      rec.left = rec.top = 0;
      rec.right = ws->width;
      rec.bottom = ws->height;
      if (palette) {
         SelectPalette(hdc, palette, FALSE);
         SelectPalette(hdc2, palette, FALSE);
	 RealizePalette(hdc);
	 RealizePalette(hdc2);
	 }
      brush = CreateBrushIndirect(&(wc->bgbrush));
      if (ws->win)
         FillRect(hdc, &rec, brush);
      FillRect(hdc2, &rec, brush);
      DeleteObject(brush);
      SelectObject(hdc2, oldpix);
      ReleaseDC(ws->iconwin, hdc);
      DeleteDC(hdc2);

      imd = &ws->initimage;
      if (imd->width) {
	 r = strimage(w, 0, 0, imd->width, imd->height, imd->paltbl,
		      imd->data, (word)imd->width * (word)imd->height, 0);
	 free((pointer)imd->paltbl);
	 free((pointer)imd->data);
	 imd->width = 0;
	 if (r < 0) {
	    return 0;
	    }
	 }
      }
   if (ws->win)
      UpdateWindow(ws->win);

   return (FILE *)w;
   }

int handle_config(wbp w, UINT msg, WPARAM wp, LPARAM lp)
   {
   int neww, newh;
   struct descrip d;
   wsp ws = w->window;

   if (wp == SIZE_MINIMIZED) {
      if (ws->win) {
         SetWindowText(ws->win, ws->iconlabel);
         ws->win = NULL;
	 }
      return 1;
      }

   if (ws->win)
      SetWindowText(ws->win, ws->windowlabel);
   ws->win = ws->iconwin;

   /*
    * make sure text cursor stays on-screen
    */
   ws->x = min(ws->x, LOWORD(lp) - FWIDTH(w));
   ws->y = min(ws->y, HIWORD(lp));

   neww = LOWORD(lp);
   newh = HIWORD(lp);

   /*
    * if it was not a resize, drop it
    */
   if ((ws->width == neww) && (ws->height == newh)) {
      return 1;
      }

   ws->width = neww;
   ws->height = newh;
   if (! resizePixmap(w, ws->width, ws->height)) return 0;
   if (!ISEXPOSED(w)) {
      SETEXPOSED(w);
      return 1;
      }
   MakeInt(RESIZED, &d);
   qevent(w->window, &d, ws->width, ws->height, ~(uword)0, 0);
   return 1;
   }

/*
 * handle window controls (child windows), at the moment these are
 * buttons and scrollbars.  wp is which child (base 1).
 * Buttons come in as undiluted messages.
 * Scrollbars come in with msg = new value of scrollbar
 */
void handle_child(wbp wb, UINT msg, WPARAM wp, LPARAM lp)
   {
   DWORD dw;
   LONG t;
   WORD x, y;
   wsp ws = wb->window;
   char *s;
   int len;
   struct descrip d;
   int flags = 0;
   if (LOWORD(wp) > ws->nChildren) return;
   s = ws->child[LOWORD(wp) - 1].id;
   len = strlen(s);
   d = nulldesc;
   StrLoc(d) = alcstr(s, len);
   StrLen(d) = len;
   switch (HIWORD(wp)) {
   case BN_CLICKED: {
      dw = GetMessagePos();
      x = LOWORD(dw);
      y = HIWORD(dw);
      break;
      }
   case EN_SETFOCUS: case EN_KILLFOCUS: case EN_CHANGE: case EN_UPDATE:
   case EN_ERRSPACE: case EN_MAXTEXT: case EN_HSCROLL: case EN_VSCROLL: {
	return;
      }
   default: { /* scrollbar */
      x = y = msg;
      }
   }
   t = GetMessageTime();
   qevent(ws, &d, x, y, t, flags);
   if (ws->focusChild)
      SetFocus(ws->focusChild);
   else if (ws->win)
      SetFocus(ws->win);
   else
      SetFocus(ws->iconwin);
   }

void handle_menu(wbp wb, UINT msg, WPARAM wp, LPARAM lp)
   {
   wsp ws = wb->window;
   DWORD dw;
   LONG t;
   WORD x, y;
   struct descrip d;
   char *s = ws->menuMap[wp];
   int len = strlen(s);
   int flags = 0;

   d = nulldesc;
   StrLoc(d) = alcstr(s, len);
   StrLen(d) = len;
   dw = GetMessagePos();
   x = LOWORD(dw);
   y = HIWORD(dw);
   t = GetMessageTime();
   qevent(ws, &d, x, y, t, flags);
   }

void handle_keypress(wbp wb, UINT msg, WPARAM wp, LPARAM lp, int meta)
   {
   wsp ws = wb->window;
   DWORD dw;
   LONG t;
   WORD x, y;
   struct descrip d;
   int flags = 0;
   if (msg == WM_CHAR || msg == WM_SYSCHAR) {
      StrLen(d) = 1;
      StrLoc(d) = (char *)&allchars[wp & 0xFF];
      }
   else { /* WM_KEYDOWN or WM_SYSKEYDOWN */
      MakeInt(wp, &d);
      }
   dw = GetMessagePos();
   x = LOWORD(dw);
   y = HIWORD(dw);
   t = GetMessageTime();
   if (GetKeyState(VK_CONTROL) < 0) flags |= ControlMask;
   if (GetKeyState(VK_SHIFT) < 0) flags |= ShiftMask;

   if (meta) flags |= Mod1Mask;
   qevent(ws, &d, x, y, t, flags);
   }

void handle_mouse(wbp wb, UINT msg, WPARAM wp, LPARAM lp)
   {
   wsp ws = wb->window;
   LONG t;
   WORD x, y;
   struct descrip d;
   long flags = 0, eventcode;

   switch(msg) {
   case WM_MOUSEMOVE: /* only called if one of these three cases is true */
      if (MK_LBUTTON & wp)
	 eventcode = MOUSELEFTDRAG;
      else if (MK_RBUTTON & wp)
	 eventcode = MOUSERIGHTDRAG;
      else if (MK_MBUTTON & wp)
	 eventcode = MOUSEMIDDRAG;
      else eventcode = 0;
      break;
   case WM_LBUTTONDOWN:
      eventcode = MOUSELEFT;
      break;
   case WM_MBUTTONDOWN:
      eventcode = MOUSEMID;
      break;
   case WM_RBUTTONDOWN:
      eventcode = MOUSERIGHT;
      break;
   case WM_LBUTTONUP:
      eventcode = MOUSELEFTUP;
      break;
   case WM_MBUTTONUP:
      eventcode = MOUSEMIDUP;
      break;
   case WM_RBUTTONUP:
      eventcode = MOUSERIGHTUP;
      break;
   default:
      eventcode = 0;
      break;
      }

   MakeInt(eventcode, &d);
   x = LOWORD(lp);
   y = HIWORD(lp);
   t = GetMessageTime(); /* why might someone comment this out? */

   if (MK_CONTROL & wp) flags |= ControlMask;
   if (MK_SHIFT & wp) flags |= ShiftMask;

   qevent(ws, &d, x, y, t, flags);
   }

LRESULT_CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
   HDC hdc, hdc2;
   PAINTSTRUCT ps;
   RECT rect;
   wbp wb = NULL;
   wsp ws = NULL;
   int n, i, imin, imax;

   /*
    * find a binding associated with the given window.
    */
   for (wb = wbndngs; wb; wb=wb->next) {
      ws = wb->window;

      if ((ws->win == hwnd) || (ws->iconwin == hwnd)) break;
      }
   if (msg == WM_QUIT) {
      wfreersc();
      }
   else if (!wb) {
      /*
       * doesn't look like its for one of our windows, pass it to
       * DefWindowProc and hope for the best.
       */
      }
   else
   switch(msg) {
   case WM_PAINT:
      hdc = BeginPaint(hwnd, &ps);
      GetClientRect(hwnd, &rect);
      if (IsIconic(hwnd)) {
         HBRUSH hb = CreateBrushIndirect(&(wb->context->brush));
	 FrameRect(hdc, &rect, hb);
	 DeleteObject(hb);
	 DrawText(hdc, "Iconx", 5, &rect, DT_WORDBREAK);
	 }
      else {
         HBITMAP oldpix;
	 hdc2 = CreateCompatibleDC(hdc);
	 oldpix = SelectObject(hdc2, ws->pix);
	 BitBlt(hdc, rect.left, rect.top,
		rect.right - rect.left + 1, rect.bottom - rect.top + 1,
		hdc2, rect.left, rect.top, SRCCOPY);
         SelectObject(hdc2, oldpix);
	 DeleteDC(hdc2);
	 }
      EndPaint(hwnd, &ps);
      return 0;
   case WM_MOUSEMOVE:
      if (ws->curcursor)
         SetCursor(ws->curcursor);
      if ((MK_LBUTTON | MK_RBUTTON | MK_MBUTTON) & wp)
	 handle_mouse(wb,msg,wp,lp);
      return 0;
   case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MBUTTONDOWN:
   case WM_LBUTTONUP:   case WM_RBUTTONUP:   case WM_MBUTTONUP:
      handle_mouse(wb,msg,wp,lp);
      return 0;
   case WM_KEYDOWN:
      switch (wp) { /* VK defn's from <winuser.h> */
      case VK_F1: case VK_F2: case VK_F3: case VK_F4:
      case VK_F5: case VK_F6: case VK_F7: case VK_F8:
      case VK_F9: case VK_F10: case VK_F11: case VK_F12:
      case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT:
      case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN:
      case VK_INSERT: case VK_SELECT: case VK_PRINT:
      case VK_EXECUTE: case VK_SNAPSHOT: case VK_HELP: case VK_SEPARATOR:
      case VK_CLEAR: case VK_PAUSE: case VK_SCROLL:
	 handle_keypress(wb, msg, wp, lp, 0);
	 return 0;
      case VK_DELETE:
	 handle_keypress(wb, WM_CHAR, '\177', lp, 0);
	 return 0;
      }
      break;
   case WM_SYSKEYDOWN:
      switch (wp) {
      case VK_F1: case VK_F2: case VK_F3: /* alt-F4 terminates */
      case VK_F5: case VK_F6: case VK_F7: case VK_F8:
      case VK_F9: case VK_F10: case VK_F11: case VK_F12:
      case VK_HOME: case VK_END: case VK_PRIOR: case VK_NEXT:
      case VK_LEFT: case VK_RIGHT: case VK_UP: case VK_DOWN:
      case VK_INSERT: case VK_DELETE: case VK_SELECT: case VK_PRINT:
      case VK_EXECUTE: case VK_SNAPSHOT: case VK_HELP: case VK_SEPARATOR:
      case VK_CLEAR: case VK_PAUSE:
	 handle_keypress(wb, msg, wp, lp, 1);
	 return 0;
	}
      break;
   case WM_CHAR:
      handle_keypress(wb, msg, wp, lp, 0);
      return 0;
   case WM_SYSCHAR:
      handle_keypress(wb, msg, wp, lp, 1);
      /*
       * Unless there is a menu bar installed,
       * Alt-A .. Alt-Z, and Alt-0 .. Alt-9 are eaten by Icon;
       * others are passed on to Windows for things like Alt-Esc.
       */
      if (isalnum(wp) && !(ws->menuMap)) return 0;
      break;
   case WM_HSCROLL:
   case WM_VSCROLL:
      for(n=0; n < ws->nChildren && ws->child[n].win != (HWND)lp; n++){
         }
      if (n == ws->nChildren) break;
      i = GetScrollPos(ws->child[n].win, SB_CTL);
      GetScrollRange(ws->child[n].win, SB_CTL, &imin, &imax);
      switch (LOWORD(wp)) {
      case SB_PAGEDOWN :
         break;
      case SB_LINEDOWN :
         if (i < imax) {
            SetScrollPos(ws->child[n].win, SB_CTL,
	       GetScrollPos(ws->child[n].win, SB_CTL) + 1, TRUE);
	    }
         break;
      case SB_PAGEUP :
         break;
      case SB_LINEUP :
         if (i > imin) {
            SetScrollPos(ws->child[n].win, SB_CTL,
	       GetScrollPos(ws->child[n].win, SB_CTL) - 1, TRUE);
	    }
         break;
      case SB_TOP :
         SetScrollPos(ws->child[n].win, SB_CTL, imin, TRUE);
         break;
      case SB_BOTTOM :
         SetScrollPos(ws->child[n].win, SB_CTL, imax, TRUE);
         break;
      case SB_THUMBPOSITION :
         SetScrollPos(ws->child[n].win, SB_CTL, HIWORD(wp), TRUE);
         break;
      case SB_THUMBTRACK :
         SetScrollPos(ws->child[n].win, SB_CTL, HIWORD(wp), TRUE);
         break;
      case SB_ENDSCROLL: /* noop */
         break;
      default : /* potentially a problem here */
         break;
      }
      i = GetScrollPos(ws->child[n].win, SB_CTL);
      handle_child(wb, i, n+1, -1);
      break;
   case WM_COMMAND:
      if (LOWORD(lp) == 0)
         handle_menu(wb, msg, wp, lp);
      else
         handle_child(wb, msg, wp, lp);
      break;
   case WM_SIZE:
      handle_config(wb, msg, wp, lp);
      break;
   case WM_MOVE:
      ws->posx = LOWORD(lp) - (BORDWIDTH>>1);
      ws->posy = HIWORD(lp) - (BORDHEIGHT - 4);
      break;
   case WM_ACTIVATE:
      if (wp == WA_INACTIVE) {
         if (ws->savedcursor) SetCursor(ws->savedcursor);
         if (ws->hasCaret) {
            HideCaret(ws->iconwin);
            DestroyCaret();
            ws->hasCaret = 0;
            }
         }
      else { /* ... */
         if (ws->savedcursor == NULL)
            ws->savedcursor = SetCursor(ws->curcursor);
         else (void) SetCursor(ws->curcursor);
         if (ISCURSORON(wb)) {
	    CreateCaret(ws->iconwin, NULL, FWIDTH(wb), FHEIGHT(wb));
	    SetCaretPos(ws->x, ws->y - ASCENT(wb));
	    SetCaretBlinkTime(500);
	    ShowCaret(ws->iconwin);
	    ws->hasCaret = 1;
            }
	 }
      break;
   case WM_GETMINMAXINFO: {
      MINMAXINFO *mmi = (MINMAXINFO *)lp;
      if (! ISRESIZABLE(wb)) {
	 mmi->ptMinTrackSize.x = mmi->ptMaxTrackSize.x =
	    ws->width + BORDWIDTH;
	 mmi->ptMinTrackSize.y = mmi->ptMaxTrackSize.y =
	    ws->height + BORDHEIGHT;
	 }
      return 0;
      }
   case WM_KILLFOCUS:
      if (ws->hasCaret) {
         HideCaret(ws->iconwin);
         DestroyCaret();
         ws->hasCaret = 0;
         }
      break;
   case WM_SETFOCUS:
      if (ws->focusChild)
         SetFocus(ws->focusChild);
      else if (ISCURSORON(wb)) {
	 CreateCaret(ws->iconwin, NULL, FWIDTH(wb), FHEIGHT(wb));
	 SetCaretPos(ws->x, ws->y - ASCENT(wb));
	 SetCaretBlinkTime(500);
	 ShowCaret(ws->iconwin);
	 ws->hasCaret = 1;
         }
      break;
   /* case WM_QUIT is handled prior to the switch*/
   case WM_DESTROY:
      if (ws->win == hwnd)
	 ws->win = NULL;
      if (ws->iconwin == hwnd)
	 ws->iconwin = NULL;
      if (ws->refcount > 0) {
	 PostQuitMessage(0);
	 return 0;
	 }
      else if (ws->refcount < 0) {
	 ws->refcount = -ws->refcount;
	 }
      break;
   case MM_MCINOTIFY:
      mciSendCommand(LOWORD(lp), MCI_CLOSE, 0, (DWORD)NULL);
      break;
   }
   return DefWindowProc(hwnd, msg, wp, lp);
}

/*
 * wclose - make sure the window goes away - no questions asked
 */
int wclose(wbp w)
   {
   wsp ws = w->window;
   if (pollevent() == -1) return -1;
   if (ws->win && ws->refcount > 1) {
      /*
       * Decrement refcount and negate it to tell the window procedure
       * that we closed the window, not the user, so don't terminate.
       */
      ws->refcount--;
      ws->refcount = -ws->refcount;
      DestroyWindow(ws->win);
      while (ws->win)
	 if (pollevent() == -1) return -1;
      }
   else {
      free_binding(w);
      }
   return 1;
   }

int pollevent()
   {
   wbp w;
   MSG m;
   int result;

   /* some while PeekMessage loops here, maybe one per window ? */
   while (PeekMessage(&m, NULL, 0, 0, PM_NOREMOVE)) {
      if ((result = check_and_get_msg(&m)) <= 0) return result;
      TranslateMessage(&m);
      DispatchMessage(&m);
      }
   return 400;
   }

/*
 * write some text to both the window and the pixmap
 */
void xdis(w,s,n)
register wbp w;
char *s;
int n;
   {
   XPoint pt;
   HBRUSH hb;
   XRectangle rect;
   STDLOCALS(w);

   STDFONT;
   rect.left = ws->x; rect.right = ws->x + dc_textwidth(pixdc, s, n);
   rect.top = ws->y - ASCENT(w); rect.bottom = ws->y + DESCENT(w);

   /* skip resource allocation if we are offscreen */
   if (!(rect.left > ws->width || rect.right < 0 ||
       rect.top < 0 || rect.bottom > ws->height)) {

      hb = CreateBrushIndirect(&(wc->bgbrush));
      if (stdwin) {
         /*
          *  SetBkColor() does not dither consistently with bgbrush;
          *  erase the background beforehand and use transparent drawing
          */
         FillRect(stddc, &rect, hb);
         SetBkMode(stddc, TRANSPARENT);
         SetTextColor(stddc, PALCLR(wc->fg));
         TextOut(stddc, ws->x, ws->y - ASCENT(w), s, n);
         }
      FillRect(pixdc, &rect, hb);
      DeleteObject(hb);
      SetBkMode(pixdc, TRANSPARENT);
      SetTextColor(pixdc, PALCLR(wc->fg));
      TextOut(pixdc, ws->x, ws->y - ASCENT(w), s, n);
      }
   ws->x += dc_textwidth(pixdc, s, n);

   FREE_STDLOCALS(w);
   }
/*
 * wputc
 */
int wputc(int ci, wbp w)
   {
   char c = (char)ci;
   wsp ws = w->window;
   wcp wc = w->context;
   int y_plus_descent;
   HBRUSH hb;

   switch (c) {
   case '\n':
      ws->y += LEADING(w);
      if (ws->y + DESCENT(w) > ws->height) {
	 RECT r;
	 STDLOCALS(w);
	 ws->y -= LEADING(w);
	 y_plus_descent = ws->y + DESCENT(w);
	 BitBlt(pixdc, 0, 0,
		ws->width, y_plus_descent,
		pixdc, 0, LEADING(w), SRCCOPY);
	 r.left = 0;
	 r.top = y_plus_descent - FHEIGHT(w);
	 r.right = ws->width;
	 r.bottom = ws->height;
         hb = CreateBrushIndirect(&(wc->bgbrush));
	 FillRect(pixdc, &r, hb);
	 DeleteObject(hb);
	 if (stdwin)
	    BitBlt(stddc, 0, 0, ws->width, ws->height,
		   pixdc, 0, 0, SRCCOPY);
	 FREE_STDLOCALS(w);
	 }
      /* intended fall-through */
    case '\r':
      /*
       * set the new x position
       */
      ws->x = wc->dx;
      break;
   case '\t':
      xdis(w, "        ", 8 - (XTOCOL(w,ws->x) & 7));
      break;
      /*
       * Handle backspaces.  This implements cooked mode echo handling.
       */
   case '\177':
   case '\010': {
      int i = 0, pre_x;

      /*
       * Start with the last character queued up.
       */
      i--;
      /*
       * Trot back to the control-H itself.
       */
      while ((i>-EQUEUELEN) && (EVQUESUB(w,i) != c)) {
	 i--;
	 }
      if (i == -EQUEUELEN) break;
      /*
       * Go past the control-H.
       */
      i--;
      /*
       * Go back through any number of control-H's from prior lifetimes.
       */
      while((i > -EQUEUELEN) && !isprint(EVQUESUB(w,i))) {
	 i--;
	 }
      if (i == -EQUEUELEN) break;

      /*
       * OK, here's the character we're actually rubbing out.  Back up.
       */
      c = EVQUESUB(w,i);
      pre_x = ws->x;
      ws->x -= TEXTWIDTH(w, &c, 1);
      /*
       * Physically erase the character from the queue.  This results in
       * two control-H's present in the queue.
       */
      *evquesub(w,i) = '\010';
      /*
       * Save the backed-up position, and draw spaces through the erased.
       */
      i = ws->x;
      while(ws->x < pre_x) {
	 xdis(w, " ",1);
	 }
      ws->x = i;
      break;
      }
    /*
     * bell (control-G)
     */
    case '\007':
      break;
   default:
      xdis(w, &c, 1);
      }
   /*
    * turn the cursor back on
    */
   UpdateCursorPos(ws,wc);
   return 0;
   }

/*
 * wgetq - get event from pending queue
 */
int wgetq(wbp w, dptr res)
   {
   MSG m;
   wsp ws;
   int first = 0, i = 0, j;
   int hascaret = 0;
   FILE *f;

   if (!w || !(ws = w->window) || !(ws->iconwin)) {
      return -1;
      }
   while (1) {
      /*
       * grab the built up queue
       */
      if (!EVQUEEMPTY(ws)) {
	 EVQUEGET(ws, *res);
	 if (ws->hasCaret) {
            HideCaret(ws->iconwin);
            DestroyCaret();
            ws->hasCaret = 0;
            }
	 return 1;
	 }
      if (ISCURSORON(w) && ws->hasCaret == 0) {
	 CreateCaret(ws->iconwin, NULL, FWIDTH(w), FHEIGHT(w));
	 SetCaretPos(ws->x, ws->y - ASCENT(w));
	 SetCaretBlinkTime(500);
         ShowCaret(ws->iconwin);
	 ws->hasCaret = 1;
         }
      i++;
      if (check_and_get_msg(&m) <= 0) return -1;
      TranslateMessage(&m);
      DispatchMessage(&m);
      }
   }

/*
 * determine the new size of the client
 */
int setheight(w, height)
wbp w;
int height;
   {
   wsp ws = w->window;
   ws->height = height;
   return Succeeded;
   }

/*
 * determine new size of client
 */
int setwidth(w, width)
wbp w;
SHORT width;
   {
   wsp ws = w->window;
   ws->width = width;
   return Succeeded;
   }

int setgeometry(w, geo)
wbp w;
char *geo;
   {
   wsp ws = w->window;
   int width = 0, height = 0;
   int x = 0, y = 0, status;
   if ((status = parsegeometry(geo, &x, &y, &width, &height)) == 0)
      return Error;
   if (status & 1) {
      ws->width = width;
      ws->height = height;
      }
   if (status & 2) {
      ws->posx = x;
      ws->posy = y;
      }
   return Succeeded;
   }

int setcanvas(w,s)
wbp w;
char *s;
   {
   int cmd;
   wsp ws = w->window;
   HWND stdwin = ws->win;

   if (!strcmp(s, "iconic")) {
      cmd = SW_MINIMIZE;
      CLRTOBEHIDDEN(ws);
      }
   else if (!strcmp(s, "normal")) {
      cmd = SW_RESTORE;
      stdwin = ws->win = ws->iconwin;
      CLRTOBEHIDDEN(ws);
      }
   else if (!strcmp(s, "maximal")) {
      cmd = SW_SHOWMAXIMIZED;
      stdwin = ws->win = ws->iconwin;
      CLRTOBEHIDDEN(ws);
      }
   else if (!strcmp(s, "hidden")) {
      cmd = SW_HIDE;
      SETTOBEHIDDEN(ws);
      }
   else {
      return Error;
      }
   if (stdwin)
      ShowWindow(stdwin, cmd);

   return Succeeded;
   }

int seticonicstate(w, val)
wbp w;
char *val;
   {
   int height;
   return Failed;
   }

int seticonlabel(w, val)
wbp w;
char *val;
   {
   wsp ws = w->window;
   if (ws->iconlabel != NULL) free(ws->iconlabel);
   ws->iconlabel = salloc(val);
   if (ws->win && IsIconic(ws->win))
      SetWindowText(ws->win, ws->iconlabel);
   return Succeeded;
   }

int seticonpos(w, val)
wbp w;
char *val;
   {
   return Failed;
   }


int setwindowlabel(w, val)
wbp w;
char *val;
   {
   wsp ws = w->window;
   /*
    * plug in the new string
    */
   if (ws->windowlabel != NULL)
      free(ws->windowlabel);
   ws->windowlabel = salloc(val);

   /*
    * if we have to update, do it
    */
   if (ws->win && !IsIconic(ws->win))
      SetWindowText(ws->win, ws->windowlabel);
   return Succeeded;
   }

int setcursor(w, on)
wbp w;
int on;
   {
   wsp ws = w->window;
   if (on) {
      SETCURSORON(w);
      }
   else {
      CLRCURSORON(w);
      }
   return Succeeded;
   }

HFONT findfont(char *family, int size, int flags, int ansi)
{
   int weight;
   char slant, spacing;

   if (size < 0) size = DEFAULTFONTSIZE;

   if (flags & FONTFLAG_MEDIUM)
      weight = FW_MEDIUM;
   else if ((flags & FONTFLAG_DEMI) && (flags & FONTFLAG_BOLD))
      weight = FW_DEMIBOLD;
   else if (flags & FONTFLAG_BOLD)
      weight = FW_BOLD;
   else if (flags & FONTFLAG_DEMI)
      weight = FW_SEMIBOLD;
   else if (flags & FONTFLAG_LIGHT)
      weight = FW_LIGHT;
   else
      weight = FW_DONTCARE;

   if (flags & FONTFLAG_ITALIC)
      slant = 1;
   else
      slant = 0;

   if (flags & FONTFLAG_PROPORTIONAL)
      spacing = VARIABLE_PITCH;
   else if (flags & FONTFLAG_MONO)
      spacing = FIXED_PITCH;
   else spacing = DEFAULT_PITCH;

   return CreateFont(size, 0, 0, 0, weight, slant, 0, 0,
		(ansi && (MAXBYTESPERCHAR==1)) ? ANSI_CHARSET:DEFAULT_CHARSET,
		OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
		spacing, family);
}

HFONT mkfont(char *s)
{
   int flags, size;
   char family[MAXFONTWORD+1];
   char *stdfam = NULL;
   HFONT hf = 0;

   if (parsefont(s, family, &flags, &size)) {
      /*
       * This is a legal Icon font spec.
       * Check first for special "standard" family names.
       */
      if (!strcmp(family, "mono") || !strcmp(family, "fixed")) {
	 stdfam = "Lucida Sans";
	 flags |= FONTFLAG_MONO + FONTFLAG_SANS;
	 }
      else if (!strcmp(family, "typewriter")) {
	 stdfam = "Courier New"; /* was "courier" */
	 flags |= FONTFLAG_MONO + FONTFLAG_SERIF;
	 }
      else if (!strcmp(family, "sans")) {
	 stdfam = "Arial"; /* was "swiss" */
	 flags |= FONTFLAG_PROPORTIONAL + FONTFLAG_SANS;
	 }
      else if (!strcmp(family, "serif")) {
	 stdfam = "Times New Roman";
	 flags |= FONTFLAG_PROPORTIONAL + FONTFLAG_SERIF;
	 }
      else stdfam = NULL;

      if (stdfam) {
	 /*
	  * Standard name: first try preferred family, then generalize.
	  * ICONFONT can be NULL, in which case Windows chooses.
	  */
	 hf = findfont(stdfam, size, flags, 1);
	 if (hf == NULL)
	    hf = findfont(getenv("ICONFONT"), size, flags, 1);
	 }
      else {
	 /*
	  * Any other name: must match as specified.
	  */
	 hf = findfont(family, size, flags, 0);
	 }
      }
   return hf;
}

/*
 * Set the window's font by name.
 */
int setfont(w, s)
wbp w;
char **s;
   {
   wsp ws = w->window;
   wcp wc = w->context;
   HFONT hf, oldfont;
   TEXTMETRIC	metrics;
   HDC tmpdc;

   hf = mkfont(*s);
   if (hf != NULL) {
      if (wc->font->font)
	 DeleteObject(wc->font->font);
      wc->font->font = hf;
      if (wc->font->name)
         free(wc->font->name);
      wc->font->name = salloc(*s);

      tmpdc = GetDC(ws->iconwin);
      oldfont = SelectObject(tmpdc, hf);
      wc->font->charwidth = dc_maxcharwidth(tmpdc);
      if (GetTextMetrics(tmpdc, &metrics) == 0) {
         /* gettextmetrics can fail; what should we do about it? */
         ;
         }
      SelectObject(tmpdc, oldfont);
      ReleaseDC(ws->iconwin, tmpdc);
      wc->font->ascent = metrics.tmAscent;
      wc->font->descent = metrics.tmDescent;
/*    wc->font->charwidth = metrics.tmMaxCharWidth; unreliable due to MS bug */
      wc->leading = wc->font->height = metrics.tmHeight;
      return Succeeded;
      }
   return Failed;
   }

/*
 * rebind() - bind w's context to that of w2.
 */
int rebind(w, w2)
wbp w, w2;
   {
   wsp ws = w->window;
   /* decrement w->context->refcount?  increment w2->context->refcount? */
   w->context = w2->context;
   return Succeeded;
   }

void setclip(w)
wbp w;
   {
   wcp wc = w->context;
   if (wc->cliprgn)
      DeleteObject(wc->cliprgn);
   if (wc->clipw >= 0)
      wc->cliprgn = CreateRectRgn(wc->clipx, wc->clipy,
				  wc->clipx + wc->clipw,
				  wc->clipy + wc->cliph);
   else
      wc->cliprgn = NULL;
   }

void unsetclip(w)
wbp w;
   {
   wcp wc = w->context;
   if (wc->cliprgn)
      DeleteObject(wc->cliprgn);
   wc->cliprgn = NULL;
   }

 int lowerWindow(wbp w)
   {
   wsp ws = w->window;
   if (ws->win)
      SetWindowPos(ws->win, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
   return Succeeded;
   }

int raiseWindow(wbp w)
   {
   wsp ws = w->window;
   if (ws->win)
      SetWindowPos(ws->win, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
   return Succeeded;
   }

int nativecolor(w, s, r, g, b)
wbp w;
char *s;
long *r, *g, *b;
   {
   return 0;                    /* no new colors beyond those of Icon */
   }
/*
 * convert an Icon linear color into an MS Windows color value
 */
SysColor mscolor(wbp w, long r, long g, long b)
{
   SysColor x;
   double invgamma = 1.0 / w->context->gamma;
   long int red, green, blue;

   red   = 65535L * pow(r / 65535.0, invgamma);
   green = 65535L * pow(g / 65535.0, invgamma);
   blue  = 65535L * pow(b / 65535.0, invgamma);
   return RGB(red >> 8, green >> 8, blue >> 8);
}

/*
 * Set the context's fill style by name.
 */
int setfillstyle(w, s)
wbp w;
char *s;
   {
   wcp wc = w->context;

   if (!strcmp(s, "solid")) {
      wc->brush.lbStyle = wc->fillstyle = BS_SOLID;
      wc->brush.lbColor = PALCLR(wc->fg);
      }
   else {
      if (!strcmp(s, "masked")
      || !strcmp(s, "stippled") || !strcmp(s, "patterned")) {
	 wc->brush.lbStyle = wc->fillstyle = BS_PATTERN;
	 wc->brush.lbColor = PALCLR(wc->fg);
	 wc->brush.lbHatch = (LONG)wc->pattern;
         wc->bkmode = TRANSPARENT;
	 }
      else if (!strcmp(s, "textured")
      || !strcmp(s, "opaquestippled") || !strcmp(s, "opaquepatterned")) {
	 wc->brush.lbStyle = wc->fillstyle = BS_PATTERN;
	 wc->brush.lbColor = PALCLR(wc->fg);
	 wc->brush.lbHatch = (LONG)wc->pattern;
         wc->bkmode = OPAQUE;
	 }
      else {
	 return Error;
	 }
      }
   return Succeeded;
   }

/*
 * Set the context's line style by name.
 */
int setlinestyle(w, s)
wbp w;
char *s;
   {
   wcp wc = w->context;
   SHORT ltype;

   if ((ltype = si_s2i(siLineTypes, s)) < 0)
      return Error;
   wc->pen.lopnStyle = ltype;
   resetfg(w);
   if(!strcmp(s, "striped")) wc->bkmode = OPAQUE;
   else wc->bkmode = TRANSPARENT;
   return Succeeded;
   }

/*
 * Set the context's line width
 */
int setlinewidth(wbp w, LONG linewid)
   {
   wcp wc = w->context;
   wc->pen.lopnWidth.x = wc->pen.lopnWidth.y =
      wc->bgpen.lopnWidth.x = wc->bgpen.lopnWidth.y = linewid;
   return Succeeded;
   }


/*
 * Set the foreground to draw in a mutable color
 */
int isetfg(wbp w, int i)
   {
   char tmp[20];
   wcp wc = w->context;
   if (-i > numColors) return Failed;
   wc->fg = (0x01000000L | -i);
   sprintf(tmp, "%ld", -i);
   if (wc->fgname != NULL) free(wc->fgname);
   wc->fgname = salloc(tmp);
   wc->pen.lopnColor = wc->fg;
   wc->brush.lbStyle = BS_SOLID;
   wc->brush.lbColor = wc->fg;
   return Succeeded;
   }

/*
 * Set the context's background color by color cell.
 */
int isetbg(w, i)
wbp w;
int i;
   {
   char tmp[20];
   wcp wc = w->context;
   if (-i > numColors) return Failed;
   wc->bg = (0x01000000L | -i);
   sprintf(tmp, "%ld", -i);
   if (wc->bgname != NULL) free(wc->bgname);
   wc->bgname = salloc(tmp);
   wc->bgpen.lopnColor = wc->bg;
   wc->bgbrush.lbStyle = BS_SOLID;
   wc->bgbrush.lbColor = wc->bg;
   return ISXORREVERSE(w) ? resetfg(w) : Succeeded;
   }


int getdepthDC(HDC dc)
{
   return GetDeviceCaps(dc, BITSPIXEL) * GetDeviceCaps(dc, PLANES);
}

int getdepth(wbp w)
{
   int i, j;
   STDLOCALS(w);
   i = GetDeviceCaps(pixdc, BITSPIXEL);
   j = GetDeviceCaps(pixdc, PLANES);
   FREE_STDLOCALS(w);
   return i * j;
}

int devicecaps(wbp w, int i)
{
   int rv;
   STDLOCALS(w);
   rv = GetDeviceCaps(pixdc, i);
   FREE_STDLOCALS(w);
   return rv;
}

/*
 * Reset the context's foreground color to whatever it is supposed to be.
 */
int resetfg(w)
wbp w;
   {
   return setfg(w, w->context->fgname);
   }

int alc_rgb(wbp w, SysColor rgb)
{
   int i;
   wsp ws = w->window;
   HDC hdc;
   PALETTEENTRY pe;
   LOGPALETTE lp;
   if (palette) {
      for (i=0; i < numColors; i++) {
         if (rgb == scp[i].c && scp[i].type == CSHARED) break;
	 }
      if (i == numColors) {
         numColors++;
	 if (ResizePalette(palette, numColors) == 0) {
	    numColors--;
	    return Failed;
            }
         scp = realloc(scp, numColors * sizeof(struct wcolor));
         if (scp == NULL) { numColors--; return Failed; }
	 scp[numColors - 1].c = rgb;
	 scp[numColors - 1].type = CSHARED;
	 sprintf(scp[numColors - 1].name, "%d,%d,%d",
                 RED(rgb), GREEN(rgb), BLUE(rgb));
         lp.palNumEntries = 1;
	 lp.palVersion = 0x300;
	 lp.palPalEntry[0].peFlags = 0;
	 lp.palPalEntry[0].peRed = RED(rgb);
	 lp.palPalEntry[0].peGreen = GREEN(rgb);
	 lp.palPalEntry[0].peBlue = BLUE(rgb);
         SetPaletteEntries(palette, numColors - 1, 1, lp.palPalEntry);
	 hdc = GetDC(ws->iconwin);
	 SelectPalette(hdc, palette, FALSE);
	 RealizePalette(hdc);
	 ReleaseDC(ws->iconwin, hdc);
	 }
      }
   return Succeeded;
}

/*
 * Retrieve next message, returning 0 if WM_QUIT, -1 if there is an error.
 */
int check_and_get_msg( MSG *pMsg )
{
   BOOL result;
   if ((result = GetMessage(pMsg, NULL, 0, 0)) <= 0)
   {
      return (result < 0) ? -1 : 0;
   }
   return 1;
}

/*
 * Set the context's foreground color
 */
int setfg(wbp w, char *val)
   {
   long r, g, b;
   wcp wc = w->context;
   if (parsecolor(w, val, &r, &g, &b) == Succeeded) {
      wc->fg = mscolor(w, r, g, b);
      if (alc_rgb(w, wc->fg) == Failed) return Failed;
      if (!wc->fgname) wc->fgname = salloc(val);
      else if (strcmp(wc->fgname, val)) {
         free(wc->fgname);
         wc->fgname = salloc(val);
	 }
      wc->brush.lbColor =
	 PALCLR(ISXORREVERSE(w) ? ((wc->fg ^ wc->bg) & 0x00FFFFFF) : wc->fg);
      wc->pen.lopnColor = wc->brush.lbColor;
      wc->brush.lbStyle = wc->fillstyle;
      if (wc->fillstyle == BS_PATTERN)
	 wc->brush.lbHatch = (LONG)wc->pattern;
      return Succeeded;
      }
   return Failed;
   }

/*
 * Set the window context's background color
 */
int setbg(wbp w, char *val)
   {
   long r, g, b;
   wcp wc = w->context;
   if (parsecolor(w, val, &r, &g, &b) == Succeeded) {
      wc->bg = mscolor(w, r, g, b);
      if (alc_rgb(w, wc->bg) == Failed) return Failed;
      if (!wc->bgname) wc->bgname = salloc(val);
      else if (strcmp(wc->bgname, val)) {
         free(wc->bgname);
         wc->bgname = salloc(val);
	 }
      wc->bgpen.lopnColor = PALCLR(wc->bg);
      wc->bgbrush.lbStyle = BS_SOLID;
      wc->bgbrush.lbColor = PALCLR(wc->bg);
      return ISXORREVERSE(w) ? resetfg(w) : Succeeded;
      }
   return Failed;
   }

/*
 * Set the gamma correction factor.
 */
int setgamma(w, gamma)
wbp w;
double gamma;
   {
   wcp wc = w->context;
   wc->gamma = gamma;
   setfg(w, wc->fgname);
   setbg(w, wc->bgname);
   return Succeeded;
   }

/*
 * setpointer() - define a mouse pointer shape
 */
int setpointer(w, val)
wbp w;
char *val;
   {
   HCURSOR c;
   char *cval;
   if (!strcmp(val,"arrow")) cval = IDC_ARROW;
   else if (!strcmp(val,"cross")) cval = IDC_CROSS;
   else if (!strcmp(val,"ibeam")) cval = IDC_IBEAM;
   else if (!strcmp(val,"uparrow")) cval = IDC_UPARROW;
   else if (!strcmp(val,"wait")) cval = IDC_WAIT;
   else if (!strcmp(val,"starting")) cval = IDC_APPSTARTING;
   else if (!strcmp(val,"icon")) cval = IDC_ICON;
   else if (!strcmp(val,"size")) cval = IDC_SIZE;
   else if (!strcmp(val,"sizenesw")) cval = IDC_SIZENESW;
   else if (!strcmp(val,"sizens")) cval = IDC_SIZENS;
   else if (!strcmp(val,"sizenwse")) cval = IDC_SIZENWSE;
   else if (!strcmp(val,"sizewe")) cval = IDC_SIZEWE;
   else if (!strcmp(val,"no")) cval = IDC_NO;
   else {
      return Failed;
      }
   c = LoadCursor(NULL, cval);
   if (c == NULL) {
     return Failed;
     }
   w->window->curcursor = c;
   if (w->window->cursorname) free(w->window->cursorname);
   w->window->cursorname = salloc(val);
   if (! w->window->savedcursor)
      w->window->savedcursor = SetCursor(c);
   else (void) SetCursor(c);
   /* should restore savedcursor when pointer moves outside our window */
   return Succeeded;
   }

/*
 * setdrawop() - set the drawing operation
 */
int setdrawop(w, val)
wbp w;
char *val;
   {
   wcp wc = w->context;
   if (!strcmp(val,"reverse")) {
      if (!ISXORREVERSE(w)) {
	 SETXORREVERSE(w);
         wc->drawop = R2_XORPEN;
         resetfg(w);
	 }
      }
   else {
      if (ISXORREVERSE(w)) {
	 CLRXORREVERSE(w);
         resetfg(w);
	 }
      wc->drawop = si_s2i(drawops,val);
      if (wc->drawop == -1) { wc->drawop = R2_COPYPEN; return Error; }
      }
   return Succeeded;
   }

setdisplay(wbp w, char *val)
   {
   if (strcmp(val, "MS Windows"))
      return Failed;
   return Succeeded;
   }

setimage(wbp w, char *val)
   {
   wsp ws = w->window;
   int status;
   ws->initialPix = loadimage(w, val, &(ws->width), &(ws->height),
			  0, &status);
   if (ws->initialPix == (HBITMAP) NULL) return Failed;
   return Succeeded;
   }

setleading(w, i)
wbp w;
int i;
   {
   wcp wc = w->context;
   wc->leading = i;
   }

void toggle_fgbg(w)
wbp w;
   {
   SysColor tmp;
   LOGPEN tpen;
   LOGBRUSH tbrush;
   wcp wc = w->context;
   tmp = wc->fg;
   wc->fg = wc->bg;
   wc->bg = tmp;
   tpen = wc->pen;
   wc->pen = wc->bgpen;
   wc->bgpen = tpen;
   tbrush = wc->brush;
   wc->brush = wc->bgbrush;
   wc->bgbrush = tbrush;
   }

int getvisual(w, answer)
wbp w;
char *answer;
   {
   return Failed;
   }

/*
 * getpos() - update the window state's notion of its current position
 */
int getpos(w)
wbp w;
   {
   return Succeeded;
   }

void getfg(w, answer)
wbp w;
char *answer;
   {
   strcpy(answer, w->context->fgname);
   }

void getbg(w, answer)
wbp w;
char *answer;
   {
   strcpy(answer, w->context->bgname);
   }

void getlinestyle(w, answer)
wbp w;
char *answer;
   {
   wcp wc = w->context;
   char *ptr = si_i2s(siLineTypes, wc->pen.lopnStyle);
   if (ptr != NULL) {
      strcpy(answer, ptr);
      }
   else strcpy(answer, "unknown");
   }

void getfntnam(w, answer)
wbp w;
char *answer;
   {
   strcpy(answer, w->context->font->name);
   }

void getpointername(w, answer)
wbp w;
char *answer;
   {
   char *s;
   wsp ws = w->window;
   strcpy(answer, w->window->cursorname);
   }

void getdisplay(wbp w, char *answer)
   {
   strcpy(answer, "MS Windows");
   }

void getdrawop(w, answer)
wbp w;
char *answer;
   {
   char *s;
   if (ISXORREVERSE(w)) s = "reverse";
   else s = si_i2s(drawops, w->context->drawop);
   if (s) sprintf(answer, "%s", s);
   else strcpy(answer, "unknown");
   }

void geticonic(w, answer)
wbp w;
char *answer;
   {
   getcanvas(w, answer);
   }

void getcanvas(w, answer)
wbp w;
char *answer;
   {
   wsp ws = w->window;
   if (ws->iconwin) {
      if (!IsWindowVisible(ws->iconwin)) sprintf(answer, "hidden");
      else if (IsIconic(ws->iconwin)) sprintf(answer, "iconic");
      else if (IsZoomed(ws->iconwin)) sprintf(answer, "maximal");
      else sprintf(answer,"normal");
      }
   else sprintf(answer,"hidden");
   }

int geticonpos(w, val)
wbp w;
char *val;
   {
   return Failed;
   }

/*
 * erase an area
 */
void eraseArea(w,x,y,width,height)
wbp w;
int x, y, width, height;
   {
   HBRUSH hb, oldbrush, oldbrush2;
   XRectangle rect;
   STDLOCALS(w);
   hb = CreateBrushIndirect(&(wc->bgbrush));
   if (stdwin) oldbrush = SelectObject(stddc, hb);
   oldbrush2 = SelectObject(pixdc, hb);
   rect.left = x; rect.right = x + width;
   rect.top = y; rect.bottom = y + height;

   if (stdwin) FillRect(stddc, &rect, hb);
   FillRect(pixdc, &rect, hb);
   if (stdwin) SelectObject(stddc, oldbrush);
   SelectObject(pixdc, oldbrush2);
   DeleteObject(hb);
   FREE_STDLOCALS(w);
   }

/*
 * copy an area
 */
int copyArea(w,w2,x,y,width,height,x2,y2)
wbp w, w2;
int x, y, width, height, x2, y2;
   {
   int lpad, rpad, tpad, bpad;
   RECT r;
   HDC srcdc, srcpixdc;
   HBRUSH hb;
   wsp ws1 = w->window;
   HBITMAP oldpix;
   STDLOCALS(w2);
   /*
    * setup device contexts for area copy
    */
   SetROP2(pixdc, R2_COPYPEN);
   hb = CreateBrushIndirect(&(wc->bgbrush));
   if (stdwin)
      SetROP2(stddc, R2_COPYPEN);
   if (w2->window == w->window) {
      srcdc = pixdc;
      srcpixdc = pixdc;
      }
   else {
      srcdc = GetDC(w->window->iconwin);
      srcpixdc = CreateCompatibleDC(srcdc);
      SetROP2(srcpixdc, R2_COPYPEN);
      }
   oldpix = SelectObject(srcpixdc, w->window->pix);

   /*
    * copy area, write unavailable areas with bg color
    */
   if (x + width < 0 || y + height < 0 || x >= ws1->pixwidth || y >= ws1->pixheight) {
      /* source is entirely offscreen, just fill with background */
      r.left = x2; r.top = y2;
      r.right = x2 + width; r.bottom = y2 + height;
      if (stdwin)
         FillRect(stddc, &r, hb);
      FillRect(pixdc, &r, hb);
      }
   else {
	 /*
	  * Check for source partially offscreen, but copy first and
	  * fill later in case the source and destination overlap.
	  */
	 lpad = rpad = tpad = bpad = 0;
         if (x < 0) {			/* source extends past left edge */
	    lpad = -x;
            width -= lpad;
            x2 += lpad;
            x = 0;
            }
         if (x + width > ws1->pixwidth) {  /* source extends past right edge */
            rpad = x + width - ws1->pixwidth;
            width -= rpad;
            }
         if (y < 0) {			/* source extends above top edge */
	    tpad = -y;
            height -= tpad;
            y2 += tpad;
            y = 0;
            }
         if (y + height > ws1->pixheight) {  /* source extends below bottom */
            bpad = y + height - ws1->pixheight;
            height -= bpad;
            }
      if (stdwin)
         BitBlt(stddc, x2, y2, width, height, srcpixdc, x, y, SRCCOPY);
      BitBlt(pixdc, x2, y2, width, height, srcpixdc, x, y, SRCCOPY);

	 if (lpad > 0) {
            r.left = x2-lpad;
	    r.top = y2-tpad;
	    r.right = r.left + lpad;
	    r.bottom = r.top + tpad+height+bpad;
            if (stdwin)
               FillRect(stddc, &r, hb);
            FillRect(pixdc, &r, hb);
            }
	 if (rpad > 0) {
            r.left = x2+width;
	    r.top = y2-tpad;
	    r.right = r.left + rpad;
	    r.bottom = r.top + tpad+height+bpad;
            if (stdwin)
               FillRect(stddc, &r, hb);
            FillRect(pixdc, &r, hb);
	    }
	 if (tpad > 0) {
	    r.left = x2;
	    r.top = y2-tpad;
	    r.right = r.left + width;
	    r.bottom = r.top + tpad;
            if (stdwin)
               FillRect(stddc, &r, hb);
            FillRect(pixdc, &r, hb);
	    }
	 if (bpad > 0) {
	    r.left = x2;
	    r.top = y2+height;
	    r.right = r.left + width;
	    r.bottom = r.top + bpad;
            if (stdwin)
               FillRect(stddc, &r, hb);
            FillRect(pixdc, &r, hb);
	    }
      }

   /*
    * free resources
    */
   SelectObject(srcpixdc, oldpix);
   if (w2->window != w->window) {
      ReleaseDC(w->window->iconwin, srcdc);
      DeleteDC(srcpixdc);
      }
   DeleteObject(hb);
   FREE_STDLOCALS(w2);
   return Succeeded;
   }

int getdefault(w, prog, opt, answer)
wbp w;
char *prog, *opt, *answer;
   {
   return Failed;
   }

/*
 * Draw a bilevel image.
 */
int blimage(w, x, y, width, height, ch, s, len)
wbp w;
int x, y, width, height, ch;
unsigned char *s;
word len;
   {
   unsigned int m, msk1, c, ix, iy;
   long fg, bg;
   SysColor palfg, palbg;
   STDLOCALS(w);
   palfg = PALCLR(wc->fg);
   palbg = PALCLR(wc->bg);
   /*
    * Read the image string and set the pixel values.  Note that
    * the hex digits in sequence fill the rows *right to left*.
    */
   m = width % 4;
   if (m == 0)
      msk1 = 8;
   else
      msk1 = 1 << (m - 1);              /* mask for first byte of row */

   fg = wc->fg;
   bg = wc->bg;
   ix = width;
   iy = 0;
   m = msk1;
   while (len--) {
      if (isxdigit(c = *s++)) {         /* if hexadecimal character */
	 if (!isdigit(c))               /* fix bottom 4 bits if necessary */
	    c += 9;
	 while (m > 0) {                /* set (usually) 4 pixel values */
	    --ix;
	    if (c & m) {
	       SetPixel(pixdc, ix, iy, palfg);
	       }
	    else if (ch != TCH1) {      /* if zeroes aren't transparent */
	       SetPixel(pixdc, ix, iy, palbg);
	       }
	    m >>= 1;
	    }
	 if (ix == 0) {                 /* if end of row */
	    ix = width;
	    iy++;
	    m = msk1;
	    }
	 else
	    m = 8;
	 }
      }
   if (ix > 0)                          /* pad final row if incomplete */
      while (ix < width)
	 SetPixel(pixdc, ix++, iy, palbg);

   /*
    * Put it on the screen.
    */
   if (ws->win)
      BitBlt(stddc, x, y, width, height, pixdc, x, y, SRCCOPY);
   FREE_STDLOCALS(w);
   return Succeeded;
   }

/*
 * Draw a character-per-pixel image.
 */
int strimage(w, x, y, width, height, e, s, len, on_icon)
wbp w;
int x, y, width, height;
struct palentry *e;
unsigned char *s;
word len;
int on_icon;
   {
   HDC tempdc;
   HBITMAP temppix;
   register int c;
   register unsigned int ix;
   int v, anytransparent=0;
   unsigned int iy, tmpw;
   SysColor clrlist[256], xc, palbg;
   char tmp[24];
   BITMAPINFO *bmi;
   BITMAPINFOHEADER *bmih = &(bmi->bmiHeader);
   HBITMAP oldpix = 0;
   STDLOCALS(w);

   bmi = malloc(sizeof(BITMAPINFO) + 256 * sizeof(SysColor));
   if (bmi == NULL) {
      FREE_STDLOCALS(w);
      return -1;
      }

   bmih = &(bmi->bmiHeader);
   palbg = PALCLR(wc->bg);
   if (on_icon) {
      free(bmi);
      FREE_STDLOCALS(w);
      return -1;
      }

   bmih->biClrImportant = 0;
   /*
    * Build arrays of colors in SysColor and RGBQUAD format for use by
    * either SetPixel or DIB.  Decide which to use based on whether
    * there are any transparent pixels
    */
   for (c = 0; c < 256; c++) {
      if (e[c].transpt) anytransparent++;
      if (e[c].used && e[c].valid) {
         bmih->biClrImportant++;
	 clrlist[c] = mscolor(w, e[c].clr.red, e[c].clr.green, e[c].clr.blue);
	 bmi->bmiColors[c].rgbBlue = BLUE(clrlist[c]);
	 bmi->bmiColors[c].rgbRed = RED(clrlist[c]);
	 bmi->bmiColors[c].rgbGreen = GREEN(clrlist[c]);
         if (alc_rgb(w, clrlist[c]) == Failed) {
            free(bmi);
            FREE_STDLOCALS(w);
            return -1;
            }
	 clrlist[c] = PALCLR(clrlist[c]);
	 }
      else {
	 bmi->bmiColors[c].rgbBlue = BLUE(wc->bg);
	 bmi->bmiColors[c].rgbRed = RED(wc->bg);
	 bmi->bmiColors[c].rgbGreen = GREEN(wc->bg);
         }
      }

   /*
    * if transparent characters are not present, blast out a DIB.
    */
   if (anytransparent == 0) {
      char *buf = malloc(height * (width+4)), *buf2;
      buf2 = buf;
      bmih->biSize = sizeof(BITMAPINFOHEADER);
      bmih->biWidth = width;
      bmih->biHeight = -height;
      bmih->biPlanes = 1;
      bmih->biBitCount = 8;
      bmih->biCompression = BI_RGB;
      bmih->biSizeImage = 0;
      bmih->biXPelsPerMeter = 0;
      bmih->biYPelsPerMeter = 0;
      bmih->biClrUsed = 256;

      ix = 0;
      while (len--) {
         *buf++ = *s++;
         if (++ix >= width) {
            while(ix % 4) {
               buf++;
               ix++;
               }
            ix = 0;
            }
         }
      temppix=CreateDIBitmap(pixdc, bmih, CBM_INIT, buf2, bmi, DIB_RGB_COLORS);
      free(buf2);
      tempdc = CreateCompatibleDC(stddc);
      oldpix = SelectObject(tempdc, temppix);
      BitBlt(pixdc, x, y, width, height, tempdc, 0, 0, SRCCOPY);
      SelectObject(tempdc, oldpix);
      DeleteDC(tempdc);
      DeleteObject(temppix);
      }
   else {
      /*
       * The image contains some transparent pixels.
       * Read the image string and set the pixel values.
       * Note that SetPixelV() fails under Win32s; so we don't use it.
       */
      ix = x;
      iy = y;
      tmpw = x + width;
      while (len--) {
         c = *s++;
         v = e[c].valid;
         if (v) {                         /* put char if valid */
	    xc = SetPixel(pixdc, ix, iy, clrlist[c]);
	    }
         if (v || e[c].transpt) {        /* advance if valid or transparent */
	    if (++ix >= tmpw) {
	       ix = x;                             /* reset for new row */
	       iy++;
	       }
	    }
         }
      if (ix > 0)                          /* pad final row if incomplete */
         while (ix < width)
	    SetPixel(pixdc, x+ix++, y+iy, palbg);
      }

   free(bmi);
   /*
    * Copy it from the pixmap onto the screen.
    */
   if (on_icon) {
      FREE_STDLOCALS(w);
      return -1;
      }
   else {
      if (ws->win)
	 BitBlt(stddc, x, y, width, height, pixdc, x, y, SRCCOPY);
      }
   FREE_STDLOCALS(w);
   return 0;
   }

/*
 * imqsearch(key,base,nel) - binary search hardwired for images
 *
 *  A binary search routine with arguments similar to qsort(3).
 *  Returns a pointer to the item matching "key", or NULL if none.
 *  This is called a LOT, so it is hardwired for speed.
 *  Based on Bentley, CACM 28,7 (July, 1985), p. 676.
 */

SysColor * imqsearch (SysColor key, SysColor *base, int nel)
{
   int l, u, m;
   SysColor * a;

   l = 0;
   u = nel - 1;
   while (l <= u) {
      m = (l + u) / 2;
      a = base + m;
      if (*a < key)
         l = m + 1;
      else if (*a > key)
         u = m - 1;
      else
         return a;
      }
   while (a>base && key < *a) a--;
   while (a<base+nel && key > *a) a++;
   return a;
}

/*
 * capture -- get an image region.
 *
 * Stores the specified subimage in data as 15-bit color.
 */
int capture(w, xx, yy, width, height, data)
wbp w;
int xx, yy, width, height;
short *data;
   {
   SysColor px;
   int r, g, b, x, y;
   int wd = xx + width;
   int ht = yy + height;
   STDLOCALS(w);

   for (y = yy; y < ht; y++) {
      for (x = xx; x < wd; x++) {
	 px = GetPixel(pixdc, x, y);
	 r = RED(px) >> 3;
	 g = GREEN(px) >> 3;
	 b = BLUE(px) >> 3;
	 *data++ = (r << 10) | (g << 5) | b;
	 }
      }
   FREE_STDLOCALS(w);
   return 1;
   }

int readimage(w, filename, x, y, status)
wbp w;
char *filename;
int x, y, *status;
   {
   HBITMAP p, oldpix;
   unsigned int width, height;
   HDC srcdc, srcpixdc;

   if (!x && !y)
      p = loadimage(w, filename, &width, &height, 1, status);
   else
      p = loadimage(w, filename, &width, &height, 0, status);

   if (p == (HBITMAP) NULL) {
     return Failed;
     }

   {
   STDLOCALS(w);

   srcdc = GetDC(ws->iconwin);
   srcpixdc = CreateCompatibleDC(srcdc);
   oldpix = SelectObject(srcpixdc, p);
   BitBlt(pixdc, x, y, width, height, srcpixdc, 0, 0, SRCCOPY);
   if (stdwin)
      BitBlt(stddc, x, y, width, height, srcpixdc, 0, 0, SRCCOPY);
   SelectObject(srcpixdc, oldpix);
   ReleaseDC(ws->iconwin, srcdc);
   DeleteDC(srcpixdc);

   /*
    * Make sure previous ops on p are complete, then free it.
    */
   DeleteObject(p);
   FREE_STDLOCALS(w);
   return Succeeded;
   }
   }



/*
 * Initialize client for producing pixels from a window, or in this case,
 * only create a device context once, not once per getpixel.
 */
int getpixel_init(w, imem)
wbp w;
struct imgmem *imem;
   {
   COLORREF *p;
   wsp ws = w->window;
   int i, j, x2, y2;
   HDC stddc = GetDC(ws->iconwin), pixdc = CreateCompatibleDC(stddc);
   HBITMAP oldpix;

   if (palette) SelectPalette(pixdc, palette, FALSE);
   oldpix = SelectObject(pixdc, ws->pix);

   /* this looks like a bug for Win16 for images > 100x100 or so... */
   imem->crp = malloc( imem->width * imem->height * sizeof(COLORREF));
   if (imem->crp == NULL) return Failed;
   p = imem->crp;
   x2 = imem->x + imem->width;
   y2 = imem->y + imem->height;
   for(i = imem->y; i < y2; i++)
      for(j = imem->x; j < x2; j++) {
         if ((*p++ = GetPixel(pixdc, j, i)) == (COLORREF)-1L) {
	    free(imem->crp);
            SelectObject(pixdc, oldpix);
            FREE_STDLOCALS(w);
            return Failed;
            }
         }
   SelectObject(pixdc, oldpix);
   FREE_STDLOCALS(w);
   return Succeeded;
   }

int getpixel_term(w, imem)
wbp w;
struct imgmem *imem;
{
   free(imem->crp);
   return Succeeded;
}

/*
 * Return pixel (x,y) from a window
 */
int getpixel(wbp w, int x, int y, long *rv, char *s, struct imgmem *imem)
   {
   COLORREF cr = imem->crp[(y-imem->y) * imem->width + (x-imem->x)];
   *rv = 1;
   sprintf(s, "%ld,%ld,%ld",
	   (long)RED(cr)*257L, (long)GREEN(cr)*257L, (long)BLUE(cr)*257L);
   return Succeeded;
   }

int query_pointer(w, pp)
wbp w;
XPoint *pp;
   {
   wsp ws = w->window;
   RECT r;
   if (ws->win) {
      GetCursorPos(pp);
      GetWindowRect(ws->win, &r);
      pp->x -= r.left;
      pp->y -= r.top;
      return Succeeded;
      }
   return Failed;
   }

int query_rootpointer(pp)
XPoint *pp;
   {
   GetCursorPos(pp);
   return Succeeded;
   }

int seticonimage(w, dp)
wbp w;
dptr dp;
   {
   wsp ws = w->window;
   return Succeeded;
   }

/*
 * dumpimage -- write an image to a disk file.  Return 0 on failure.
 */
int dumpimage(wbp w, char *filename, unsigned int x, unsigned int y,
	      unsigned int width, unsigned int height)
   {
   int result = 0;
   HDIB dib;
   HDC  destdc;
   HBITMAP dumppix, oldpix;
   STDLOCALS(w);

   if (strcmp(".bmp", filename + strlen(filename) - 4) &&
       strcmp(".BMP", filename + strlen(filename) - 4)) {
      FREE_STDLOCALS(w);
      return NoCvt;
      }

   /*
    * extract the desired rectangle from the source bitmap
    */
   if (x || y || width != ws->pixwidth || height != ws->pixheight) {
      dumppix  = CreateCompatibleBitmap(stddc, width, height);
      destdc   = CreateCompatibleDC(stddc);
      oldpix = SelectObject(destdc, dumppix);
      BitBlt(destdc, 0, 0, width, height, pixdc, x, y, SRCCOPY);
      }
   else dumppix = ws->pix;
   dib = BitmapToDIB(dumppix, palette);
   if (dumppix != ws->pix) {
      SelectObject(destdc, oldpix);
      DeleteDC(destdc);
      DeleteObject(dumppix);
      }

   if (dib == NULL) {
      result = Failed;
      }
   else {
      if (result = SaveDIB(dib, filename)) { /* != 0 implies error */
	 result = Failed;
	 }
      else {
         result = Succeeded;
         }
      DestroyDIB(dib);
      }

   FREE_STDLOCALS(w);
   return result;
   }


/*
 * loadimage
 */
HBITMAP loadimage(wbp w, char *filename, unsigned int *width,
		  unsigned int *height, int atorigin, int *status)
   {
   HDC hdc;
   HDIB dib;
   HBITMAP bmap;
   HPALETTE p2;
   PALETTEENTRY pe;
   LPBITMAPINFO lpbmi;
   int j;
   int ii,jj, kk;
   int xx[256];
   unsigned char * pd;
   char *j2;

   dib = LoadDIB(filename);
   if (dib != NULL) {
      LPSTR pdib;
      p2 = CreateDIBPalette(dib);
      j2 = GlobalLock(dib);
      j = DIBNumColors(j2);
      jj = DIBWidth(j2);
      kk = DIBHeight(j2);
      GlobalUnlock(dib);

      if (!palette) {
	 LOGPALETTE logpal[4]; /* (1, + space for an extra palette entry) */
         hdc = GetDC(w->window->iconwin);
         if ((GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE) && (numColors ==0)){
	    /* This window is on a device that supports palettes */
	    numColors = 2;
	    logpal[0].palNumEntries = 2;
	    logpal[0].palVersion = 0x300;
	    logpal[0].palPalEntry[0].peFlags = 0;
	    logpal[0].palPalEntry[0].peRed = 0;
	    logpal[0].palPalEntry[0].peGreen = 0;
	    logpal[0].palPalEntry[0].peBlue = 0;
	    logpal[0].palPalEntry[1].peFlags = 0;
	    logpal[0].palPalEntry[1].peRed = 255;
	    logpal[0].palPalEntry[1].peGreen = 255;
	    logpal[0].palPalEntry[1].peBlue = 255;
	    palette = CreatePalette(logpal);
	    if (!palette) {
	       return NULL;
	       }
	    if ((scp = malloc(2 * sizeof (struct wcolor))) == NULL)
	       return NULL;
	    scp[0].c = RGB(0,0,0);
	    scp[0].type = CSHARED;
	    strcpy(scp[0].name, "black");
	    scp[1].c = RGB(255,255,255);
	    scp[1].type = CSHARED;
	    strcpy(scp[1].name, "white");
            }
	 else {
	    /* this window is not on a device that supports palettes */
	    }
	 ReleaseDC(w->window->iconwin, hdc);
         }
      if (palette) {
         if (ResizePalette(palette, numColors + j) == 0) {
            return NULL;
            }
         for (ii = 0; ii < j; ii++) {
            if (GetPaletteEntries(p2, ii, 1, &pe) == 0) {
               return NULL;
	       }
            SetPaletteEntries(palette, numColors++, 1, &pe);
            }
         }
      bmap = DIBToBitmap(dib, palette);
      pdib = GlobalLock(dib);
      *width = DIBWidth(pdib);
      *height = DIBHeight(pdib);
      GlobalUnlock(dib);
      DestroyDIB(dib);
      DeleteObject(p2);
      *status = 0;
      return bmap;
      }
   return NULL;
   }


char *get_mutable_name(wbp w, int mute_index)
   {
   char *tmp;
   PALETTEENTRY pe;

   if (-mute_index > numColors || scp[-mute_index].type != CMUTABLE) {
      return NULL;
      }

   if (GetPaletteEntries(palette, -mute_index, 1, &pe) == 0) {
      return NULL;
      }
   tmp = scp[-mute_index].name;
   sprintf(tmp, "%d", mute_index);
   sprintf(tmp + strlen(tmp) + 1, "%d,%d,%d",
      (pe.peRed << 8) | 0xff, (pe.peGreen << 8) | 0xff, (pe.peBlue << 8) | 0xff);
   return tmp + strlen(tmp) + 1;
   }

int set_mutable(wbp w, int i, char *s)
   {
   long r, g, b;
   UINT rv;
   PALETTEENTRY pe;
   if (palette == 0) return Failed;

   {
   STDLOCALS(w);
   if (parsecolor(w, s, &r, &g, &b) != Succeeded) {
      FREE_STDLOCALS(w);
      return Failed;                        /* invalid color specification */
      }
   pe.peRed = r >> 8;
   pe.peGreen = g >> 8;
   pe.peBlue = b >> 8;
   pe.peFlags = PC_RESERVED;
   raiseWindow(w);	/* mutable won't mutate if window isn't active */
#if 1
   AnimatePalette(palette, -i, 1, &pe);
   rv = SetPaletteEntries(palette, -i, 1, &pe);
#endif
   UnrealizeObject(palette);
   RealizePalette(stddc);
   AnimatePalette(palette, -i, 1, &pe);
   FREE_STDLOCALS(w);
}
   return Succeeded;
   }

void free_mutable(wbp w, int mute_index)
   {
   }

/*
 * Allocate a mutable color
 */
int mutable_color(wbp w, dptr argv, int argc, int *retval)
   {
   long r, g, b;
   tended char *str;
   LOGPALETTE lp;
   {
   STDLOCALS(w);

   if (!stddc || ((GetDeviceCaps(stddc, RASTERCAPS) & RC_PALETTE) == 0)) {
      FREE_STDLOCALS(w);
      return Failed;
      }

   numColors++;
   scp = realloc(scp, numColors * sizeof(struct wcolor));
   if (scp == NULL) {
      FREE_STDLOCALS(w);
      return Failed;
      }
   scp[numColors-1].c = -(numColors-1);
   sprintf(scp[numColors-1].name, "%d:", -(numColors-1));
   scp[numColors-1].type = CMUTABLE;
   if (ResizePalette(palette, numColors) == 0) {
      FREE_STDLOCALS(w);
      return Failed;
      }

   if (argc > 0) {                     /* set the color */
      if (argc != 1) {
         FREE_STDLOCALS(w);
         return Error;
         }
      /*
       * old-style check for C integer
       */
      else if (argv[0].dword == D_Integer) {/* check for color cell */
	 if (IntVal(argv[0]) >= 0) {
            FREE_STDLOCALS(w);
	    return Failed;        /* must be negative */
            }
	 if (GetPaletteEntries(palette, -IntVal(argv[0]),
	    1, lp.palPalEntry) == 0) {
            FREE_STDLOCALS(w);
            return Error;
            }
	 /* convert to linear color? */
	 }
      else {
	 if (!cnv:C_string(argv[0],str)) {
            FREE_STDLOCALS(w);
	    ReturnErrVal(103,argv[0], Error);
	    }
	 if (parsecolor(w, str, &r, &g, &b) != Succeeded) {
	    /* reduce logical palette size and count */
            FREE_STDLOCALS(w);
	    numColors--;
            ResizePalette(palette, numColors);
	    return Failed;                   /* invalid color specification */
	    }
	 lp.palPalEntry[0].peRed = r >> 8;
	 lp.palPalEntry[0].peGreen = g >> 8;
	 lp.palPalEntry[0].peBlue = b >> 8;
	 }
      lp.palNumEntries = 1;
      lp.palVersion = 0x300;
      lp.palPalEntry[0].peFlags = PC_RESERVED;
      SetPaletteEntries(palette, numColors - 1, 1, lp.palPalEntry);
      UnrealizeObject(palette);
      RealizePalette(stddc);
      }

   *retval = -(numColors - 1);
   FREE_STDLOCALS(w);
   return Succeeded;
   }
   }

void freecolor(wbp w, char *s)
   {
   }

/*
 * drawarcs() - assumes x and y are already fixed up for the bitmap
 */
void drawarcs(wbp wb, XArc *arcs, int narcs)
   {
   register XArc *arc = arcs;
   int i, halfwidth, halfheight, x1, y1, x2, y2, right, bottom;
   double a1_a2;
   HPEN hp, oldpen, oldpen2;
   STDLOCALS(wb);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   for (i = 0; i < narcs; i++, arc++) {
      halfwidth = arc->width >> 1;
      halfheight = arc->height >> 1;
      arc->angle1 = -arc->angle1 - arc->angle2;
      a1_a2 = arc->angle1 + arc->angle2;
      x1 = arc->x + halfwidth + (int)(halfwidth * cos(arc->angle1));
      y1 = arc->y + halfheight - (int)(halfheight * sin(arc->angle1));
      x2 = arc->x + halfwidth + (int)(halfwidth * cos(a1_a2));
      y2 = arc->y + halfheight - (int)(halfheight * sin(a1_a2));
      right = arc->x + arc->width + 1;
      bottom = arc->y + arc->height + 1;
      if (ws->win)
	 Arc(stddc, arc->x, arc->y, right, bottom, x1, y1, x2, y2);
      Arc(pixdc, arc->x, arc->y, right, bottom, x1, y1, x2, y2);
      }
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   FREE_STDLOCALS(wb);
   return;
   }


/*
 * drawlines - Support routine for DrawLine
 */
void drawlines(wbinding *wb, XPoint *points, int npoints)
   {
   int i, diff, bheight;
   HPEN hp, oldpen, oldpen2;
   XPoint tmp[2];
   STDLOCALS(wb);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   tmp[0] = points[npoints-1];
   tmp[1] = points[npoints-2];
   if (ws->win) {
      SetBkMode(stddc, wc->bkmode);
      Polyline(stddc, points, npoints);
      Polyline(stddc, tmp, 2);
      }
   SetBkMode(pixdc, wc->bkmode);
   Polyline(pixdc, points, npoints);
   Polyline(pixdc, tmp, 2);
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   FREE_STDLOCALS(wb);
   return;
   }


/*
 * drawpoints() -
 *  Parameters - the window binding for output, an array of points (assumed
 *               to be fixed up for bitmap) and the number of points
 */
void drawpoints(wbinding *wb, XPoint *points, int npoints)
   {
   register XPoint *p, *endp;
   SysColor palfg;
   STDLOCALS(wb);
   endp = points + npoints;
   palfg = PALCLR(wc->fg);
   if (stdwin) {
      for(p = points; p < endp; p++) {
         SetPixel(stddc, p->x, p->y, palfg);
         SetPixel(pixdc, p->x, p->y, palfg);
         }
      }
   else {
      for(p = points; p < endp; p++) {
         SetPixel(pixdc, p->x, p->y, palfg);
         }
      }
   FREE_STDLOCALS(wb);
   return;
   }


/*
 * drawsegments() -
 */
void drawsegments(wbinding *wb, XSegment *segs, int nsegs)
   {
   int i, bheight;
   XPoint ps[2];
   HPEN hp, oldpen, oldpen2;
   STDLOCALS(wb);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   if (stdwin) {
      SetBkMode(stddc, wc->bkmode);
      for (i = 0; i < nsegs; i++) {
	 Polyline(stddc, (POINT *)(segs+i), 2);
	 }
      }
   SetBkMode(pixdc, wc->bkmode);
   for (i = 0; i < nsegs; i++) {
      Polyline(pixdc, (POINT *)(segs+i), 2);
      }
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   FREE_STDLOCALS(wb);
   return;
   }


int allowresize(w, on)
wbp w;
int on;
   {
   if (on)
      SETRESIZABLE(w);
   else
      CLRRESIZABLE(w);
   return Succeeded;
   }
int getselection(wbp w, char *buf)
{
   return Failed;
   }
int setselection(wbp w, char *val)
{
   return Failed;
   }

/*
 * drawstrng()
 */
void drawstrng(wbinding *wb, int x, int y, char *s, int slen)
   {
   STDLOCALS(wb);

   STDFONT;
   if (stdwin) {
      SetBkMode(stddc, TRANSPARENT);
      if (wc->fg != RGB(0, 0, 0)) SetTextColor(stddc, PALCLR(wc->fg));
      if (wc->bg != RGB(255, 255, 255)) SetBkColor(stddc, PALCLR(wc->bg));
      TextOut(stddc, x, y - ASCENT(wb), s, slen);
      }
   SetBkMode(pixdc, TRANSPARENT);
   if (wc->fg != RGB(0, 0, 0)) SetTextColor(pixdc, PALCLR(wc->fg));
   if (wc->bg != RGB(255, 255, 255)) SetBkColor(pixdc, PALCLR(wc->bg));
   TextOut(pixdc, x, y - ASCENT(wb), s, slen);

   FREE_STDLOCALS(wb);
   return;
   }


/*
 * fillarcs
 */
void fillarcs(wbp wb, XArc *arcs, int narcs)
   {
   register XArc *arc = arcs;
   int i, diff, bheight;
   HBRUSH hb, oldbrush, oldbrush2;
   POINT pts[3];
   HPEN hp, oldpen, oldpen2;
   STDLOCALS(wb);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   hb = CreateBrushIndirect(&(wc->brush));
   if (stdwin) oldbrush = SelectObject(stddc, hb);
   oldbrush2 = SelectObject(pixdc, hb);
   SetTextColor(pixdc, PALCLR(wc->fg));
   if (stdwin) SetTextColor(stddc, PALCLR(wc->fg));
   SetBkColor(pixdc, PALCLR(wc->bg));
   if (stdwin) SetBkColor(stddc, PALCLR(wc->bg));
   if (stdwin) SetBkMode(stddc, wc->bkmode);
   SetBkMode(pixdc, wc->bkmode);
   for (i = 0; i < narcs; i++, arc++) {
      if (arc->angle2 >= 2 * Pi) {
         /*
          * from SDK reference: Ellipse() draws up to but not including
          * the right and bottom coordinates.  Add +1 to compensate.
          */
	 if (stdwin)
	    Ellipse(stddc, arc->x, arc->y,
		    arc->x + arc->width + 1, arc->y + arc->height + 1);
	 Ellipse(pixdc, arc->x, arc->y,
		 arc->x + arc->width + 1, arc->y + arc->height + 1);
	 }
      else {
	 arc->angle1 = -arc->angle1 - arc->angle2;
	 pts[0].x = arc->x + (arc->width>>1);
	 pts[0].y = arc->y + (arc->height>>1);
	 pts[1].x = arc->x + (arc->width>>1) +
            (int)(((arc->width + 1)>>1) * cos(arc->angle1));
	 pts[1].y = arc->y + (arc->height>>1) -
            (int)(((arc->height )>>1) * sin(arc->angle1));
	 pts[2].x = arc->x + (arc->width>> 1) +
            (int)(((arc->width + 1)>>1) * cos(arc->angle1+arc->angle2));
	 pts[2].y = arc->y + (arc->height>>1) -
            (int)(((arc->height )>>1) * sin(arc->angle1+arc->angle2));
	 if (stdwin) {
	    Pie(stddc, arc->x, arc->y,
		arc->x + arc->width + 1, arc->y + arc->height + 1,
		pts[1].x, pts[1].y, pts[2].x, pts[2].y);
	    }
	 Pie(pixdc, arc->x, arc->y,
	     arc->x + arc->width + 1, arc->y + arc->height + 1,
		pts[1].x, pts[1].y, pts[2].x, pts[2].y);
	 }
      }
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   if (stdwin) SelectObject(stddc, oldbrush);
   SelectObject(pixdc, oldbrush2);
   DeleteObject(hb);
   FREE_STDLOCALS(wb);
   return;
   }

/*
 * fillrectangles
 */
void fillrectangles(wbp wb, XRectangle *recs, int nrecs)
   {
   int i, diff, bheight;
   HBRUSH hb, oldbrush, oldbrush2;
   HPEN hp, oldpen, oldpen2;
   STDLOCALS(wb);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   hb = CreateBrushIndirect(&(wc->brush));
   if (stdwin) oldbrush = SelectObject(stddc, hb);
   oldbrush2 = SelectObject(pixdc, hb);
   SetTextColor(pixdc, PALCLR(wc->fg));
   if (stdwin) SetTextColor(stddc, PALCLR(wc->fg));
   SetBkColor(pixdc, PALCLR(wc->bg));
   if (stdwin) SetBkColor(stddc, PALCLR(wc->bg));
   if (stdwin) SetBkMode(stddc, wc->bkmode);
   SetBkMode(pixdc, wc->bkmode);
   for (i = 0; i < nrecs; i++) {
      recs[i].right += recs[i].left;
      recs[i].bottom += recs[i].top;
      if (stdwin) FillRect(stddc, (recs+i), hb);
      FillRect(pixdc, (recs+i), hb);
      }
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   if (stdwin) SelectObject(stddc, oldbrush);
   SelectObject(pixdc, oldbrush2);
   DeleteObject(hb);
   FREE_STDLOCALS(wb);
   return;
   }


/*
 * drawrectangles - draw nrecs # of rectangles in array recs to binding w
 */
void drawrectangles(wbp w, XRectangle *recs, int nrecs)
   {
   register XRectangle *r;
   LOGBRUSH lb;
   HBRUSH hb, oldbrush, oldbrush2;
   HPEN hp, oldpen, oldpen2;
   STDLOCALS(w);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   lb.lbStyle = BS_NULL;
   hb = CreateBrushIndirect(&lb);
   if (stdwin) oldbrush = SelectObject(stddc, hb);
   oldbrush2 = SelectObject(pixdc, hb);
   for (r = recs; r < recs + nrecs; r++) {
      /*
       * from SDK reference: Rectangle() draws up to but not including
       * the right and bottom coordinates.  Add +1 to compensate.
       */
      r->right += r->left + 1;
      r->bottom += r->top + 1;
      if (stdwin) Rectangle(stddc, r->left, r->top, r->right, r->bottom);
      Rectangle(pixdc, r->left, r->top, r->right, r->bottom);
      }
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   if (stdwin) SelectObject(stddc, oldbrush);
   SelectObject(pixdc, oldbrush2);
   DeleteObject(hb);
   FREE_STDLOCALS(w);
   return;
   }

/*
 * fillpolygon
 */
void fillpolygon(wbp w, XPoint *pts, int npts)
   {
   HBRUSH hb, oldbrush;
   HPEN hp, oldpen, oldpen2;
   STDLOCALS(w);
   if (stdwin) SetBkMode(stddc, wc->bkmode);
   SetBkMode(pixdc, wc->bkmode);
   hp = CreatePenIndirect(&(wc->pen));
   if (stdwin) oldpen = SelectObject(stddc, hp);
   oldpen2 = SelectObject(pixdc, hp);
   hb = CreateBrushIndirect(&(wc->brush));
   if (stdwin) {
      oldbrush = SelectObject(stddc, hb);
      Polygon(stddc, pts, npts);
      SelectObject(stddc, oldbrush);
      }
   oldbrush = SelectObject(pixdc, hb);
   Polygon(pixdc, pts, npts);
   SelectObject(pixdc, oldbrush);
   DeleteObject(hb);
   if (stdwin) SelectObject(stddc, oldpen);
   SelectObject(pixdc, oldpen2);
   DeleteObject(hp);
   FREE_STDLOCALS(w);
   }

LONG NumWindows = 0;


/*
 * allocate a context.  Can't be called until w has a display and window.
 */
wcp alc_context(w)
wbp w;
   {
   int i;
   wcp wc;

   GRFX_ALLOC(wc, _wcontext);

   wc->bkmode = OPAQUE;        /* at present, only used in line drawing */
   wc->fg = RGB(0,0,0);
   wc->bg = RGB(255,255,255);
   wc->fgname = salloc("black");
   wc->bgname = salloc("white");
   wc->pen.lopnStyle = PS_SOLID;
   wc->pen.lopnWidth.x = wc->pen.lopnWidth.y = 1;
   wc->pen.lopnColor = PALCLR(wc->fg);
   wc->bgpen.lopnStyle = PS_SOLID;
   wc->bgpen.lopnWidth.x = wc->bgpen.lopnWidth.y = 1;
   wc->bgpen.lopnColor = PALCLR(wc->bg);
   wc->fillstyle = BS_SOLID;
   wc->brush.lbStyle = BS_SOLID;
   wc->brush.lbColor = PALCLR(wc->fg);
   wc->bgbrush.lbStyle = BS_SOLID;
   wc->bgbrush.lbColor = PALCLR(wc->bg);
   wc->gamma = GammaCorrection;
   wc->drawop = R2_COPYPEN;
   wc->font = (wfp)alloc(sizeof (struct _wfont));
   wc->font->name = salloc("fixed");
   wc->font->font = CreateFont(16,0,0,0,FW_NORMAL,0,0,0,
	 ((MAXBYTESPERCHAR==1)?ANSI_CHARSET:DEFAULT_CHARSET),
	 OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
	 DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
	 getenv("ICONFONT"));

   wc->font->charwidth = 8; /* looks like a bug */
   wc->leading = 16;

   GRFX_LINK(wc, wcntxts);
   return wc;
   }

/*
 * allocate a context, cloning attributes from an existing context
 */
wcp clone_context(w)
wbp w;
   {
   wcp wc, wc2 = w->context;
   wsp ws = w->window;
   wbinding tmp;
   int i;

   GRFX_ALLOC(wc, _wcontext);

   tmp.window = ws;
   tmp.context = wc;
   /*
    * copy over some stuff
    */
   wc->clipx = wc2->clipx;
   wc->clipy = wc2->clipy;
   wc->clipw = wc2->clipw;
   wc->cliph = wc2->cliph;
   if (wc2->cliprgn)
      wc->cliprgn = CreateRectRgn(wc->clipx,wc->clipy,
			wc->clipx+wc->clipw,
			wc->clipy+wc->cliph);
   wc->dx = wc2->dx;
   wc->dy = wc2->dy;
   wc->bits = wc2->bits;
   /*
    * clone needs to make a copy of the pattern
    *   if (wc2->pattern) {
    *      wc->pattern = copy+somehow(wc2->pattern);
    *      if (wc2->patternname)
    *         wc->patternname = salloc(wc2->patternname);
    *      }
    */

   wc->bkmode = wc2->bkmode;
   wc->fg = wc2->fg;
   wc->bg = wc2->bg;
   wc->fgname = salloc(wc2->fgname);
   wc->bgname = salloc(wc2->bgname);
   wc->pen = wc2->pen;
   if (ISXORREVERSEW(wc)) {
      wc->brush.lbColor = PALCLR((wc->fg ^ wc->bg) & 0x00FFFFFF);
      }
   else {
      wc->brush.lbColor = PALCLR(wc->fg);
      }
   wc->bgpen = wc2->bgpen;
   wc->fillstyle = wc2->fillstyle;
   wc->brush.lbStyle = wc->fillstyle;
   wc->bgbrush.lbStyle = wc->fillstyle;
   wc->bgbrush.lbColor = PALCLR(wc->bg);
   wc->gamma = wc2->gamma;
   wc->drawop = wc2->drawop;
   wc->font = (wfp)alloc(sizeof (struct _wfont));
   wc->font->name = salloc("fixed");
   wc->font->font = CreateFont(13,0,0,0,FW_NORMAL,0,0,0,
	 ((MAXBYTESPERCHAR==1)?ANSI_CHARSET:DEFAULT_CHARSET),
	 OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,
	 DEFAULT_QUALITY, FIXED_PITCH | FF_MODERN,
	 getenv("ICONFONT"));
   wc->leading = wc2->leading;
   setfont(&tmp, &(wc2->font->name));

   GRFX_LINK(wc, wcntxts);
   return wc;
   }

/*
 * allocate a window state structure
 */
wsp alc_winstate()
   {
   int i;
   wsp ws;

   GRFX_ALLOC(ws, _wstate);
   ws->bits = 1024;                             /* echo ON; others OFF */
   ws->filep = nulldesc;
   ws->listp = nulldesc;
   ws->cursorname = salloc("arrow");
   ws->curcursor = LoadCursor(NULL, IDC_ARROW);
   GRFX_LINK(ws, wstates);
   return ws;
   }

/*
 * free a window state
 */
int free_window(ws)
wsp ws;
   {
   int i;
   ws->refcount--;
   if(ws->refcount == 0) {
      if (ws->win) /* && IsWindowVisible(ws->win))*/
	 DestroyWindow(ws->win);
/*      ws->win = 0;*/
      if (ws->iconwin && ws->iconwin != ws->win) {
         if (IsWindowVisible(ws->iconwin))
	    DestroyWindow(ws->iconwin);
         else DestroyWindow(ws->iconwin);
	 }
/*      ws->iconwin = 0;*/
/*      while (ws->win)
	 if (pollevent() == -1) return -1;
*/
      if (ws->windowlabel) free(ws->windowlabel);
      if (ws->iconlabel) free(ws->iconlabel);
      if (ws->pix)
	 DeleteObject(ws->pix);
      ws->pix = 0;
      if (ws->iconpix)
	 DeleteObject(ws->iconpix);
      ws->iconpix = 0;
      if (ws->initialPix)
	 DeleteObject(ws->initialPix);
      ws->initialPix = 0;
      /* need to enumerate and specifically free each string */
      if (ws->menuMap) {
	 for(i=0;i<ws->nmMapElems;i++) free(ws->menuMap[i]);
         free(ws->menuMap);
         ws->menuMap = 0;
	 }
      free(ws->cursorname);
      if (ws->child) {
         for(i=0;i<ws->nChildren;i++) {
            free(ws->child[i].id);
            if (ws->child[i].font) DeleteObject(ws->child[i].font);
	    }
         free(ws->child);
	 }
      ws->child = 0;
      GRFX_UNLINK(ws, wstates);
      }
   return 0;
   }

/*
 * free a window context
 */
void free_context(wc)
wcp wc;
   {
   wc->refcount--;
   if(wc->refcount == 0) {
      if (wc->cliprgn)
	 DeleteObject(wc->cliprgn);
      wc->cliprgn = 0;
      if (wc->pattern)
	 DeleteObject(wc->pattern);
      wc->pattern = 0;
      if (wc->patternname)
	 free(wc->patternname);
      wc->patternname = 0;
      if (wc->fgname) free(wc->fgname);
      wc->fgname = 0;
      if (wc->bgname) free(wc->bgname);
      wc->bgname = 0;
      if (wc->font) {
	 if (wc->font->font)
	    DeleteObject(wc->font->font);
	 wc->font->font = 0;
	 if (wc->font->name)
	    free(wc->font->name);
	 wc->font->name = 0;
	 free(wc->font);
	 }
      wc->font = 0;
      GRFX_UNLINK(wc, wcntxts);
      }
   }

int walert(wbp w, int volume)
   {
   MessageBeep(0);
   }

int patbits[] = {
  0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
  0xFE,0xFF,0xEF,0xFF,0xFE,0xFF,0xEF,0xFF,
  0x77,0xDD,0x77,0xDD,0x77,0xDD,0x77,0xDD,
  0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA,
  0x11,0x44,0x11,0x44,0x11,0x44,0x11,0x44,
  0x01,0x00,0x10,0x00,0x01,0x00,0x10,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

  0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,
  0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,
  0x00,0x00,0x00,0xFF,0x00,0x00,0x00,0x00,
  0x10,0x10,0x10,0xFF,0x10,0x10,0x10,0x10,
  0x82,0x44,0x28,0x10,0x28,0x44,0x82,0x01,

  0x0F,0x0F,0x0F,0x0F,0xF0,0xF0,0xF0,0xF0,
  0x1B,0x18,0x81,0xB1,0x36,0x06,0x60,0x63,
  0x02,0x02,0x05,0xF8,0x20,0x20,0x50,0x8F,
  0x03,0x84,0x48,0x30,0x03,0x84,0x48,0x30,
};

/*
 * SetPattern
 */
int SetPattern(w, name, len)
wbp w;
char *name;
int len;
   {
   int width, nbits;
   int i, j;
   int symbol;
   C_integer v, bits[MAXXOBJS];
   HBITMAP p;
   char data[MAXXOBJS];
   char *buf = data;
   wcp wc = w->context;

   if (wc->patternname != NULL)
      free(wc->patternname);
   wc->patternname = malloc(len+1);
   strncpy(wc->patternname, name, len);
   wc->patternname[len] = '\0';

   /*
    * If the pattern starts with a number it is a width , bits encoding
    */
   if ((len > 0) && isdigit(name[0])) {
      nbits = MAXXOBJS;
      switch (parsepattern(name, len, &width, &nbits, bits)) {
      case Failed:
	 return Failed;
      case Error:
	 ReturnErrNum(145, Error);
	 }
      if (w->window->iconwin == NULL) return Succeeded;
      return SetPatternBits(w, width, bits, nbits);
      }

   /*
    * Otherwise, it is a named pattern.  Find the symbol id.
    */
   if ((symbol = si_s2i(siPatternSyms, wc->patternname)) >= 0) {
      if (w->window->iconwin == NULL) return Succeeded;
      for(i = 0; i < 8; i++) {
	 v = reversebits(~(patbits[symbol * 8 + i]));
	 *buf++ = v;
	 }
      p = CreateBitmapFromData(data);
      if (wc->pattern)
	 DeleteObject(wc->pattern);
      wc->pattern = p;
      if (wc->fillstyle == BS_PATTERN) {
	 wc->brush.lbStyle = BS_PATTERN;
	 wc->brush.lbColor = PALCLR(wc->fg);
	 wc->brush.lbHatch = (LONG)p;
	 }
      return Succeeded;
      }
   ReturnErrNum(145, Error);
   }

/*
 * Create an 8x8 bitmap from some data
 */
HBITMAP CreateBitmapFromData(char *data)
{
   WORD *wBits = alloc(8 * sizeof(WORD));
   HBITMAP rv;
   int i;
   static BITMAP bitmap = { 0, 8, 8, 2, 1, 1};
   for (i = 0; i < 8; i++)
      wBits[i] = data[i];
   bitmap.bmBits = (LPSTR) wBits;
   rv = CreateBitmapIndirect(&bitmap);
   free(wBits);
   return rv;
}


int SetPatternBits(w, width, bits, nbits)
wbp w;
int width;
C_integer *bits;
int nbits;
   {
   C_integer v;
   int i, j, k;
   HBITMAP p;
   char data[MAXXOBJS];
   char *buf = data;
   wcp wc = w->context;

   if (width != nbits)
      return Failed;

   if (width == 8) {
      for(i = 0; i < nbits; i++) {
         v = bits[i];
	 *buf++ = reversebits(~v);
	 }
      }
   else if (width == 4) {
      for(k=0; k < 2; k++) /* do twice to get 8 rows */
      for(i = 0; i < nbits; i++) {
         v = widenbits(bits[i]);
	 *buf++ = reversebits(~v);
	 }
      }
   else return Failed;

   p = CreateBitmapFromData(data);
   if (wc->pattern)
      DeleteObject(wc->pattern);
   wc->pattern = p;
   if (wc->fillstyle == BS_PATTERN) {
      wc->brush.lbStyle = BS_PATTERN;
      wc->brush.lbColor = PALCLR(wc->fg);
      wc->brush.lbHatch = (LONG)p;
      }
   return Succeeded;
   }

int widenbits(int c)
{
   int rv = c;
   if (c & 1) rv |= 16;
   if (c & 2) rv |= 32;
   if (c & 4) rv |= 64;
   if (c & 8) rv |= 128;
   return rv;
}

int reversebits(int c)
{
  int rv = 0;
  if (c & 1) rv |= 128;
  if (c & 2) rv |= 64;
  if (c & 4) rv |= 32;
  if (c & 8) rv |= 16;
  if (c & 16) rv |= 8;
  if (c & 32) rv |= 4;
  if (c & 64) rv |= 2;
  if (c & 128) rv |= 1;
  return rv;
}

int pixmap_init(w)
wbp w;
   {
   wsp ws = w->window;
   resizePixmap(w, ws->width, ws->height);
   return Succeeded;
   }


int do_config(w, status)
wbp w;
int status;
   {
   wsp ws = w->window;
   int wid = ws->width, ht = ws->height;
   int posx = ws->posx, posy = ws->posy;
   if (! resizePixmap(w, wid, ht))
      return Failed;
   if (ws->win) {
      pollevent();
      if (status == 3) {
	 SetWindowPos(ws->win, ws->win,
		      posx,
		      posy,
		      wid, ht, SWP_NOZORDER);
	 }
      else if (status == 2) {
	 SetWindowPos(ws->win, ws->win, 0, 0,
		      wid, ht, SWP_NOMOVE|SWP_NOZORDER);
         }
      else if (status == 1)
	 SetWindowPos(ws->win, ws->win,
		      posx,
		      posy,
		      0, 0, SWP_NOSIZE|SWP_NOZORDER);
      }
   else if (ws->iconwin) {
      if (status == 3) {
	 SetWindowPos(ws->iconwin, ws->iconwin,
		      posx,
		      posy,
		      wid, ht, SWP_NOZORDER);
	 }
      else if (status == 2) {
	 SetWindowPos(ws->iconwin, ws->iconwin, 0, 0,
		      wid, ht, SWP_NOMOVE|SWP_NOZORDER);
         }
      else if (status == 1)
	 SetWindowPos(ws->iconwin, ws->iconwin,
		      posx,
		      posy,
		      0, 0, SWP_NOSIZE|SWP_NOZORDER);
      }
   return Succeeded;
   }

DWORD playMIDIfile(HWND hWndNotify, LPSTR s)
{
   UINT wDeviceID;
   DWORD dwReturn;
   MCI_OPEN_PARMS mciOpenParms;
   MCI_PLAY_PARMS mciPlayParms;
   MCI_STATUS_PARMS mciStatusParms;
   MCI_SEQ_SET_PARMS mciSeqSetParms;

   mciOpenParms.lpstrDeviceType = "sequencer";
   mciOpenParms.lpstrElementName = s;
   if (dwReturn = mciSendCommand((UINT)NULL, MCI_OPEN,
				 MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
				 (DWORD)(LPVOID) &mciOpenParms)) {
      return dwReturn;
      }
   wDeviceID = mciOpenParms.wDeviceID;

   /* attempt to select the MIDI mapper */
   mciSeqSetParms.dwPort = MIDI_MAPPER;
   if (dwReturn = mciSendCommand(wDeviceID, MCI_SET, MCI_SEQ_SET_PORT,
				 (DWORD)(LPVOID) &mciSeqSetParms)) {
      /* could not select the MIDI mapper; play anyway */
      }

   mciPlayParms.dwCallback = (DWORD) hWndNotify;
   if (dwReturn = mciSendCommand(wDeviceID, MCI_PLAY, MCI_NOTIFY,
         (DWORD)(LPVOID) &mciPlayParms)) {
      mciSendCommand(wDeviceID, MCI_CLOSE, 0, (DWORD)NULL);
      return dwReturn;
      }

   return 0L;
}


int playmedia(wbp w, char *s)
{
   if (strstr(s, ".wav") || strstr(s, ".WAV")) {
      sndPlaySound((LPSTR) s, SND_ASYNC);
      return Succeeded;
      }
   else if (strstr(s, ".mid") || strstr(s, ".MID") ||
            strstr(s, ".rmi") || strstr(s, ".RMI")) {
      if (playMIDIfile(w->window->win, (LPSTR) s) == 0)
         return Succeeded;
      }
   /*
    * Interpret as an MCI command string
    */
   else {
      if (mciSendString(s, NULL, 0, 0L)) return Failed;
      return Succeeded;
      }
}

/*
 * UpdateCursorPos
 */
void UpdateCursorPos(wsp ws, wcp wc)
{
   if (ISCURSORONW(ws)) {
      if (ws->hasCaret) {
         }
      CreateCaret(ws->iconwin, NULL, FWIDTHC(wc), FHEIGHTC(wc));
      SetCaretBlinkTime(500);
      SetCaretPos(ws->x, ws->y - ASCENTC(wc));
      ShowCaret(ws->iconwin);
      ws->hasCaret = 1;
      }
}

int resizePixmap(wbp w, int width, int height)
   {
   HDC hdc, hdc2, hdc3;
   HBITMAP newpix, oldpix, oldpix2;
   HBRUSH hb;
   LOGBRUSH lb;
   XRectangle rect;
   wsp ws = w->window;
   int x = ws->pixwidth, y = ws->pixheight;
   if (ISEXPOSED(w)) {
      if (ws->pixwidth >= width && ws->pixheight >= height) {
         return 1;
         }
      ws->pixheight = max(ws->pixheight, height);
      ws->pixwidth = max(ws->pixwidth, width);
      }
   else {
      ws->pixwidth = width;
      ws->pixheight = height;
      }
   hdc = GetDC(ws->iconwin);
   newpix = CreateCompatibleBitmap (hdc, ws->pixwidth, ws->pixheight);
   if (ws->pix) {
      hdc2 = CreateCompatibleDC(hdc);
      oldpix = SelectObject(hdc2, ws->pix);
      }
   hdc3 = CreateCompatibleDC(hdc);
   oldpix2 = SelectObject(hdc3, newpix);
   if (palette) {
      SelectPalette(hdc, palette, FALSE);
      if (ws->pix) SelectPalette(hdc2, palette, FALSE);
      SelectPalette(hdc3, palette, FALSE);
      RealizePalette(hdc);
      if (ws->pix) RealizePalette(hdc2);
      RealizePalette(hdc3);
      }
   lb.lbStyle = BS_SOLID;
   lb.lbColor = PALCLR(w->context->bg);
   hb = CreateBrushIndirect(&lb);
   /*
    * initialize the new pixmap, including areas not in the old pixmap.
    */
   rect.left = 0; rect.right = ws->pixwidth;
   rect.top = 0; rect.bottom = ws->pixheight;
   FillRect(hdc3, &rect, hb);
   if (ws->win)
      FillRect(hdc, &rect, hb);

   if (ws->pix) BitBlt(hdc3, 0, 0, x - 2, y - 1, hdc2, 0, 0, SRCCOPY);
   if (ws->win)
      BitBlt(hdc, 0, 0, ws->pixwidth, ws->pixheight, hdc3, 0, 0, SRCCOPY);
   SelectObject(hdc3, oldpix2);
   DeleteDC(hdc3);
   if (ws->pix) {
      SelectObject(hdc2, oldpix);
      DeleteDC(hdc2);
      }
   ReleaseDC(ws->iconwin, hdc);
   if (ws->pix) DeleteObject(ws->pix);
   DeleteObject(hb);
   ws->pix = newpix;
   return 1;
   }

/*
 * CreateWinDC - create a device context for drawing on the window
 *  In addition, select objects specified by flags.
 */
HDC CreateWinDC(wbp w)
   {
   wsp ws = w->window;
   wcp wc = w->context;
   HDC hdc = GetDC(ws->iconwin);
   if (numColors > 0) {
      SelectPalette(hdc, palette, FALSE);
/*      UnrealizeObject(palette); */
      RealizePalette(hdc);
      if (numRealized < numColors) {
         numRealized = numColors;
         if (RealizePalette(hdc) == 0) /* noop */;
         }
      }
   SetROP2(hdc, wc->drawop);
   if (wc->clipw >= 0){
      SelectClipRgn(hdc, wc->cliprgn);
      }
   return hdc;
   }

HDC CreatePixDC(wbp w, HDC hdc)
   {
   wsp ws = w->window;
   wcp wc = w->context;
   HBITMAP oldpix;
   HDC hdc2 = CreateCompatibleDC(hdc);
   if (numColors > 0) {
      SelectPalette(hdc2, palette, FALSE);
      RealizePalette(hdc2);
      }
/*   ws->initialPix = */ ws->theOldPix = SelectObject(hdc2, ws->pix);
   SetROP2(hdc2, wc->drawop);
   if (wc->clipw >= 0){
      SelectClipRgn(hdc2, wc->cliprgn);
      }
   return hdc2;
   }

int dc_maxcharwidth(HDC dc)
{
  int i, m = -1, x;
  char s[2];
  s[1] = '\0';
  for (i=0; i<256; i++) {
    s[0] = i;
    x = dc_textwidth(dc, s, 1);
    if (x > m) m = x;
  }
  return m;
}

/*
 * compute a text width for a current device context (typically pixdc)
 */
int dc_textwidth(HDC dc, char *s, int n)
{
   SIZE sz;
   /*
    * GetTextExtentPoint32(dc, s, n, &sz) gives incorrect behavior
    *  under Win32s
    */
   GetTextExtentPoint(dc, s, n, &sz);
   return (int)sz.cx;
}

int sysScrollWidth()
{
   return GetSystemMetrics(SM_CXVSCROLL);
}

int sysFontHeight(wbp w)
{
   TEXTMETRIC tm;
   int rv;
   wsp ws = w->window;
   HDC dc = GetDC(ws->iconwin);
   HFONT oldfont = SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT));
   GetTextMetrics(dc, &tm);
   SelectObject(dc, oldfont);
   ReleaseDC(ws->iconwin, dc);
   return tm.tmHeight + tm.tmExternalLeading;
}

int sysTextWidth(wbp w, char *s, int n)
{
   int rv;
   wsp ws = w->window;
   HDC dc = GetDC(ws->iconwin);
   HFONT oldfont;
   oldfont = SelectObject(dc, GetStockObject(SYSTEM_FIXED_FONT));
   rv = dc_textwidth(dc, s, n);
   SelectObject(dc, oldfont);
   ReleaseDC(ws->iconwin, dc);
   return rv;
}

int textWidth(wbp w, char *s, int n)
   {
   int rv;
   wsp ws = w->window;
   HDC stddc = GetDC(ws->iconwin);
   HFONT oldfont = SelectObject(stddc, w->context->font->font);
   rv = dc_textwidth(stddc, s, n);
   SelectObject(stddc, oldfont);
   ReleaseDC(ws->iconwin, stddc);
   return rv;
   }

void warpPointer(w, x, y)
wbp w;
int x, y;
   {
   wsp ws = w->window;
   SetCursorPos(ws->posx + x, ws->posy + y);
   }

/*
 * free all Windows resources allocated by this instantiation of iconx
 */
void wfreersc()
{
   wbp w;
   extern struct palentry *palsetup_palette;
   while (wbndngs != NULL) {
      w = wbndngs;
      wbndngs = wbndngs->next;
      free(w);
      }
   while (wstates != NULL) {
      wstates->refcount = 1;
      free_window(wstates);
      }
   while (wcntxts != NULL) {
      wcntxts->refcount = 1;
      free_context(wcntxts);
      }
   if (palette) {
      DeleteObject(palette);
      palette = 0;
      }
   if (palsetup_palette) {
      free(palsetup_palette);
      palsetup_palette = 0;
      }
   if (scp) {
      free(scp);
      scp = 0;
      }
   if (wlhead)
      wlfree();
   mciSendCommand(MCI_ALL_DEVICE_ID, MCI_CLOSE, 0, (DWORD)NULL);
}


/*
 * Native Windows UI facilities
 */
void makebutton(wsp ws, childcontrol *cc, char *s)
{
   cc->type = CHILD_BUTTON;
   cc->font = 0;
   cc->id = salloc(s);
   cc->win = CreateWindow("button", cc->id,
	         WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
		 0, 0, 0, 0, ws->iconwin, (HMENU)ws->nChildren, mswinInstance,
		 NULL);
}

void makescrollbar(wsp ws, childcontrol *cc, char *s, int i1, int i2)
{
   cc->type = CHILD_SCROLLBAR;
   cc->id = salloc(s);
   cc->font = 0;
   cc->win = CreateWindow("scrollbar", cc->id,
            WS_CHILD | WS_VISIBLE | SBS_VERT, 0, 0, 0, 0,
	    ws->iconwin, (HMENU)ws->nChildren, mswinInstance, NULL);
   SetScrollRange(cc->win, SB_CTL, i1, i2, FALSE);
}

int nativemenubar(wbp w, int total, int argc, dptr argv, int warg, dptr d)
{
   wsp ws;
   tended struct b_list *hp;
   HMENU tempMenu, tempMenu2 = NULL;
   tended char *s, *s2;
   int r, i;
   ws = w->window;

   if (ws->nmMapElems)
      tempMenu2 = ws->menuBar;

   ws->menuBar = CreateMenu();
   ws->nmMapElems = total;
   total = 0;
   while (warg < argc){
      /*
       * each argument must be a list of strings
       */
      hp = (struct b_list *)BlkLoc(argv[warg]);
      r = hp->size;
      /*
       * Construct a Windows menu corresponding to the Icon list
       */
      tempMenu = CreateMenu();
      for(i=0; i < r; i++) {
         c_get(hp, d);
         if (!is:string(*d)) return Error;
         if (!cnv:C_string(*d, s)) return Error;
         s = strdup(s);
         if (i == 0) s2=s;
         else
            AppendMenu(tempMenu, MF_STRING, total, s);
         ws->menuMap[total++] = s;
         c_put(&(argv[warg]), d);
         }
      AppendMenu(ws->menuBar, MF_POPUP, (unsigned int)tempMenu, s2);
      warg++;
      }
   /*
    * Insert the menu into the window
    */
   if (ws->win) SetMenu(ws->win, ws->menuBar);
   if (tempMenu2) {
      int i, n = GetMenuItemCount(tempMenu2);
      for (i=0; i < n; i++) {
         DestroyMenu(GetSubMenu(tempMenu2, i));
         }
      DestroyMenu(tempMenu2);
      }
   return Succeeded;
}

void makeeditregion(wbp w, childcontrol *cc, char *s)
{
   wsp ws = w->window;
   cc->type = CHILD_EDIT;
   cc->id = salloc(s);
   cc->win = CreateWindow("edit", NULL,
		WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |
		WS_BORDER | ES_LEFT | ES_MULTILINE |
		ES_AUTOHSCROLL | ES_AUTOVSCROLL,
		0, 0, 0, 0, ws->iconwin,
		(HMENU) ws->nChildren, mswinInstance, NULL);
   setchildfont(cc, w->context->font->name);
}

void cleareditregion(childcontrol *cc)
{
   SendMessage(cc->win, WM_CLEAR, 0, 0);
}

void copyeditregion(childcontrol *cc)
{
   SendMessage(cc->win, WM_COPY, 0, 0);
}

void cuteditregion(childcontrol *cc)
{
   SendMessage(cc->win, WM_CUT, 0, 0);
}

void pasteeditregion(childcontrol *cc)
{
   SendMessage(cc->win, WM_PASTE, 0, 0);
}

int undoeditregion(childcontrol *cc)
{
   if (!SendMessage(cc->win, WM_UNDO, 0, 0)) return Failed;
   return Succeeded;
}

int modifiededitregion(childcontrol *cc)
{
   if (!SendMessage(cc->win, EM_GETMODIFY, 0, 0)) return Failed;
   return Succeeded;
}

int setmodifiededitregion(childcontrol *cc, int i)
{
   SendMessage(cc->win, EM_SETMODIFY, i, 0);
   return Succeeded;
}

void geteditregion(childcontrol *cc, dptr d)
{
   int y = GetWindowTextLength(cc->win);
   char *s2 = alcstr(NULL, y + 1);
   GetWindowText(cc->win, s2, y+1);
   StrLoc(*d) = s2;
   StrLen(*d) = y;
}

void seteditregion(childcontrol *cc, char *s2)
{
   SetWindowText(cc->win, s2);
}


void movechild(childcontrol *cc,
	       C_integer x, C_integer y, C_integer width, C_integer height)
{
   MoveWindow(cc->win, x, y, width, height, TRUE);
}

int setchildfont(childcontrol *cc, char *fontname)
{
   HFONT hf;
   RECT rect;
   if (hf = mkfont(fontname)) {
      SendMessage(cc->win, WM_SETFONT, (WPARAM)hf, 0);
      if (cc->font) DeleteObject(cc->font);
      cc->font = hf;
      GetClientRect(cc->win, &rect);
      InvalidateRect(cc->win, &rect, TRUE);
      return Succeeded;
      }
   return Failed;
}

void setfocusonchild(wsp ws, childcontrol *cc, int width, int height)
{
   if (width || height) {
      SetFocus(cc->win);
      ws->focusChild = cc->win;
      }
   else ws->focusChild = 0;
}

void setchildselection(wsp ws, childcontrol *cc, int x, int y)
{
   int iLine = SendMessage(cc->win, EM_LINEFROMCHAR, x-1,0);
   int topLine = SendMessage(cc->win, EM_GETFIRSTVISIBLELINE, 0, 0);
   if (topLine != iLine) {
      SendMessage(cc->win, EM_LINESCROLL, 0, iLine-topLine);
      }
   SendMessage(cc->win, EM_SETSEL, x - 1, y - 1);
   SetFocus(cc->win);
   ws->focusChild = cc->win;
}

CHOOSEFONT cf;
LOGFONT lf;

int nativefontdialog(wbp w, char *buf, int flags, int fheight)
{
   strcpy(lf.lfFaceName, buf);
   lf.lfHeight = fheight;
   lf.lfWidth = 0;
   lf.lfEscapement = 0;
   if (!strcmp(lf.lfFaceName, "mono") || !strcmp(lf.lfFaceName, "fixed")){
      strcpy(lf.lfFaceName, "Lucida Sans Typewriter");
      flags |= FONTFLAG_MONO + FONTFLAG_SANS;
      }
   else if (!strcmp(lf.lfFaceName, "typewriter")) {
      strcpy(lf.lfFaceName, "courier");
      flags |= FONTFLAG_MONO + FONTFLAG_SERIF;
      }
   else if (!strcmp(lf.lfFaceName, "sans")) {
      strcpy(lf.lfFaceName, "swiss");
      flags |= FONTFLAG_PROPORTIONAL + FONTFLAG_SANS;
      }
   else if (!strcmp(lf.lfFaceName, "serif")) {
      strcpy(lf.lfFaceName, "roman");
      flags |= FONTFLAG_PROPORTIONAL + FONTFLAG_SERIF;
      }

   if (flags & FONTFLAG_BOLD) lf.lfWeight = FW_BOLD;
   else
      lf.lfWeight = FW_DONTCARE;
   if (flags & FONTFLAG_ITALIC) lf.lfItalic = 1;
   lf.lfUnderline = lf.lfStrikeOut = 0;
   lf.lfCharSet =
	 ((MAXBYTESPERCHAR==1)?ANSI_CHARSET:DEFAULT_CHARSET);
   lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
   lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
   lf.lfQuality = DEFAULT_QUALITY;
   if (FONTFLAG_PROPORTIONAL)
      lf.lfPitchAndFamily = VARIABLE_PITCH;
   else if (FONTFLAG_MONO)
      lf.lfPitchAndFamily = FIXED_PITCH;
   else
      lf.lfPitchAndFamily = DEFAULT_PITCH;
   if (!strcmp(lf.lfFaceName, "swiss")) lf.lfPitchAndFamily |= FF_SWISS;
   else if (!strcmp(lf.lfFaceName, "roman"))
      lf.lfPitchAndFamily |= FF_ROMAN;
   else
      lf.lfPitchAndFamily |= FF_DONTCARE;

   memset(&cf, 0, sizeof(CHOOSEFONT));
   cf.lStructSize = sizeof(CHOOSEFONT);
   cf.hwndOwner   = w->window->iconwin;
   cf.lpLogFont = &lf;
   cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;
   cf.rgbColors = RGB(0,0,0);
   cf.nFontType = SCREEN_FONTTYPE;
   if (ChooseFont(&cf) == 0) return Failed;
   sprintf(buf, "%s,%d%s%s", lf.lfFaceName,
           ((lf.lfHeight > 0) ? lf.lfHeight : -lf.lfHeight),
           (lf.lfItalic ? ",italic" : ""),
	   ((lf.lfWeight > 500) ? ",bold" : ""));
   return Succeeded;
}

/*
 * common dialog functions
 */
COLORREF aclrCust[16];
CHOOSECOLOR cc;

char *nativecolordialog(wbp w, long r, long g, long b, char *buf)
{
   aclrCust[0] = RGB(255,255,255);
   aclrCust[1] = RGB(239,239,239);
   aclrCust[2] = RGB(223,223,223);
   aclrCust[3] = RGB(207,207,207);
   aclrCust[4] = RGB(191,191,191);
   aclrCust[5] = RGB(175,175,175);
   aclrCust[6] = RGB(159,159,159);
   aclrCust[7] = RGB(143,143,143);
   aclrCust[8] = RGB(127,127,127);
   aclrCust[9] = RGB(111,111,111);
   aclrCust[10] = RGB(95,95,95);
   aclrCust[11] = RGB(79,79,79);
   aclrCust[12] = RGB(63,63,63);
   aclrCust[13] = RGB(47,47,47);
   aclrCust[14] = RGB(31,31,31);
   aclrCust[15] = RGB(15,15,15);
   memset(&cc, 0, sizeof(CHOOSECOLOR));
   cc.lStructSize = sizeof(CHOOSECOLOR);
   cc.hwndOwner   = w->window->iconwin;
   cc.lpCustColors = aclrCust;
   cc.rgbResult = mscolor(w, r, g, b);
   cc.Flags = CC_FULLOPEN | CC_RGBINIT;
   if (ChooseColor(&cc) == 0) {
      return NULL;
      }
   sprintf(buf, "%d,%d,%d", (RED(cc.rgbResult)<<8) | 0xFF,
      (GREEN(cc.rgbResult) << 8) | 0xFF,
      (BLUE(cc.rgbResult) << 8) | 0xFF);
   return buf;
}




char *nativeselectdialog(wbp w, struct b_list *L, char *s)
{
   int i, j, okflag=0, yesnoflag=0, cancelflag=0, retryflag=0, otherflag=0;
   tended struct b_list *hp = L;
   tended char *s1 = NULL;
   tended struct descrip d, d2;
   char s3[8];
   wsp ws = w->window;
   int lsize;

   if (hp == NULL) {
      okflag = 1;
      }
   else {
      BlkLoc(d2) = (union block *)hp;
      d2.dword = D_List;
      lsize  = hp->size;

      for(i=0; i < lsize; i++) {
         c_get(hp, &d);
         if (!cnv:C_string(d, s1)) return NULL;
         for(j=0; j<8; j++) {
            s3[j] = tolower(s1[j]);
            if (s3[j] == '\0') break;
            }
         if (!strcmp(s3, "ok")) okflag = 1;
         else if (!strcmp(s3, "okay")) okflag = 1;
         else if (!strcmp(s3, "no")) yesnoflag = MB_YESNO;
         else if (!strcmp(s3, "yes")) yesnoflag = MB_YESNO;
         else if (!strcmp(s3, "cancel")) cancelflag++;
         else if (!strcmp(s3, "retry")) retryflag = MB_RETRYCANCEL;
         else { otherflag++; return NULL; }
         c_put(&d2, &d);
         }
      }
   /*
    * validate flags
    */
   if (okflag && yesnoflag) return NULL;
   if (okflag && retryflag) return NULL;
   if (yesnoflag && retryflag) return NULL;
   if (retryflag && !cancelflag) return NULL;

   if (cancelflag) {
      if (okflag) {
         okflag = MB_OKCANCEL;
         }
      else if (yesnoflag) yesnoflag = MB_YESNOCANCEL;
      }
    else if (okflag) okflag = MB_OK;

   j = MessageBox((ws->focusChild ? ws->focusChild :
		  (ws->win ? ws->win : ws->iconwin)),
           s, " ",
          okflag | yesnoflag | retryflag
          | (strchr(s, '!') ? MB_ICONEXCLAMATION :
	     (strchr(s, '?') ? MB_ICONQUESTION : MB_ICONASTERISK)));

   switch (j) {
   case IDOK: return "Okay";
   case IDCANCEL: return "Cancel";
   case IDYES: return "Yes";
   case IDNO: return "No";
   case IDRETRY: return "Retry";
   default: return NULL;
      }
}

OPENFILENAME ofn;

char *nativeopendialog(wbp w, char *s1, char *s2, char *s3, int i, int j)
{
   char buf[128], buf2[64];

   memset(&ofn, 0, sizeof(OPENFILENAME));
   ofn.lStructSize = sizeof(OPENFILENAME);
   ofn.hwndOwner   = w->window->iconwin;
   ofn.lpstrFilter = s3;
   ofn.nFilterIndex = j;
   strcpy(buf, s2);
   ofn.lpstrFile = buf;
   ofn.nMaxFile = sizeof(buf);
   ofn.lpstrTitle = s1;
   ofn.lpstrFileTitle = buf2;
   ofn.nMaxFileTitle = sizeof(buf2);
   ofn.lpstrInitialDir = NULL;
   ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST;
   if (GetOpenFileName(&ofn) == 0) return NULL;
   return ofn.lpstrFile;
}


char *nativesavedialog(wbp w, char *s1, char *s2, char *s3, int i, int j)
{
   char buf[128], buf2[64];
   /*
    * Use the standard dialog to obtain a filename.
    */
   memset(&ofn, 0, sizeof(OPENFILENAME));
   ofn.lStructSize = sizeof(OPENFILENAME);
   ofn.hwndOwner   = w->window->iconwin;
   ofn.lpstrFilter = s3;
   ofn.nFilterIndex = j;
   strcpy(buf, s2);
   ofn.lpstrFile = buf;
   ofn.nMaxFile = sizeof(buf);
   ofn.lpstrTitle = s1;
   ofn.lpstrFileTitle = buf2;
   ofn.nMaxFileTitle = sizeof(buf2);
   ofn.lpstrInitialDir = NULL;
   ofn.Flags = OFN_SHOWHELP | OFN_PATHMUSTEXIST;
   if (GetSaveFileName(&ofn) == 0) return NULL;
   return ofn.lpstrFile;
}

/*
 * flush a window - noop under Windows
 */
void wflush(w)
wbp w;
   {
   }

#endif					/* Graphics */
