/*
 * Created 03.Oct.98 by Cyrus Patel <cyp@fb14.uni-mainz.de>
 * Copyright distributed.net 1997-2000 - All Rights Reserved
 * For use in distributed.net projects only.
 * Any other distribution or use of this source violates copyright.
*/
const char *w32cons_cpp(void) {
return "@(#)$Id: w32cons.cpp,v 1.51.2.48 2000/02/20 15:33:00 jlawson Exp $"; }

//#define TRACE

//define any/all/some of the following to TRACE_OUT(x) for sectional tracing
#define PAINTTRACE_OUT(x) //TRACE_OUT(x)
#define FLOWTRACE_OUT(x)  //TRACE_OUT(x)
#define CONFTRACE_OUT(x)  //TRACE_OUT(x)
#define FONTTRACE_OUT(x)  //TRACE_OUT(x)
#define TRAYTRACE_OUT(x)  //TRACE_OUT(x)
#define ADJRECTTRACE_OUT(x) //TRACE_OUT(x)
#define INITTRACE_OUT(x)  //TRACE_OUT(x)
#define PIPETRACE_OUT(x)  //TRACE_OUT(x)

#include "cputypes.h"
#include "baseincs.h"
#include "clitime.h"  // CliGetTimeString(&tv,4),GetBuildDate()
#include "console.h"  // for CLICONS_[SHORT|LONG]NAME, ConOut, ConInKey
#include "cliident.h" // CliGetFullVersionDescriptor()
#include "clievent.h" // event handler
#include "triggers.h" // for clisetupsignals
#include "logstuff.h" // Log()
#include "setprio.h"  // SetGlobalPriority();
#include "util.h"     // utilGetAppName()/TRACE
#include "client.h"   // modereq needs client
#include "probfill.h" //LoadSaveProblems(), PROBFILL_UNLOADALL
#define __w16ClientHardStop() LoadSaveProblems(NULL,0,PROBFILL_UNLOADALL)
#include "modereq.h"  // "modes": options
#include "client.h"   // "modes": CONTEST_COUNT for bench
#include "problem.h"  // "modes": IsProblemLoadPermitted before bench
#include "clicdata.h" // "modes": CliGetContestNameFromID() names for bench
#include "w32svc.h"   // win9x win32CliInitializeService()
#include "w32util.h"  // winGetVersion(), winIsGUIExecutable()
#include "w32cons.h"  // ourselves

#if (CLIENT_OS == OS_WIN32) /* for _open_osfhandle as used with pipes */
#include <io.h>
#include <fcntl.h>
#endif

/* --------- message extensions (also see .h for public ones) --------- */
#define WM_USER_W16CONS              (WM_USER+1)
   // command message constants
   #define W16CONS_CMD_CLEARSCREEN   0x01
   #define W16CONS_CMD_PRINTSTR      0x02
   #define W16CONS_CMD_ISKBHIT       0x03
   #define W16CONS_CMD_GETCH         0x04
   #define W16CONS_CMD_SETPOS        0x05
   #define W16CONS_CMD_GETPOS        0x06
   #define W16CONS_CMD_GETSIZE       0x07
   #define W16CONS_CMD_INDIRDESTROY  0x08 /* indirect WM_DESTROY */
   #define W16CONS_CMD_ECHOLPARAM    0x09 /* just return LPARAM (to ident)*/
#define WM_USER_SHELLNOTIFYICON      (WM_USER+10) /* for tray icon */
   // internal WM_COMMAND wParams,
   // public ones are 0.... (DNETC_WCMD_INTERNAL_FIRST-1)
   // See w32cons.h for list
   #define WMCMD_BENCHMARK            DNETC_WCMD_INTERNAL_FIRST /* 512 */
   #define WMCMD_CONFIG               (WMCMD_BENCHMARK+2+(CONTEST_COUNT*2))
   #define WMCMD_UPDATE               (WMCMD_CONFIG+1)
   #define WMCMD_FETCH                (WMCMD_UPDATE+1)
   #define WMCMD_FLUSH                (WMCMD_FETCH+1)
   #define WMCMD_SVCINSTALL           (WMCMD_FLUSH+1)
   #define WMCMD_LAGGYRESTORE         (WMCMD_SVCINSTALL+1)
   #define WMCMD_PASTE   WM_PASTE     /* 0x0302 */
   #define WMCMD_COPY    WM_COPY
   #define WMCMD_RESTORE SC_RESTORE   /* 0xF120 */

/* ------------------------------------------------------------------ */

//#define USE_NATIVE_CONSOLEIO //define this to force win32 native console i/o

// define to the dimensions of the virtual screen
#define W16CONS_WIDTH       80
#define W16CONS_HEIGHT      25

// undefine the following to allow the window to be resized
//#define W16CONS_FIXEDSIZE

// define to maximum keyboard buffer size
#define W16CONS_KEYBUFF     128

// smooth font scaling or not (also depends on system capabilities of course)
//#define W16CONS_SMOOTHSIZING

// colors (define to use user-defined color settings)
#define W16CONS_NATIVECOLORS
#ifdef W16CONS_NATIVECOLORS
  #define W16CONS_FORECOLOR GetSysColor(COLOR_WINDOWTEXT)
  #define W16CONS_BACKCOLOR GetSysColor(COLOR_WINDOW)
#else
  #define W16CONS_FORECOLOR RGB( 255,255,255 )
  #define W16CONS_BACKCOLOR RGB(0,0,128 )
#endif

/* --------------------------------------------------------------------- */

#if (CLIENT_OS == OS_WIN16)
  #define SetForegroundWindow BringWindowToTop
#endif

/* ---------------------------------------------------- */

// data structure associated with the window
struct W16ConsoleStruc
{
  HWND hwnd;
  char buff[W16CONS_HEIGHT][W16CONS_WIDTH];
  int have_marked;    // bool
  int have_dragthreshold;   // bool
  int mark_down;      // bool
  int mark_lastrow;
  int mark_ignorenextlbdown;
  char marked_buff[W16CONS_HEIGHT];
  int currow, curcol;
  struct { int shift:1, alt:1, ctrl:1; } kbflags;
  int keybuff[W16CONS_KEYBUFF];
  int keycount;
  HFONT hfont;
  int fontisstock;
  int fontx, fonty;
  int indentx, indenty;
  int dispcols, disprows;
  int painting;
  int smoothsizing;
  UINT ssmessage;
  //struct { int intray, verasst; char tip[64]; } traydata;
  struct { int top, left, fx, fy, state; } lastpos;
  UINT dnetc_cmdmsg;
  RECT lastpaintrect;
  HICON hSmallIcon;
};

/* ------------------------------------------------ */

static struct
{
  HWND    hwndList[1];
  char    szClassName[32];
  HANDLE  hmutex;
  int     hidden;
  DWORD   nCmdShow;
  FILE    *fstdout;
  FILE    *fstdin;
  int     iconisstock;
  HICON   hIcon;
  int     createflag;
  int     errorcode;
  int     asthread;
  int     debugon;
  int     nativecons;
  int     client_run_startstop_level; //inc on ClientRun start, dec on stop
  void   *devpipe;   /* named pipe (r/w) or output end of anon pipe */
  void   *devpipein; /* NULL if named pipe or input end of anon pipe */
  HWND    shimwatcher;
} constatics =
{
  {NULL},   //hwndList[1];
  {0},      //szClassName[32];
  NULL,     //hmutex;
  0,        //hidden;
  0,        //nCmdShow;
  NULL,     //FILE *fstdout;
  NULL,     //FILE *fstdin;
  0,        //iconisstock;
  NULL,     //hIcon;
  0,        //createflag;
  0,        //errorcode;
  0,        //asthread;
  0,        //debugon;
  0,        //nativecons;
  0,        //client_run_startstop_level,
  NULL,     //devpipe;
  NULL,     //devpipein;
  NULL      //shimwatcher;
};

/* ------------------------------------------------ */

typedef struct W16ConsoleStruc * W16CONP;

#define W16CONS_ERR_CREATETHREAD 1
#define W16CONS_ERR_NOFONT       2
#define W16CONS_ERR_NOMEM        3
#define W16CONS_ERR_CREATEWIN    4
#define W16CONS_ERR_REGCLASS     5
#define W16CONS_ERR_GETINST      6
#define W16CONS_ERR_NOSLOT       7
#define W16CONS_ERR_NCCREATE     8

/* ---------------------------------------------------- */

void __w16writelog( const char *format, ... )
{
  static char *openmode = "w";
  va_list argptr;
  FILE *f;
  va_start(argptr, format);
  if (strlen(format)>=6 && memcmp(format,"callee",6)==0)
  {
    f = fopen("debug.log","a+");
    if (f)
    {
      openmode = "a+";
      fclose(f);
    }
  }
  f = fopen("debug.log",openmode);
  if (f)
  {
    openmode = "a+";
    vfprintf( f, format, argptr );
    if (*format)
      format += (strlen(format)-1);
    if (*format != '\n')
      fwrite("\n",1,1,f);
    fflush(f);
    fclose(f);
  }
  va_end(argptr);
  return;
}

#if (CLIENT_OS == OS_WIN32)
void __w16showerror(const char *caption)
{
  LPVOID lpMsgBuf;
  FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER |
     FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
     GetLastError(),  0 /*MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT)*/,
     (LPTSTR) &lpMsgBuf, 0, NULL );// Process any inserts in lpMsgBuf.
  __w16writelog( "%s: %s\n", caption, lpMsgBuf );
  // w32ConOutModal((const char *)lpMsgBuf );
  LocalFree( lpMsgBuf );
  return;
}
#endif

/* =============================================================== */

static void __event_handler( int event_id /* all events */, long parm )
{
  //__w16writelog( "saw event: %d, parm: 0x%x", event_id, parm );
  if ( event_id == CLIEVENT_CLIENT_RUNSTARTED)
    constatics.client_run_startstop_level++;
  else if (event_id == CLIEVENT_CLIENT_RUNFINISHED)
    constatics.client_run_startstop_level--;
  parm = parm;
}

/* =============================================================== */

static W16CONP __win16GetHwndConsole( HWND hwnd )
{
  #if (CLIENT_OS == OS_WIN32)
  return (W16CONP)GetWindowLong(hwnd, GWL_USERDATA);
  #else
  return (W16CONP)GetWindowLong(hwnd, 0);
  #endif
}

static W16CONP __win16SetHwndConsole( HWND hwnd, W16CONP console )
{
  #if (CLIENT_OS == OS_WIN32)
  return (W16CONP)SetWindowLong(hwnd, GWL_USERDATA, (LONG)console);
  #else
  return (W16CONP)SetWindowLong(hwnd, 0, (LONG)console);
  #endif
}

/* ---------------------------------------------------- */

#if 0
static int __win16AssertHidden(HWND hwnd)
{
  if (constatics.hidden && (IsWindowVisible(hwnd) || IsWindowEnabled(hwnd)))
  {
    SetWindowPos(hwnd,HWND_BOTTOM,0,0,0,0,
        (SWP_HIDEWINDOW|SWP_NOREDRAW|SWP_NOACTIVATE));
    RECT rect;
    GetWindowRect(hwnd,&rect);
    rect.right-=rect.left;
    rect.bottom-=rect.top;
    SetWindowPos(hwnd, NULL,-rect.right, -rect.bottom,
       rect.right, rect.bottom, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOREDRAW);
    #if 0
    hwnd = GetTopWindow(GetDesktopWindow());
    if (hwnd)
    {
      EnableWindow(hwnd,1);
      SetActiveWindow(hwnd);
    }
    #endif
    return 1;
  }
  return 0;
}
#endif

/* ---------------------------------------------------------------------- */

void __conssize_saveupdateload(W16CONP console, HWND hwnd,
                               int saveupdateload /*-1,0,+1*/)
{
  if (console != NULL)
  {
    RECT rect;
    const char *sect = "client";
    if (saveupdateload <= 0) /* refresh or save */
    {
      if (!IsWindow(hwnd))
        ;
      else if (IsIconic(hwnd))
        console->lastpos.state = WS_MINIMIZE;
      else if (IsZoomed(hwnd))
        console->lastpos.state = WS_MAXIMIZE;
      else
      {
        console->lastpos.fy = console->fonty;
        console->lastpos.fx = console->fontx;
        GetWindowRect(hwnd,&rect);
        console->lastpos.top = rect.top;
        console->lastpos.left = rect.left;
        console->lastpos.state = 0;

        CONFTRACE_OUT((0,"conf: update pos: %d,%d\n",console->lastpos.left,console->lastpos.top));
        CONFTRACE_OUT((0,"conf: update fxy: %d,%d\n",console->lastpos.fx,console->lastpos.fy));
      }
      if (saveupdateload < 0) /* save */
      {
        CONFTRACE_OUT((0,"conf: save pos: %d,%d\n",console->lastpos.left,console->lastpos.top));
        CONFTRACE_OUT((0,"conf: save fxy: %d,%d\n",console->lastpos.fx,console->lastpos.fy));
        WriteDCTIProfileInt(sect, "fx", console->lastpos.fx );
        WriteDCTIProfileInt(sect, "fy", console->lastpos.fy );
        WriteDCTIProfileInt(sect, "wtop", console->lastpos.top );
        WriteDCTIProfileInt(sect, "wleft", console->lastpos.left );
        WriteDCTIProfileInt(sect, "state", console->lastpos.state );
      }
    }
    else /* (saveupdateload > 0) */  /* restore */
    {
      int maxwidth  = (int)GetSystemMetrics(SM_CXSCREEN); //width
      int maxheight = (int)GetSystemMetrics(SM_CYSCREEN); //height

      console->lastpos.state = GetDCTIProfileInt(sect, "state", 0 );
      if (console->lastpos.state != WS_MINIMIZE &&
          console->lastpos.state != WS_MAXIMIZE)
        console->lastpos.state = 0;

      if (maxwidth < 700) /* 640x480 */
      {
        console->fontx =  6;
        console->fonty = 10;
      }
      else if (maxwidth < 900) /* 800x600 */
      {
        console->fontx =  8;
        console->fonty = 14;
      }
      else
      {
        console->fontx =  9;
        console->fonty = 15;
      }
      rect.top = GetDCTIProfileInt( sect, "fy", console->fonty );
      rect.left = GetDCTIProfileInt( sect, "fx", console->fontx );

      if (rect.top > 1 && rect.top <= (maxheight/W16CONS_HEIGHT) &&
         rect.left > 1 && rect.left <= (maxwidth/W16CONS_WIDTH))
      {
        console->fonty = rect.top;
        console->fontx = rect.left;
      }

      console->lastpos.fx = console->fontx;
      console->lastpos.fy = console->fonty;

      rect.top = GetDCTIProfileInt(sect, "wtop", maxheight+1 );
      rect.left = GetDCTIProfileInt(sect, "wleft", maxwidth+1 );

      CONFTRACE_OUT((0,"conf: load pos: %d,%d\n",rect.left,rect.top));

      /* just some basic validation for top and left */
      if (rect.top < 0 || rect.top > maxheight )
        rect.top = (rand() % (maxheight>>1)); /* somewhere in the top half */
      if (rect.left < 0 || rect.left > maxwidth )
        rect.left = (rand() % (maxwidth>>1)); /* somewhere in the left half */

      console->lastpos.top = rect.top;
      console->lastpos.left = rect.left;

      CONFTRACE_OUT((0,"conf: load pos: %d,%d\n",console->lastpos.left,console->lastpos.top));
      CONFTRACE_OUT((0,"conf: load fxy: %d,%d\n",console->lastpos.fx,console->lastpos.fy));
    }
  }
  return;
}

/* ---------------------------------------------------------------------- */

#if (CLIENT_OS == OS_WIN32)
#include <shellapi.h>
static int my_Shell_NotifyIcon( DWORD dwMessage, PNOTIFYICONDATA pnid )
{
  typedef BOOL (WINAPI *Shell_NotifyIconAT)(DWORD, PNOTIFYICONDATA);
  static HMODULE hShell32;
  static Shell_NotifyIconAT _Shell_NotifyIcon = NULL;
  static int havenotifyiconproc = -1;

  TRAYTRACE_OUT((+1,"my_Shell_NotifyIcon\n"));

  if (havenotifyiconproc < 0)
  {
    TRAYTRACE_OUT((0,"(havenotifyiconproc < 0)\n"));
    if (dwMessage == NIM_DELETE)
    {
      TRAYTRACE_OUT((-1,"NIM_DELETE. No action necessary. returning 1 (ok)\n"));
      return 1;
    }
    havenotifyiconproc = 0;
    if ((winGetVersion() % 2000) >= 400)        // Win95+, NT4+
    {
      hShell32 = LoadLibrary( "shell32.dll" );
      TRAYTRACE_OUT((0,"LoadLibrary( \"shell32.dll\" ) => %x\n", hShell32));
      if (hShell32 != NULL)
      {
        _Shell_NotifyIcon = (Shell_NotifyIconAT) GetProcAddress(hShell32, "Shell_NotifyIconA");
        TRAYTRACE_OUT((0,"GetProcAddress( hmod, \"Shell_NotifyIconA\" ) => %p\n", _Shell_NotifyIcon));
        if (_Shell_NotifyIcon != NULL)
          havenotifyiconproc = +1;
        else
          FreeLibrary( hShell32 );
      }
    }
  }

  if (havenotifyiconproc > 0)
  {
    TRAYTRACE_OUT((0,"(havenotifyiconproc > 0)\n"));
    /*
    __w16writelog( "sni: msg=%s pnid.hIcon=%d\n",
         ((dwMessage==NIM_ADD)?("NIM_ADD"):
         ((dwMessage==NIM_DELETE)?("NIM_DELETE"):("NIM_MODIFY"))),
         pnid->hIcon );
    */
    if ((*_Shell_NotifyIcon)(dwMessage, pnid ))
    {
      TRAYTRACE_OUT((0,"((*_Shell_NotifyIcon)(dwMessage, pnid )) => TRUE\n"));
      if (dwMessage == NIM_DELETE)
      {
        TRAYTRACE_OUT((0,"dwMessage == NIM_DELETE\n"));
        havenotifyiconproc = -1;
        FreeLibrary( hShell32 );
      }
      TRAYTRACE_OUT((-1,"returning 1 (ok)\n"));
      return 1;
    }
  }
  TRAYTRACE_OUT((-1,"returning 0 (fail)\n"));
  return 0;
}
#endif

/* action :  <0 (remove from tray), >=0 (add/update to tray). */
/* calledfrom :  debugging trace string. */

static int __DoTrayStuff(W16CONP console, HWND hwnd, int action,
                           const char *calledfrom )
{
  int retcode = -1; /* assume failed */
  console = console; hwnd = hwnd; action = action; calledfrom = calledfrom; /* shaddup compiler */

  #if (CLIENT_OS == OS_WIN32)
  if ((winGetVersion() % 2000) >= 400)          // Win95+, NT4+
  {
    static int recursive = 0;
    if ((++recursive)==1)
    {
      if (!constatics.hidden && !constatics.nativecons &&
          !constatics.devpipe && IsWindow(hwnd))
      {
        static int intray = 0;
        DWORD realaction = 0;

        TRAYTRACE_OUT(( +1, "__DoTrayStuff: action=%d, intray=%d, calledfrom=%s\n",
                              action, intray, ((calledfrom)?(calledfrom):("???")) ));

        /* tray stuff is computationally expensive, so
           take some simple steps to avoid unecessary changes
        */
        if (action < 0) /* remove from tray */
        {
          action = 0; /* assume nothing to do */
          if (intray) /* currently in tray? */
          {
            action = 1; /* then something to do */
            realaction = NIM_DELETE;
          }
        }
        else /* if (action >= 0): refresh or create trayicon */
        {
          action = 0;  /* assume nothing to do */
          if (IsIconic(hwnd)) /* now iconic? */
          {
            action = 1;  /* then something to do */
            realaction = NIM_ADD;
          }
          else if (intray) /* not iconic, but currently in the tray? */
          {
            action = 1;  /* then something to do */
            realaction = NIM_DELETE;
          }
        }

        if (action)
        {
          HICON hIcon;
          NOTIFYICONDATA tnd;

          /* WM_GETICON is only 4.x and greater */
          hIcon = (HICON)SendMessage( hwnd, WM_GETICON, 0 /* small */, 0 );
          if (!hIcon)
            hIcon = (HICON)SendMessage( hwnd, WM_GETICON, 1 /* large */, 0 );
          if (!hIcon)
            hIcon = (HICON)GetClassLong(hwnd,GCL_HICON);
          TRAYTRACE_OUT(( 0, "got icon? =%x\n", hIcon ));

          /* construct a default structure */
          memset( (void *)&tnd, 0, sizeof(tnd));
          tnd.cbSize    = sizeof(NOTIFYICONDATA);
          tnd.uCallbackMessage= WM_USER_SHELLNOTIFYICON;
          tnd.hWnd      = hwnd;
          tnd.uID       = 1; /* App-defined id of the taskbar icon */
          tnd.hIcon     = hIcon;
          tnd.uFlags    = NIF_MESSAGE|((hIcon)?(NIF_ICON):(0));
          tnd.szTip[0]  = '\0';

          if (intray) /* if we're in the tray, delete it (may be broken) */
          {
            /* NIM_DELETE fails if explorer just got restarted */
            my_Shell_NotifyIcon(NIM_DELETE, &tnd);
            intray = 0;
          }
          if (realaction == NIM_DELETE)
          {
            retcode = 0; /* already done */
            #if 0
            if (gIcon)
            {
              /* free image */
              gIcon = NULL;
            }
            #endif
            #if 0
            if (!IsWindowVisible(hwnd))
            {
              ShowWindow(hwnd, SW_RESTORE);
              ShowWindow(hwnd, SW_SHOW);
              SetForegroundWindow(hwnd);
              BringWindowToTop(hwnd);
            }
            #endif
          }
          else /* NIM_ADD */
          {
            if (GetWindowText( hwnd, tnd.szTip, sizeof(tnd.szTip)))
              tnd.uFlags |= NIF_TIP;
            tnd.szTip[sizeof(tnd.szTip)-1]=0;

            if (my_Shell_NotifyIcon(NIM_ADD, &tnd))
            {
              intray = 1;
              if (IsWindowVisible(hwnd))
                ShowWindow(hwnd, SW_HIDE);
              retcode = 0;
            }
          }
        }

        TRAYTRACE_OUT(( -1, "__DoTrayStuff: retcode=%d intray=%d\n", retcode, intray ));
      }
    }
    --recursive;
  }
  #endif
  return retcode;
}

/* ---------------------------------------------------------------------- */

static int __stacksaving_IsOEMCharsetOk(void)
{
  char oemv[128], ansiv[128];
  unsigned int c, len = 0;
  for (c = 0x20; c < 0x7F; c++)
  {
    ansiv[c-0x20] = oemv[c-0x20] = (char)c;
    len++;
  }
  OemToAnsiBuff( oemv, ansiv, (short)len ); /* short is needed for win16 */
  if ( memcmp( oemv, ansiv, len ) != 0)
    return 0;
  return 1;
}

static BOOL __w16IsOEMCharsetOk(void) /* are our [' '-'~'] chars the same? */
{
  static int isok = -1;
  if (isok < 0)
    isok = __stacksaving_IsOEMCharsetOk();
  return isok;
}

static BOOL __w16IsFontMonospaced(HDC hdc, UINT uFirstChar,
    UINT uLastChar, UINT uAvgWidth)
{
  BOOL isok = FALSE;
  LPABC lpabc = (LPABC) malloc(sizeof(ABC) * (uLastChar - uFirstChar + 1));
  if (lpabc != NULL)
  {
    if (GetCharABCWidths(hdc, uFirstChar, uLastChar, lpabc) != 0)
    {
      int myi;
      isok = TRUE;
      for (myi = uLastChar - uFirstChar; isok && myi >= 0; myi--)
        isok = (uAvgWidth == (lpabc[myi].abcA + lpabc[myi].abcB +
          lpabc[myi].abcC));
    }
    free((void *)lpabc);
  }
  return isok;
}

static BOOL __w16Fontomatic( HWND hwnd, SIZE *newfontsize, const RECT *crect,
               int numcols, int numrows, HFONT *hfontP, int *isstockfont,
               LOGFONT *logfontP )
{
  BOOL fontchanged = FALSE;              /* returns TRUE if font changed */
  int oldMapMode, height, width, newfontx, newfonty, turn;
  HDC hdc = GetDC( hwnd );
  DWORD mapperFlags;

  if (!hdc)
    return FALSE;

  oldMapMode = SetMapMode( hdc, MM_TEXT);
  mapperFlags = SetMapperFlags( hdc, 0 );
  SetMapperFlags( hdc, mapperFlags|1 );

  height = crect->bottom - crect->top;
  width = crect->right - crect->left;
  newfonty  = ((height + (numrows>>1))/numrows);
  newfontx  = ((width + (numcols>>1))/numcols);

  //if (console->smoothsizing)
  //{
  //  if ((newfonty * W16CONS_HEIGHT) != height)
  //    newfonty  = ((height+W16CONS_HEIGHT)/W16CONS_HEIGHT);
  //  if ((newfontx * W16CONS_WIDTH) != width)
  //    newfontx  = ((width+W16CONS_WIDTH)/W16CONS_WIDTH);
  //}

  FONTTRACE_OUT((+1, "font change: want %d:%d\n", newfontx, newfonty ));

  for (turn=0;;turn++)
  {
    HFONT hfont = NULL;
    LOGFONT lfont;
    int isstock = 0, lastchance = 0;

    //how the font mapper works is well (and understandably) documented
    //in http://msdn.microsoft.com/library/techart/msdn_fontmap.htm

    if (turn == 0)
    {
      //try to avoid a change of typeface if we can help it
      if (*hfontP && !*isstockfont)
      {
        GetObject( *hfontP, sizeof(LOGFONT), (LPSTR) &lfont);
        lfont.lfHeight = newfonty;
        lfont.lfWidth = newfontx;
        hfont = CreateFontIndirect(&lfont);
        isstock = 0;
      }
    }
    #if 0 //old and probably effective only when hfontP == NULL
    else if (turn <= 5)
    {
      isstock = 0;
      hfont = CreateFont( newfonty, newfontx, 0,0, 0, 0,0,0, 0,
          ((turn > 3)?(0):(OUT_TT_PRECIS)),
          ((turn > 2)?(0):(CLIP_CHARACTER_PRECIS)),
          ((turn > 1)?(0):(PROOF_QUALITY)),
          FIXED_PITCH|FF_MODERN,
          ((turn > 4)?(""):("Courier")) );
    }
    #else
    else if (turn <= 9)
    {
      isstock = 0;
      if (turn == 9)
        hfont = CreateFont( newfonty, newfontx,  0, 0, 0, 0, 0, 0, 0,
                        0, 0, 0, FIXED_PITCH|FF_MODERN, NULL );
      else
      {
        memset(&lfont,0,sizeof(lfont));
        lfont.lfHeight = newfonty;
        lfont.lfWidth = newfontx;
        lfont.lfWeight = FW_NORMAL;
        lfont.lfCharSet = ANSI_CHARSET; /* critical */
        lfont.lfOutPrecision = OUT_TT_PRECIS;
        lfont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
        lfont.lfQuality = DEFAULT_QUALITY;
        lfont.lfPitchAndFamily = (FIXED_PITCH | FF_MODERN); /* monospaced */
        if (turn == 1)
          strcpy(lfont.lfFaceName,"Lucida Console");
        else if (turn == 2)
          strcpy( lfont.lfFaceName, "Courier New" );
        /* increasingly less nice (3==same as 1/2, but no name) */
        if (turn >= 4) lfont.lfOutPrecision = OUT_DEVICE_PRECIS;
        if (turn >= 5) lfont.lfWeight = FW_DONTCARE;
        if (turn >= 6) lfont.lfCharSet = OEM_CHARSET;
        if (turn >= 7) lfont.lfOutPrecision = OUT_DEFAULT_PRECIS;
        if (turn >= 8) lfont.lfCharSet = DEFAULT_CHARSET;
        hfont = CreateFontIndirect(&lfont);
      }
    }
    #endif
    else
    {
      if (*hfontP == NULL)
      {
        // define to the font type (must be fixed width)
        hfont = (HFONT)GetStockObject(SYSTEM_FIXED_FONT); //ANSI_FIXED_FONT
        isstock = 1;
      }
      lastchance = 1;
    }
    if (hfont)
    {
      TEXTMETRIC tm;
      HFONT hOldfont = (HFONT)SelectObject(hdc, hfont);
      if (GetTextMetrics( hdc, &tm ))
      {
        #ifdef TRACE
        GetObject( hfont, sizeof(LOGFONT), (LPSTR) &lfont);
        FONTTRACE_OUT((0, "turn %d name='%s' pf=%x/%x cs=%d/%d h=%d/%d/%d w=%d/%d,%d/%d\n",
                          turn, lfont.lfFaceName,
                          lfont.lfPitchAndFamily, tm.tmPitchAndFamily,
                          lfont.lfCharSet, tm.tmCharSet,
                          lfont.lfHeight, tm.tmHeight, newfonty,
                          lfont.lfWidth, tm.tmAveCharWidth, tm.tmMaxCharWidth,
                          newfontx ));
        #endif
        if ((tm.tmPitchAndFamily & 0xf0) != FF_DECORATIVE &&
            (tm.tmPitchAndFamily & 0xf0) != FF_SCRIPT)
        {
          if (*hfontP == NULL || tm.tmAveCharWidth == newfontx)
          {
            if (*hfontP == NULL || tm.tmHeight == newfonty)
            {
              if ((tm.tmCharSet == ANSI_CHARSET) ||
                  (tm.tmCharSet == OEM_CHARSET && __w16IsOEMCharsetOk()))
              {
                if ((tm.tmPitchAndFamily & TMPF_FIXED_PITCH) != 0 ||
                    (tm.tmAveCharWidth == tm.tmMaxCharWidth) ||
                    __w16IsFontMonospaced(hdc, 0x20, 0x7E, tm.tmAveCharWidth))
                {
                  fontchanged = TRUE;
                  if (logfontP)
                    GetObject( hfont, sizeof(LOGFONT), (LPSTR) &lfont);
                }
              }
            }
          }
        }
      }
      SelectObject(hdc, hOldfont);
      if (lastchance && *hfontP == NULL)
        fontchanged = TRUE; /* we _have_ to take it */
      if (fontchanged)
      {
        FONTTRACE_OUT((0, "font change: turn %d accepted\n", turn ));
        if (*hfontP != NULL && *isstockfont == 0)
          DeleteObject(*hfontP);
        if (logfontP)
          memcpy( logfontP, &lfont, sizeof(LOGFONT));
        *isstockfont = isstock;
        *hfontP = hfont;
        newfontsize->cx = newfontx = tm.tmAveCharWidth;
        newfontsize->cy = newfonty = tm.tmHeight;
        break;
      }
      FONTTRACE_OUT((0, "font change: turn %d rejected\n", turn ));
      if (!isstock)
        DeleteObject(hfont);
      hfont = NULL;
    }
    if (lastchance)
      break;
  } //for (turn=0;;turn++)

  SetMapperFlags( hdc, mapperFlags );
  SetMapMode( hdc, oldMapMode );
  ReleaseDC( hwnd, hdc );

  FONTTRACE_OUT((-1, "font %schanged\n", (fontchanged)?(""):("NOT ") ));
  return fontchanged;
}

/* ---------------------------------------------------- */

#if defined(TRACE)
const char *__trace_map_msg_toname(UINT message)
{
  switch (message)
  {
    case WM_CREATE: return "WM_CREATE";
    case WM_PAINT: return "WM_PAINT";
    #if defined(WM_SIZING)
    case WM_SIZING: return "WM_SIZING";
    #endif
    case WM_SIZE: return "WM_SIZE";
  }
  return "???";
}
#endif

static LRESULT __w16AdjustRect( W16CONP console, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  if (console)
  {
    console->disprows = W16CONS_HEIGHT;
    console->dispcols = W16CONS_WIDTH;
    wParam = wParam; //not used except in WM_SIZING (which might be #if'd out)

    if ( message == WM_CREATE ||
         message == WM_PAINT  ||
         #if defined(WM_SIZING)
         message == WM_SIZING ||
         #endif
         message == WM_SIZE )
    {
      int top, left, width, height, oldwidth, oldheight, adjx, adjy;
      int oldfx, oldfy;

      #if defined(WM_SIZING)
      RECT *wmsizing_rect = NULL;
      #endif
      RECT rect;

      ADJRECTTRACE_OUT((0, "step 1 (%s)\n",__trace_map_msg_toname(message)));

      GetWindowRect(hwnd, &rect);
      top = rect.top;
      left = rect.left;
      width = rect.right - rect.left;
      height = rect.bottom - rect.top;
      GetClientRect(hwnd, &rect);
      adjx = width - (rect.right - rect.left);
      adjy = height - (rect.bottom - rect.top);
      //adjx = (GetSystemMetrics(SM_CXFRAME)<<1);
      //adjy = (GetSystemMetrics(SM_CYFRAME)<<1)+GetSystemMetrics(SM_CYCAPTION);

      ADJRECTTRACE_OUT((0, "adjx: %d, adjy:%d\n", adjx, adjy ));

      width -= adjx;
      height -= adjy;

      oldfx = console->fontx;
      oldfy = console->fonty;
      //make expwidth and oldheight the old client dimensions
      oldwidth  = console->dispcols * console->fontx;
      oldheight = console->disprows * console->fonty;

      if (message == WM_SIZE)
      {
        width = LOWORD(lParam);  // width of client area
        height = HIWORD(lParam); // height of client area
        ADJRECTTRACE_OUT((0,"size oldwidth=%d, oldheight=%d\n", oldwidth, oldheight ));
        ADJRECTTRACE_OUT((0,"size newwidth=%d, newheight=%d\n", width, height ));
      }
      else if (message == WM_PAINT) //dummy meaning wParam and lParam are invalid
      {
        ADJRECTTRACE_OUT((0,"paint width=%d, height=%d\n", rect.right, rect.bottom ));
        if (console->hfont && rect.bottom < rect.top) /* rollup support */
          return 0;
        width = (rect.right-rect.left);
        height = (rect.bottom-rect.top);
      }
      #if defined(WM_SIZING)
      else if (message == WM_SIZING)
      {
        wmsizing_rect = (RECT *) lParam;
        height = (wmsizing_rect->bottom - wmsizing_rect->top) - adjy;
        width  = (wmsizing_rect->right - wmsizing_rect->left) - adjx;
        wParam = wParam;
        //wParam specifies the edge of window being sized
      }
      #endif
      else if (message == WM_CREATE)
      {
        __conssize_saveupdateload(console, hwnd, +1/*saveupdateload=-1,0,+1*/);
        top = console->lastpos.top;
        left = console->lastpos.left;
        width = console->fontx * console->dispcols;
        height = console->fonty * console->disprows;
        oldfx = oldfy = oldwidth = oldheight = 0;
      }

      if (console->fontisstock || console->hfont == NULL ||
         /* no point doing this if the font isn't going to change */
         (abs(oldwidth - width) >= console->dispcols) ||
         (abs(oldheight - height) >= console->disprows) )
      {
        SIZE newfontsize; /* in logical units */
        rect.left = rect.top = 0;
        rect.right = width;
        rect.bottom = height;

        if (__w16Fontomatic( hwnd, &newfontsize, &rect,
                             console->dispcols, console->disprows,
                             &(console->hfont), &(console->fontisstock), NULL ))
        {
          /* font has changed */

          //save new font size
          console->fontx = newfontsize.cx;
          console->fonty = newfontsize.cy;
        }
      }
      if (console->hfont == NULL)
        return FALSE;

//char tbuf[128];
//sprintf(tbuf,"%d,%d %d,%d (%d,%d)", oldwidth,oldheight,width,height,(console->dispcols * console->fontx), (console->disprows * console->fonty) );
//SetWindowText(hwnd,tbuf);

      //this is the width/height that was expected of us
      oldwidth = width;
      oldheight = height;

      //make width and height the new client dimensions
      width  = console->dispcols * console->fontx;
      height = console->disprows * console->fonty;

      ADJRECTTRACE_OUT((0, "step 7\n"));

      //if this a WM_SIZING message, then adjust the new size and return
      #if defined(WM_SIZING)
      if (wmsizing_rect != NULL && console->smoothsizing == 0)
      {
        //make width and height the new window dimensions
        if (wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT || wParam == WMSZ_TOPLEFT)
          wmsizing_rect->top = wmsizing_rect->bottom - (height + adjy);
        if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMRIGHT || wParam == WMSZ_BOTTOMLEFT)
          wmsizing_rect->bottom = wmsizing_rect->top + (height + adjy);
        if (wParam == WMSZ_TOPRIGHT || wParam == WMSZ_BOTTOMRIGHT || wParam == WMSZ_RIGHT)
          wmsizing_rect->right = wmsizing_rect->left + (width + adjx);
        if (wParam == WMSZ_TOPLEFT || wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT)
          wmsizing_rect->left = wmsizing_rect->right - (width + adjx);
        return TRUE;
      }
      #endif

      ADJRECTTRACE_OUT((0, "step 8\n"));

      //if the request was not satisfied, ...
      if (oldwidth != width || oldheight != height || message == WM_CREATE)
      {
        ADJRECTTRACE_OUT((0, "step 9\n"));

        /* if (...) order is important here */
        if (message == WM_PAINT)
        {
          /* nothing */
        }
        else if (message == WM_SIZE)
        {
          ADJRECTTRACE_OUT((0, "step 9.1\n"));
          //Invalidate the rectange (although HREDRAW|VREDRAW should do this)
          InvalidateRect(hwnd,NULL,FALSE);
          UpdateWindow( hwnd );
        }
        else if (message == WM_CREATE)
        {
          ADJRECTTRACE_OUT((0, "step 9.2 SetWindowPos(%d,%d,width=%d,height=%d)\n",left,top,width,height));
          SetWindowPos(hwnd, NULL, left, top,
                       (width + adjx), (height + adjy),
                       SWP_NOREDRAW|SWP_NOZORDER|SWP_NOACTIVATE);
        }
        /*
        else if (console->smoothsizing == 0)
        {
          ADJRECTTRACE_OUT((0, "step 9.3 MoveWindow(%d,%d,width=%d,height=%d)\n",left,top,width,height));
          MoveWindow(hwnd, left, top, (width + adjx), (height + adjy), TRUE);
        }
        */
        ADJRECTTRACE_OUT((0, "step 10\n"));
      }
      return TRUE;
    }
  }
  return FALSE;
}

/* ---------------------------------------------------- */

static int __win16AdjustCaret(HWND hwnd, W16CONP console, int destroy_first )
{
  //if (hwnd == GetFocus())
  {
    if (destroy_first)
      DestroyCaret();

    if (console->smoothsizing == 0)
    {
      CreateCaret( hwnd, NULL, console->fontx, console->fonty / 6);
      SetCaretPos( console->indentx + console->curcol * console->fontx,
        console->indenty + (console->currow + 1) * console->fonty -(console->fonty / 6) );
      ShowCaret( hwnd );
    }
    else
    {
      RECT clirect;
      GetClientRect( hwnd, &clirect );

      int col = ( ((unsigned long)(clirect.right-clirect.left)) *
          ((unsigned long)(console->curcol)) ) / console->dispcols;
      int row = ( ((unsigned long)(clirect.bottom-clirect.top)) *
          ((unsigned long)(console->currow)) ) / console->disprows;
      int cwidth = (clirect.right-clirect.left) / console->dispcols;
      int cheight = (clirect.bottom-clirect.top) / console->disprows;

      CreateCaret( hwnd, NULL, cwidth, cheight / 6);
      SetCaretPos( col, row+(cheight-cheight/6) );
      ShowCaret( hwnd );
    }
  }
  return 0;
}

/* ---------------------------------------------------- */

DWORD CALLBACK __w16AboutBox( HWND dialog, UINT msg, WORD wParam, LONG lParam )
{
  static HBRUSH hBGBrush = NULL;
  lParam = lParam;

  if (msg == WM_INITDIALOG)
  {
    HWND hwnd;
    if ((hwnd = GetDlgItem( dialog, 202 )) != NULL)
    {
      SetWindowText( hwnd, "This client is maintained by "
      #if defined(_M_ALPHA)
      "Mike Marcelais <michmarc@microsoft.com>"
      #else
      "\nCyrus Patel <cyp@distributed.net>, <cyp@fb14.uni-mainz.de>"
      #endif
      );
    }
    if ((hwnd = GetDlgItem( dialog, 201 )) != NULL)
    {
      SetWindowText( hwnd, CliGetFullVersionDescriptor() );
    }
    return( TRUE );
  }
  #if defined(WM_CTLCOLOREDIT)
  else if ((msg==WM_CTLCOLOREDIT) && (GetDlgItem(dialog,201)==((HWND)lParam)))
  #define SetBrushOrg(__hdc,__x,__y) SetBrushOrgEx(__hdc,__x,__y,NULL)
  #else
  else if ((msg == WM_CTLCOLOR) && (HIWORD(lParam) == CTLCOLOR_EDIT))
  #endif
  {
    HDC hdc = (HDC)wParam;
    HBRUSH hBrush = hBGBrush;
    if (!hBrush)
    {
      #if defined(WM_CTLCOLOREDIT)
      hBrush = GetSysColorBrush(COLOR_BTNFACE);
      #else
      hBrush = hBGBrush = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
      if (!hBrush)
        hBrush = GetStockObject(LTGRAY_BRUSH);
      #endif
    }
    SetTextColor(hdc, GetSysColor(COLOR_BTNTEXT) );
    SetBkColor(hdc, GetSysColor(COLOR_BTNFACE) );
    UnrealizeObject(hBrush);
    SetBrushOrg(hdc, 0, 0);
    return (DWORD)hBrush;
  }
  else if (msg == WM_CLOSE || (msg == WM_COMMAND && LOWORD(wParam)==IDOK))
  {
    if (hBGBrush)
      DeleteObject(hBGBrush);
    EndDialog( dialog, TRUE );
    return( TRUE );
  }
  return( FALSE );
}

/* ---------------------------------------------------- */

static void __clear_marked(W16CONP console)
{
  if (console != NULL)
  {
    memset(&(console->marked_buff[0]),0,sizeof(console->marked_buff));
    console->mark_lastrow = console->mark_down =
        console->have_marked = console->have_dragthreshold = 0;
  }
  return;
}

/* ---------------------------------------------------- */

static int __w16WindowHandle_DNETC_WCMD(HWND hwnd, UINT message,
                        WPARAM wParam, LPARAM lParam, LRESULT *lResultP )
{
  int handled = 0;
  hwnd = hwnd; lParam = lParam; // shaddup compiler

  if (message == WM_COMMAND)
  {
    if (wParam == DNETC_WCMD_SHUTDOWN) // 1
    {
      RaiseExitRequestTrigger();
      CheckExitRequestTrigger();
      handled = 1;
    }
    else if (wParam == DNETC_WCMD_RESTART) // 2
    {
      RaiseRestartRequestTrigger();
      CheckExitRequestTrigger();
      handled = 1;
    }
    else if (wParam == DNETC_WCMD_PAUSE) //3
    {
      RaisePauseRequestTrigger();
      CheckPauseRequestTrigger();
      handled = 1;
    }
    else if (wParam == DNETC_WCMD_UNPAUSE) //13
    {
      ClearPauseRequestTrigger();
      CheckPauseRequestTrigger();
      handled = 1;
    }
  }
  if (handled && lResultP)
    *lResultP = DNETC_WCMD_ACKMAGIC;
  return handled;
}

/* ---------------------------------------------------- */

static LRESULT __w16WindowFuncInternal(int nestinglevel, HWND hwnd,
                                UINT message, WPARAM wParam, LPARAM lParam)
{
  W16CONP console = __win16GetHwndConsole( hwnd );
  nestinglevel = nestinglevel; /* shaddup compiler */

  if (console != NULL)
  {
    if (console->ssmessage && console->ssmessage == message)
    {
      message = WM_COMMAND;
      if (wParam == DNETC_WCMD_SHUTDOWN)
        console->ssmessage = 0;
    }
    else if (console->dnetc_cmdmsg && console->dnetc_cmdmsg == message)
      message = WM_COMMAND;
  }

  #if defined(TRACE) && (CLIENT_CPU == CPU_X86)
  {
    static char *applsp = NULL;
    if (applsp == NULL)
    {
      applsp = (char *)&hwnd;
      _asm sub applsp,esp
      FLOWTRACE_OUT((0,"__w16WindowFunc local var size = %ld (0x%lx)\n",applsp,applsp));
    }
  }
  #endif

  switch (message)
  {
    case WM_LBUTTONUP:
    case WM_LBUTTONDOWN:
    case WM_MOUSEACTIVATE:
    case WM_MOUSEMOVE:
    {
      if (console != NULL)
      {
        if (message == WM_MOUSEACTIVATE)
        {
          if (LOWORD(lParam) == HTCLIENT && HIWORD(lParam) == WM_LBUTTONDOWN)
            console->mark_ignorenextlbdown = 1;
          console = NULL; /* nothing more to do with this message */
        }
        else if (message == WM_LBUTTONDOWN)
        {
          if (console->mark_ignorenextlbdown)
          {
            console->mark_ignorenextlbdown = 0;
            console = NULL; /* ignore this lbdown */
          }
        }
        else if (message == WM_MOUSEMOVE)
        {
          if ((wParam & MK_LBUTTON) != 0 && console->mark_down)
            ; /* nothing - treat as additive click below */
          else
            console = NULL; /* don't do anything with the message */
        }
      }
      if (console != NULL)
      {
        int i, row = 0;
        while (row <= console->disprows)
        {
          if (HIWORD(lParam) >= (row * console->fonty) &&
              HIWORD(lParam) <  ((row+1) * console->fonty))
            break;
          row++;
        }
        if (row >= 0 && row < W16CONS_HEIGHT)
        {
          if (message == WM_LBUTTONUP)
          {
            if (console->mark_down && !console->have_marked)
            {                                /* click without move */
              console->marked_buff[row] = 1;
              console->have_marked = 1;
            }
          }
          else if (message == WM_MOUSEMOVE || (wParam & MK_SHIFT) != 0)
          {
            /* extending an existing selection region. */
            for (i = 0; i < W16CONS_HEIGHT; i++)
              console->marked_buff[i] = 0;
            if (console->mark_lastrow < 0 ||
                console->mark_lastrow >= W16CONS_HEIGHT)
            {
              console->mark_lastrow = row;
              console->have_dragthreshold = 0;
              console->have_marked = 0;
            }
            else
            {
              if (console->have_dragthreshold || row != console->mark_lastrow)
              {
                i = ((row > console->mark_lastrow) ? -1 : +1 );
                for (; row != console->mark_lastrow; row += i)
                  console->marked_buff[row] = 1;
                console->marked_buff[console->mark_lastrow] = 1;
                console->have_dragthreshold = 1;
                console->have_marked = 1;
              }
              else console->have_marked = 0;
            }
            console->mark_down = 1;
          }
          else if ((wParam & MK_CONTROL) != 0)
          {
            /* selective select/deselect of individual lines */
            if (console->marked_buff[row])
            {
              console->marked_buff[row] = 0;
              console->have_marked = 0;
              console->mark_lastrow = -1; /* invalid */
              for (i = 0; i < W16CONS_HEIGHT; i++)
              {
                if (console->marked_buff[i])
                {
                  console->have_marked = 1;
                  break;
                }
              }
              if (!console->have_marked)
              {
                console->mark_lastrow = row;
                console->mark_down = 1;
                console->have_marked = 1;
              }
              console->have_dragthreshold = 1;
            }
            else
            {
              console->marked_buff[row] = 1;
              console->mark_lastrow = row;
              console->mark_down = 1;
              console->have_marked = 1;
              console->have_dragthreshold = 1;
            }
          }
          else
          {
            /* first click marks starting point. */
            for (i = 0; i < W16CONS_HEIGHT; i++)
              console->marked_buff[i] = 0;
            console->have_marked = 0;
            console->mark_lastrow = row;
            console->mark_down = 1;
            console->have_dragthreshold = 0;
          }
          InvalidateRect(hwnd, NULL, FALSE);
        } /* if (row >= 0 && row < W16CONS_HEIGHT) */
        if (message == WM_LBUTTONUP)
          console->mark_down = 0;
      } /* if (console) */
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_NCCREATE:
    {
      if ( DefWindowProc(hwnd, message, wParam, lParam) != 0)
      {
        constatics.createflag = 0;
        constatics.errorcode = W16CONS_ERR_NCCREATE;
        return -1;
      }
      return 0;
    }
    case WM_SYSCOMMAND:
    {
      if (wParam == 0x1000)
      {
        #ifndef GWL_HINSTANCE
        #define GWL_HINSTANCE (-6)
        #endif
        HINSTANCE hinst = (HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE);
        if ( hinst )
        {
          FARPROC func = MakeProcInstance( (FARPROC)__w16AboutBox, hinst);
          DialogBox( hinst, MAKEINTRESOURCE(1), hwnd, (DLGPROC)func );
          (void)FreeProcInstance( func );
        }
        return 0;
      }
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_CREATE:
    {
      #ifndef GWL_HINSTANCE
      #define GWL_HINSTANCE (-6)
      #endif
      HINSTANCE hinst = (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE);

      INITTRACE_OUT((0,"WM_CREATE,hInstance=%ld (0x%x)\n", hinst,hinst));

      if (FindResource( hinst, MAKEINTRESOURCE(1), RT_DIALOG ))
      {
        HMENU hmenu = GetSystemMenu( hwnd, 0);
        if (hmenu)
        {
          AppendMenu(hmenu, MF_SEPARATOR, 0, "" );
          AppendMenu(hmenu, MF_ENABLED, 0x1000, "About..." );
        }
      }
      console = (W16ConsoleStruc *)malloc(sizeof(W16ConsoleStruc));
      if (console)
      {
        int reallyhidden = 0;
        // initialize our private structure
        memset((void *)console, 0, sizeof(W16ConsoleStruc));
        memset((void *)(&(console->buff[0][0])),' ',sizeof(console->buff));

        console->hwnd = hwnd;
        console->disprows = W16CONS_HEIGHT;
        console->dispcols = W16CONS_WIDTH;
        console->smoothsizing = 0;
        console->ssmessage = GlobalFindAtom( W32CLI_SSATOM_NAME );
        console->dnetc_cmdmsg = RegisterWindowMessage(W32CLI_CONSOLE_NAME);

        console->fontx = (int)GetSystemMetrics(SM_CXSCREEN);
        if (console->fontx < 700) /* 640x480 */
        { console->fontx =  6; console->fonty = 10; }
        if (console->fontx < 900) /* 800x600 */
        { console->fontx =  8; console->fonty = 14; }
        else
        { console->fontx =  9; console->fonty = 15; }

        #ifdef W16CONS_SMOOTHSIZING
        {
          #if defined(SPI_GETFONTSMOOTHING)
          {
            UINT oldval = 0;
            if (!SystemParametersInfo(SPI_GETFONTSMOOTHING,0,&oldval,0))
              oldval = 0;
            if (oldval)
              console->smoothsizing = 1;
          }
          #endif
          if (console->smoothsizing && (winGetVersion()%2000) < 400)
            console->smoothsizing = 0;
        }
        #endif

        __w16AdjustRect( console, hwnd, WM_CREATE, 0, 0 );
        INITTRACE_OUT((0,"WM_CREATE visible=%d,zoomed=%d,iconic=%d\n",IsWindowVisible(hwnd),IsZoomed(hwnd),IsIconic(hwnd)));
        if (console->hfont == NULL)
        {
          free((void *)console);
          constatics.createflag = 0;
          constatics.errorcode = W16CONS_ERR_NOFONT;
          return -1;
        }

        #if defined(WM_GETICON) && defined(WM_SETICON)
        if ((winGetVersion() % 2000) >= 400)        // Win95+, NT4+
        {
          HICON hIcon;
          HMODULE hInst = LoadLibrary( "user32.dll" );
          if (hInst != NULL)
          {
            typedef HANDLE (WINAPI *LoadImageAT)(HINSTANCE,LPCTSTR,UINT,int,int,UINT);
            LoadImageAT _LoadImage = (LoadImageAT) GetProcAddress(hInst, "LoadImageA");
            if (_LoadImage != NULL)
            {
              hIcon = (HICON)(*_LoadImage)(
                         (HINSTANCE)GetWindowLong(hwnd,GWL_HINSTANCE),
                           MAKEINTRESOURCE(1), IMAGE_ICON,
                             GetSystemMetrics(SM_CXSMICON),
                             GetSystemMetrics(SM_CYSMICON), 0 );
              if (hIcon != NULL)
              {
                console->hSmallIcon = hIcon;
                SendMessage(hwnd, WM_SETICON, 0 /*ICON_SMALL*/, (LPARAM)hIcon);
              }
            }
            FreeLibrary(hInst);
          }
          hIcon = (HICON)GetClassLong(hwnd, GCL_HICON);
          if (hIcon)
            SendMessage( hwnd, WM_SETICON, 1 /*ICON_LARGE*/, (LPARAM)hIcon );
        }
        #endif

        __win16SetHwndConsole( hwnd, console );
        constatics.createflag = 1;
        break;
      }
      else
      {
        constatics.createflag = 0;
        constatics.errorcode = W16CONS_ERR_NOMEM;
        // fail to create
        return -1;
      }
    }
    case WM_CLOSE:
    {
      __conssize_saveupdateload(console, hwnd, -1 /*saveupdateload=-1,0,+1*/);
      RaiseExitRequestTrigger();
      return DNETC_WCMD_ACKMAGIC; /* all win cares about is non-zero */
    }
    case WM_QUERYENDSESSION:
    {
      return TRUE; /* yes, we can terminate */
    }
    case WM_ENDSESSION:
    {
      if (wParam) /* yes, we are terminating */
      {
        __conssize_saveupdateload(console, hwnd,-1/*saveupdateload=-1,0,+1*/);
        /* for the record:
           a) win95 (only) has a borked service shutdown
           sequence that permits other threads belonging to the process
           to continue running even when the window thread has died. This
           often causes a segfault because thread data has disappeared too.
           b) All win32: When *not* running as a service all threads *except*
           the window thread are suspended once shutdown is in progress.
           Waiting on a child will thus result in a 'hang' (win will kill
           the process after 10 seconds or so).
         */
        #if (CLIENT_OS == OS_WIN32)
        if (win32CliServiceRunning())
        {
          if (((lParam & ENDSESSION_LOGOFF)!=0))
            return 1;  /* if we are running as a service. don't exit */
          else if (constatics.client_run_startstop_level > 0)
          {
            RaiseExitRequestTrigger();
            while (constatics.client_run_startstop_level > 0)
              Sleep(100);
          }
        }
        else
        #endif
        __w16ClientHardStop();
      }
      break;
    }
    case WM_DESTROY:
    {
      __DoTrayStuff(console, hwnd, -1, "DESTROY" );
      if (console != NULL)
      {
        __conssize_saveupdateload(console, hwnd, -1   /* saveupdateload=-1/0/+1 */  );
        #if defined(WM_GETICON) && defined(WM_SETICON)
        if (console->hSmallIcon)
        {
          SendMessage(hwnd, WM_SETICON, 0 /*ICON_SMALL*/, NULL); /* remove it */
          DestroyIcon(console->hSmallIcon);
        }
        #endif
        if (console->hfont && !console->fontisstock)
          DeleteObject(console->hfont);
        free((void *)console);
      }
      __win16SetHwndConsole( hwnd, NULL );
      PostQuitMessage(0);
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_ERASEBKGND:
    {
      return TRUE; /* FALSE sets ps.fErase for WM_PAINT */
    }
    case WM_PAINT:
    {
      RECT clirect;
      if (GetUpdateRect(hwnd, &clirect, 1)!= 0) /* something needs updating */
      {
        int done_paint=0;
        if (console)
        {
          GetClientRect(hwnd,&clirect);
          if (clirect.bottom > clirect.top) /* .bottom is -5 for rollups */
          {
            if (!console->hfont ||
                memcmp(&(console->lastpaintrect),&clirect,sizeof(RECT))!=0 )
            {
              __w16AdjustRect( console, hwnd, WM_PAINT, 0, 0);
              GetClientRect(hwnd,&clirect);
              memcpy(&(console->lastpaintrect),&clirect,sizeof(RECT));
            }
            if (console->hfont)
            {
              PAINTSTRUCT ps;
              HDC paintdc = BeginPaint(hwnd, &ps);
              if (paintdc)
              {
                int row, inv_color = 0, havememcpy = 0;
                HFONT oldFont; HBRUSH hBrush[2]; HBRUSH oldBrush;
                int oldMapMode; DWORD mapperFlags;
                int offsetrow = 0; //GetScrollPos(hwnd, SB_VERT);
                int offsetcol = 0; //GetScrollPos(hwnd, SB_HORZ);
                COLORREF oldTClr, oldBClr; UINT oldalignment;
                int oldinterchargap, interchargap = 0, whichver = 0;
                HDC workdc = NULL; HBITMAP oldbitmap = NULL;

                #if 1
                workdc = CreateCompatibleDC(NULL);
                if (workdc)
                {
                  PAINTTRACE_OUT((0,"CreateCompaptibleDC() => %p\n", workdc ));
                  oldbitmap = CreateCompatibleBitmap( paintdc,
                               clirect.right+1, clirect.bottom+1 );
                  if (oldbitmap)
                  {
                    PAINTTRACE_OUT((0,"CreateCompatibleBitmap() => %p\n", oldbitmap));
                    oldbitmap = (HBITMAP)SelectObject( workdc, oldbitmap );
                    havememcpy = 1;
                  }
                  else
                  {
                    DeleteDC(workdc);
                    workdc = NULL;
                  }
                }
                #endif
                if (!workdc)
                  workdc = paintdc;

                PAINTTRACE_OUT((0,"WM_PAINT 1\n" ));
                oldMapMode = SetMapMode( workdc, MM_TEXT);
                mapperFlags = SetMapperFlags( workdc, 0 );
                SetMapperFlags( workdc, mapperFlags|1 );
                oldTClr = SetTextColor(workdc, W16CONS_FORECOLOR );
                oldBClr = SetBkColor(workdc, W16CONS_BACKCOLOR );
                oldFont = (HFONT)SelectObject(workdc, console->hfont);
                hBrush[0] = CreateSolidBrush( GetBkColor( workdc ) );
                hBrush[1] = CreateSolidBrush( GetTextColor( workdc ) );
                oldBrush = (HBRUSH)SelectObject( workdc, hBrush[0] );
                PAINTTRACE_OUT((0,"WM_PAINT 2\n" ));

                {
                  SIZE size;
                  GetTextExtentPoint(workdc, "X", 1, &size);
                  size.cx = abs(console->fontx - size.cx);
                  interchargap = abs(console->fonty - size.cy);
                  if (interchargap < size.cx)
                    interchargap = size.cx;
                }
                PAINTTRACE_OUT((0,"WM_PAINT 3\n" ));
                oldinterchargap = SetTextCharacterExtra(workdc, interchargap);
                oldalignment = SetTextAlign(workdc, TA_LEFT|TA_TOP );

                PAINTTRACE_OUT((0,"WM_PAINT 4\n" ));
                whichver = winGetVersion();
                if (whichver >= 2000)
                  whichver = 0;
                else if (whichver < 400)
                  whichver = 2;
                else /* win9x */
                  whichver = 1;
                PAINTTRACE_OUT((0,"WM_PAINT 5\n" ));

                for (row = 0; row < console->disprows; row++)
                {
                  PAINTTRACE_OUT((0,"WM_PAINT 5 (row=%d)\n", row ));
                  int exp_color = 0;
                  if (console->marked_buff[row])
                    exp_color = 1;
                  if (inv_color != exp_color )
                  {
                    PAINTTRACE_OUT((0,"WM_PAINT 6 (exp_color=%d)\n", exp_color ));
                    SelectObject( workdc, hBrush[exp_color] );
                    SetTextColor( workdc, SetBkColor(workdc,
                                     GetTextColor( workdc ) ));
                    inv_color = exp_color;
                    PAINTTRACE_OUT((0,"WM_PAINT 7\n" ));
                  }
                  if (whichver == 0)
                  {
                    RECT lrect;
                    lrect.top    = console->indenty + row * console->fonty;
                    lrect.left   = console->indentx;
                    lrect.bottom = lrect.top + console->fonty + interchargap;
                    lrect.right  = lrect.left + console->fontx + interchargap;
                    ExtTextOut( workdc, lrect.left, lrect.top, ETO_OPAQUE,
                                &lrect,
                                &console->buff[offsetrow + row][offsetcol],
                                console->dispcols,
                                (LPINT) NULL );

                  }
                  else if (whichver == 1)
                  {
                    int col;
                    for (col = 0; col < console->dispcols; col++)
                    {
                      TextOut(workdc, console->indentx + col * console->fontx,
                                       console->indenty + row * console->fonty,
                              &console->buff[offsetrow + row][offsetcol + col],
                                      1);
                    }
                  }
                  else /* faster, but not as fast as ExtTextOut (I think) */
                  {
                    TextOut(workdc, console->indentx,
                         console->indenty + row * console->fonty,
                                 &console->buff[offsetrow + row][offsetcol],
                                 console->dispcols);
                  }
                }

                PAINTTRACE_OUT((0,"WM_PAINT 10\n" ));
                SetTextAlign( workdc, oldalignment );
                SetTextCharacterExtra( workdc, oldinterchargap);

                SelectObject( workdc, oldBrush );
                DeleteObject( hBrush[0] );
                DeleteObject( hBrush[1] );

                SetBkColor  ( workdc, oldBClr );
                SetTextColor( workdc, oldTClr );
                SelectObject( workdc, oldFont );

                SetMapperFlags( workdc, mapperFlags );
                SetMapMode( workdc, oldMapMode );

                PAINTTRACE_OUT((0,"WM_PAINT 11\n" ));
                if (havememcpy) /* working with a memcpy */
                {
                  PAINTTRACE_OUT((0,"WM_PAINT 12\n" ));
                  BitBlt( paintdc, 0, 0, clirect.right+1, clirect.bottom+1,
                          workdc, 0,0, SRCCOPY);
                  DeleteObject( SelectObject( workdc, oldbitmap ) );
                  DeleteDC( workdc );
                  PAINTTRACE_OUT((0,"WM_PAINT 13\n" ));
                }

                EndPaint(hwnd, &ps);
                done_paint = 1;
              } /* if (paintdc) */
            } /* if (console->hfont) */
          } /* if (clirect.bottom > clirect.top) */ /* rollup support */
        } /* if (console) */
        PAINTTRACE_OUT((0,"WM_PAINT 14\n" ));
        if (hwnd == GetFocus())
          __win16AdjustCaret(hwnd, console, 1);
        PAINTTRACE_OUT((0,"WM_PAINT 15\n" ));
        if (!done_paint)
          ValidateRect(hwnd, NULL); /* don't send more paint msgs if paint fails */
        PAINTTRACE_OUT((0,"WM_PAINT 16 (end)\n" ));

        #if 0
        {
          char scratch[256];
          GetWindowRect(hwnd,&clirect);
          sprintf(scratch,"(%d-%d)-(%d-%d) %dx%d, ",
                    clirect.top,clirect.left,clirect.bottom,clirect.right,
                    clirect.right-clirect.left,clirect.bottom-clirect.top );
          GetClientRect(hwnd,&clirect);
          sprintf(&scratch[strlen(scratch)],"(%d-%d)-(%d-%d) %dx%d",
                    clirect.top,clirect.left,clirect.bottom,clirect.right,
                    clirect.right-clirect.left,clirect.bottom-clirect.top );
          SetWindowText(hwnd,scratch);
        }
        #endif
      }
      break;
    }
    case WM_GETMINMAXINFO:
    {
      MINMAXINFO FAR * lpmmi = (MINMAXINFO FAR *) lParam;
      #if defined(__WINDOWS_386__) /* convert 16:16 pointer to 16:32 */
         lpmmi = (MINMAXINFO FAR *)MK_FP32( (void *)lpmmi );
      #endif

      #if 0
      if (console && console->smoothsizing == 0)
      {
        lpmmi->ptMaxSize.x =
        lpmmi->ptMaxTrackSize.x =
        lpmmi->ptMinTrackSize.x = (W16CONS_WIDTH * console->fontx +
                                  2 * GetSystemMetrics(SM_CXFRAME));
        lpmmi->ptMaxSize.y =
        lpmmi->ptMaxTrackSize.y =
        lpmmi->ptMinTrackSize.y = (W16CONS_HEIGHT * console->fonty +
                                  2 * GetSystemMetrics(SM_CYFRAME) +
                                  GetSystemMetrics(SM_CYCAPTION));
      }
      else
      #endif
      {
        lpmmi->ptMinTrackSize.x = (W16CONS_WIDTH * 2 +
                                  2 * GetSystemMetrics(SM_CXFRAME));
        lpmmi->ptMinTrackSize.y = (W16CONS_HEIGHT * 4 +
                                  2 * GetSystemMetrics(SM_CYFRAME) +
                                  GetSystemMetrics(SM_CYCAPTION));

        lpmmi->ptMaxTrackSize.x = (W16CONS_WIDTH * 12 +
                                  2 * GetSystemMetrics(SM_CXFRAME));
        if (lpmmi->ptMaxTrackSize.x > lpmmi->ptMaxSize.x)
          lpmmi->ptMaxTrackSize.x = lpmmi->ptMaxSize.x;
        else
          lpmmi->ptMaxSize.x = lpmmi->ptMaxTrackSize.x;

        lpmmi->ptMaxTrackSize.y = (W16CONS_HEIGHT * 29 +
                                  2 * GetSystemMetrics(SM_CYFRAME) +
                                  GetSystemMetrics(SM_CYCAPTION));
        if (lpmmi->ptMaxTrackSize.y > lpmmi->ptMaxSize.y)
          lpmmi->ptMaxTrackSize.y = lpmmi->ptMaxSize.y;
        else
          lpmmi->ptMaxSize.y = lpmmi->ptMaxTrackSize.y;
      }
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_WINDOWPOSCHANGING:
    {
      /* This isn't strictly necessary, and is only here to
         prevent the user from using some external utility to
         show the window when the client isn't expecting it.
         Trying to adjust the rect from here will NOT work:
         Neither IsIconic() nor IsZoomed() are meaningful
         at this point, and although it might be possible to
         track the syscommand that (may have) brought us here,
         its really not worth it.
      */
      WINDOWPOS FAR *pwp = (WINDOWPOS FAR *)lParam;
      #if defined(__WINDOWS_386__) /* convert 16:16 pointer to 16:32 */
        pwp = (WINDOWPOS FAR *)(MK_FP32((void *)pwp));
      #endif
      if (constatics.hidden && (pwp->flags & SWP_SHOWWINDOW)!=0)
      {
        pwp->flags ^= SWP_SHOWWINDOW;
        pwp->flags |= SWP_HIDEWINDOW;
      }
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_WINDOWPOSCHANGED:
    {
      /* at this point we have an updated IsIconized(), and
         consequently can fix the try stuff. This is a good place
         since no painting has been done yet
      */
      __DoTrayStuff(console, hwnd, +1, "WM_WINDOWPOSCHANGED/WM_NCCALCSIZE" );
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    #if defined(WM_SIZING) && !defined(__WINDOWS_386__) //can't pass 48bit val
    case WM_SIZING:
    {
      __w16AdjustRect( console, hwnd, message, wParam, lParam);
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    #endif
    case WM_SIZE:
    {
      /* we only need to catch SIZE_MAXIMIZED because the others will
         not influence (or not have influenced) the size of the client
         rect
      */
      if (console && (wParam == SIZE_MAXIMIZED /*|| wParam == SIZE_RESTORED*/))
      {
        __w16AdjustRect( console, hwnd, message, wParam, lParam);
        if (wParam == SIZE_RESTORED)
          __conssize_saveupdateload(console, hwnd,0/*saveupdateload=-1,0,+1*/);
        return 0;
      }
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_MOVE:
    {
      __conssize_saveupdateload(console, hwnd, 0 /*saveupdateload=-1,0,+1*/);
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_USER_SHELLNOTIFYICON:
    {
      if (lParam == WM_RBUTTONUP)
      {
        SendMessage(hwnd,WM_RBUTTONUP,0,((LPARAM)0xfedccdefL));
      }
      else if (lParam == WM_LBUTTONDBLCLK)
      {
        PostMessage(hwnd,WM_COMMAND,WMCMD_LAGGYRESTORE,0);
      }                                             //absorb "extra-" click
      else if (lParam == WM_USER_SHELLNOTIFYICON)
      {
        __DoTrayStuff(console, hwnd, +1 /* wParam */, "SHELLNOTIFYICON" );
      }
      break;
    }
    case WM_RBUTTONUP:
    {
      if (!CheckExitRequestTriggerNoIO())
      {
        HMENU hmenu = CreatePopupMenu();
        if (hmenu)
        {
          int modebits = ModeReqIsSet(-1); /* get all */
          int addrestore = 0; /* add "Restore" item */
          HMENU hbench = NULL;
          POINT ptPos;
          int exiting = CheckExitRequestTrigger();
          int intray = 0;

          ptPos.x = LOWORD(lParam); ptPos.y = HIWORD(lParam);
          if ((lParam==((LPARAM)0xffffffffL)) || (lParam==(LPARAM)0XfedccdefL))
          {
            intray = ((lParam == (LPARAM)0XfedccdefL));
            GetCursorPos(&ptPos);
          }
          else
          {
            ClientToScreen(hwnd, &ptPos);
          }

          if ((modebits & MODEREQ_CONFIG)!=0)
          {
            if (intray)
              addrestore = 1;
            else
            {
              AppendMenu(hmenu, (IsClipboardFormatAvailable(CF_TEXT)?MF_ENABLED:MF_GRAYED),
                                WMCMD_PASTE, "Paste");
              AppendMenu(hmenu, ((console && console->have_marked)?(MF_ENABLED):(MF_GRAYED)),
                                WMCMD_COPY, "Copy" );
            }
          }
          else
          {
            int ispaused;
            char oplabel[128];
            const char *oplabelp;

            AppendMenu(hmenu, ((console && console->have_marked)?(MF_ENABLED):(MF_GRAYED)),
                              WMCMD_COPY, "Copy" );
            AppendMenu(hmenu, MF_SEPARATOR, 0, "" );
            AppendMenu(hmenu, ((exiting || modebits & (MODEREQ_FLUSH|MODEREQ_CONFIG))?(MF_GRAYED):(MF_ENABLED)),
                              WMCMD_FLUSH, "F&lush Work" );
            AppendMenu(hmenu, ((exiting || modebits & (MODEREQ_FETCH|MODEREQ_CONFIG))?(MF_GRAYED):(MF_ENABLED)),
                              WMCMD_FETCH, "F&etch Work" );
            AppendMenu(hmenu, ((exiting || modebits & (MODEREQ_FETCH|MODEREQ_FLUSH|MODEREQ_CONFIG))?(MF_GRAYED):(MF_ENABLED)),
                              WMCMD_UPDATE, "&Update Buffers (Fetch and Flush)" );
            AppendMenu(hmenu, MF_SEPARATOR, 0, "" );
            AppendMenu(hmenu, ((exiting || intray || (modebits & (MODEREQ_CONFIG)))?(MF_GRAYED):(MF_ENABLED)),
                              WMCMD_CONFIG, "Con&figure" );
            if (!exiting && !intray && (modebits & (MODEREQ_CONFIG|MODEREQ_BENCHMARK)) == 0)
              hbench = CreatePopupMenu();
            if (hbench)
            {
              unsigned int contest;
              int mpos = WMCMD_BENCHMARK;
              AppendMenu(hbench, MF_ENABLED, mpos++, "All long" );
              AppendMenu(hbench, MF_ENABLED, mpos++, "All short" );
              for (contest = 0;contest < CONTEST_COUNT; contest++)
              {
                int ok2bench = (IsProblemLoadPermitted(-1,contest)?(MF_ENABLED):(MF_GRAYED));
                oplabelp = CliGetContestNameFromID(contest);
                sprintf(oplabel,"%s long", oplabelp );
                AppendMenu(hbench, ok2bench, mpos++, oplabel );
                sprintf(oplabel,"%s short", oplabelp );
                AppendMenu(hbench, ok2bench, mpos++, oplabel );
              }
            }
            AppendMenu(hmenu, ((intray || !hbench)?(MF_GRAYED):(MF_POPUP)),
                                  (UINT)hbench, "&Benchmark" );
            AppendMenu(hmenu, MF_SEPARATOR, 0, "" );

            ispaused = CheckPauseRequestTriggerNoIO();
            oplabelp = ((ispaused)?("Contin&ue"):("&Pause"));
            if (!exiting && modebits)
            {
              sprintf(oplabel,"%s (may be delayed)", oplabelp);
              oplabelp = (const char *)&oplabel[0];
            }
            AppendMenu(hmenu, ((exiting)?(MF_GRAYED):(MF_ENABLED)),
              ((ispaused)?(DNETC_WCMD_UNPAUSE):(DNETC_WCMD_PAUSE)), oplabelp );
            AppendMenu(hmenu, MF_ENABLED, DNETC_WCMD_RESTART, "&Restart");

            #if 0 //(CLIENT_OS == OS_WIN32)
            if (win32CliIsServiceInstalled()==0) /* <0=err,0==no,>0=yes */
            {
              char buff[64];
              long ver = winGetVersion();
              sprintf(buff, "&Install as %s service", ((ver < 2000) ? "Win9x" : "WinNT"));
              AppendMenu(hmenu, MF_SEPARATOR, 0, "" );
              AppendMenu(hmenu, ((modebits)?(MF_GRAYED):(MF_ENABLED)),
                               WMCMD_SVCINSTALL, buff );
              AppendMenu(hmenu, MF_SEPARATOR, 0, "" );
            }
            #endif
            AppendMenu(hmenu, ((exiting)?(MF_GRAYED):(MF_ENABLED)),
                         DNETC_WCMD_SHUTDOWN,
                         ((exiting)?("Shutdown (is pending)"):("Shutdown")));
            if (intray && !exiting)
            {
              AppendMenu(hmenu, MF_SEPARATOR, 0, "" );
              addrestore = 1;
            }
          }
          if (addrestore && !exiting)
            AppendMenu(hmenu, MF_ENABLED, WMCMD_RESTORE, "Restore");
          SetForegroundWindow(hwnd);  //needed for proper focus on tray popup
          TrackPopupMenu(hmenu, TPM_LEFTALIGN, ptPos.x, ptPos.y, 0, hwnd, NULL);
          PostMessage(hwnd, WM_NULL, 0, 0);
          if (hbench)
            DestroyMenu(hbench);
          DestroyMenu(hmenu);
        }
      }
      break;
    }
    case WM_COMMAND:
    {
      LRESULT lResult;
      if (__w16WindowHandle_DNETC_WCMD(hwnd,message,wParam,lParam,&lResult))
        return lResult;

      if (wParam == WMCMD_LAGGYRESTORE) /* called from tray */
      {
        #if (CLIENT_OS == OS_WIN32)
        Sleep(300); //absorb "extra-" click
        POINT ptPos;
        ShowWindow(hwnd, SW_RESTORE);
        SetForegroundWindow(hwnd);
        BringWindowToTop(hwnd);
        SetFocus(hwnd);
        GetCursorPos(&ptPos);
        PostMessage( hwnd, WM_SYSCOMMAND, SC_RESTORE,
             ((LONG)(((WORD)(ptPos.x))|((DWORD)((WORD)(ptPos.y)))<<16)) );
        #endif
      }
      else if (wParam == WMCMD_RESTORE)
        ShowWindow(hwnd,SW_RESTORE);
      else if (wParam == WMCMD_PASTE)
        SendMessage( hwnd, WM_CHAR, 22 /* ^V */, 0);
      else if (wParam == WMCMD_COPY)
        SendMessage( hwnd, WM_CHAR, 24 /* ^X */, 0);
      else if (wParam == WMCMD_CONFIG)
      {
        __clear_marked(console);
        ModeReqSet(MODEREQ_CONFIG | MODEREQ_CONFRESTART);
      }
      else if (wParam == WMCMD_SVCINSTALL)
      {
        __clear_marked(console);
        #if (CLIENT_OS == OS_WIN32)
        win32CliInstallService(0); // == 0) /* no err */
        {
        /*
          if (IDYES == MessageBox( NULL,
              "The client is now installed as a service.\n",
               "Would you now like to restart the client to let it run as a service?",
               "distributed.net RC5DES client", MB_YESNO|MB_TASKMODAL ) )
          {
            //what we need to do is start a new process AFTER shutting down
            //the main thread but BEFORE we (the winproc) die. very hairy.
          }
        */
        }
        #endif
      }
      else if (wParam == WMCMD_FETCH)
      {
        __clear_marked(console);
        ModeReqSet(MODEREQ_FETCH);
      }
      else if (wParam == WMCMD_FLUSH)
      {
        __clear_marked(console);
        ModeReqSet(MODEREQ_FLUSH);
      }
      else if (wParam == WMCMD_UPDATE)
      {
        __clear_marked(console);
        ModeReqSet(MODEREQ_FETCH|MODEREQ_FLUSH);
      }
      else if (wParam >= WMCMD_BENCHMARK &&
               wParam <  (WMCMD_BENCHMARK+2+(CONTEST_COUNT*2)))
      {
        int do_mode = MODEREQ_BENCHMARK;
        if (((wParam - WMCMD_BENCHMARK) & 1) != 0)
          do_mode = MODEREQ_BENCHMARK_QUICK;
        __clear_marked(console);
        ModeReqSet(do_mode);
        if (wParam >= (WMCMD_BENCHMARK+2))
          ModeReqLimitProject(do_mode, (wParam-(WMCMD_BENCHMARK+2))>>1);
      }
      break;
    }
    case WM_KEYUP:
    {
      if ( console && ((int)(wParam)) == VK_INSERT &&
         ( GetKeyState(VK_SHIFT) & 0x8000 ) != 0 )  //shift-insert
      {
        wParam = 22; //^V
        message = WM_CHAR;
        //fall through!
      }
      else if ( console && ((int)(wParam)) == VK_F1 && ModeReqIsSet(-1) == 0 )
      {
        POINT ptPos; RECT rc;
        GetCursorPos(&ptPos);
        GetWindowRect(hwnd, &rc);
        if ((ptPos.x < rc.left) || (ptPos.x > rc.right) ||
            (ptPos.y < rc.top) || (ptPos.y > rc.bottom)) /*mouse is outside*/
        {
          GetClientRect(hwnd, &rc);
          ptPos.y = rc.top + (rand()%(rc.bottom-rc.top));
          ptPos.x = rc.left + (rand()%(rc.right-rc.left));
        }
        else
          ScreenToClient(hwnd, &ptPos);
        PostMessage(hwnd, WM_RBUTTONUP, 0, (((LPARAM)(ptPos.y))<<16)+ptPos.x);
        break;
      }
      else
      {
        return DefWindowProc(hwnd, message, wParam, lParam);
      }
    }
    case WM_CHAR:
    {
      if (console)
      {
        int kbuffsize = (int)(sizeof(console->keybuff)/sizeof(console->keybuff[0]));

        if (((int)(wParam)) == 0x03 /* ^C */ && console->have_marked)
          wParam = 24; /* convert to ^X since copy accel is usually ^C */

        if (((int)(wParam)) == VK_PAUSE &&
            ((lParam & KF_EXTENDED) != 0))    // pause/resume
        {
          if (CheckPauseRequestTriggerNoIO())
            ClearPauseRequestTrigger();
          else
            RaisePauseRequestTrigger();
        }
        else if (((int)(wParam)) == ('Q'-'A')) //^Q = resume
        {
          ClearPauseRequestTrigger();
        }
        else if (((int)(wParam)) == ('S'-'A')) //^S = pause
        {
          RaisePauseRequestTrigger();
        }
        else if (((int)(wParam)) == 0x03) //^C = break
        {
          RaiseExitRequestTrigger();
        }
        else if ( ((int)(wParam)) == 22 ) //^V = paste
        {
          if (ModeReqIsSet(MODEREQ_CONFIG))
          {
            if (IsClipboardFormatAvailable(CF_TEXT))
            {
              if (OpenClipboard(hwnd))
              {
                HGLOBAL hglb = GetClipboardData(CF_TEXT);
                if (hglb != NULL)
                {
                  #if defined(__WINDOWS_386__)
                  char far *lptstr = NULL;
                  void *_lptstr = (void *)GlobalLock(hglb);
                  if (_lptstr) lptstr = (char far *)(MK_FP32(_lptstr));
                  #else
                  LPTSTR lptstr = (LPTSTR)GlobalLock(hglb);
                  #endif
                  if (lptstr)
                  {
                    int len = 0, pos = 0;
                    while (pos < kbuffsize)
                    {
                      char c = lptstr[pos++];
                      if (!c || c == '\r' || c == '\n')
                        break;
                      console->keybuff[len++] = ((int)c) & 0xff;
                    }
                    if (len)
                    {
                      __clear_marked(console);
                      console->keycount = len;
                    }
                    GlobalUnlock(hglb);
                  }
                }
                CloseClipboard();
              }
            }
          }
        }
        else if (((int)(wParam)) == 24)   //^X = copy
        {
          if (console->have_marked)
          {
            const char *failmsg = NULL;
            char *copybuff = (char *)malloc(W16CONS_HEIGHT*(W16CONS_WIDTH+2));
            if (!copybuff)
              failmsg = "Insufficent memory for copy operation";
            else
            {
              int pos, copylen = 0;
              for (pos = 0; pos < console->disprows; pos++)
              {
                if (console->marked_buff[pos]) /* row is marked */
                {
                  int colpos, spcpos, linelen;
                  if (copylen) /* previous line needs '\n' */
                  {
                    // append linebreak to existing buffer contents.
                    copybuff[copylen++] = '\r';
                    copybuff[copylen++] = '\n';
                  }
                  // find end of actual non-blank characters.
                  spcpos = linelen = 0;
                  for (colpos = 0; colpos < console->dispcols; colpos++)
                  {
                    char c = console->buff[pos][colpos];
                    if (c == '\0')
                      break;
                    if (c == ' ' )
                      spcpos++;
                    else
                    {
                      linelen += spcpos+1;
                      spcpos = 0;
                    }
                  }
                  // copy the line to the buffer.
                  for (colpos = 0; colpos < linelen; colpos++)
                    copybuff[copylen++] = console->buff[pos][colpos];
                }
              }
              if (copylen)
              {
                if (!OpenClipboard(hwnd))
                  failmsg = "Unable to open clipboard for copy operation";
                else
                {
                  HGLOBAL hglb = GlobalAlloc(GMEM_MOVEABLE|GMEM_DDESHARE,copylen+1);
                  if (!hglb)
                    failmsg = "Unable to allocate global memory for copy operation";
                  else
                  {
                    #if defined(__WINDOWS_386__)
                    char far *lptstr = NULL;
                    void *_lptstr = (void *)GlobalLock(hglb);
                    if (_lptstr) lptstr = (char far *)(MK_FP32(_lptstr));
                    #else
                    char * lptstr = (char *)GlobalLock(hglb);
                    #endif
                    if (!lptstr)
                      failmsg = "Unable to lock global memory for copy operation";
                    else
                    {
                      for (pos=0;pos<copylen;pos++)
                        *lptstr++ = copybuff[pos];
                      *lptstr = 0;
                      GlobalUnlock(hglb);
                      EmptyClipboard();
                      if (!SetClipboardData(CF_TEXT,hglb))
                      {
                        failmsg = "Unable to set clipboard data";
                        GlobalFree(hglb);
                      }
                    }
                  }
                  CloseClipboard();
                }
              } /* if copylen */
              free((void *)copybuff);
            } /* if (buf) */
            if (failmsg)
            {
              MessageBox( NULL, failmsg, W32CLI_CONSOLE_NAME" ", MB_ICONHAND);
            }
            else
            {
              __clear_marked(console);
              InvalidateRect(hwnd,NULL,FALSE);
            }
          } /* if (console->have_marked) */
        }
        else if (console->keycount < kbuffsize)
        {
          // anything else.
          console->keybuff[console->keycount++] = (int) wParam;
          if (console->have_marked)
            __clear_marked(console);
        }
        else
        {
          // beep, keyboard buffer full
          MessageBeep(MB_OK);
        }
      }
      break;
    }
    case WM_SETFOCUS:
    {
      LRESULT lResult = DefWindowProc(hwnd, message, wParam, lParam);
      if (console)
        __win16AdjustCaret(console->hwnd, console, 0 );
      return lResult;
    }
    case WM_KILLFOCUS:
    {
      if (console)
        DestroyCaret();
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
    case WM_USER_W16CONS:
    {
      if (wParam == W16CONS_CMD_ECHOLPARAM) /* identify me */
        return lParam;
      if (!console)
      {
        //__w16writelog("(WM_USER+0) 0x%04x called when no console.", (int)wParam);
        return -1;
      }
      switch (wParam)
      {
        case W16CONS_CMD_INDIRDESTROY: // we need this because we can't directly
        {                          // send a WM_DESTROY from another thread
          //SendMessage(hwnd, WM_DESTROY, 0, 0 );
          DestroyWindow(hwnd);
          return 0;
        }
        case W16CONS_CMD_CLEARSCREEN:
        {
          memset((void *)(&(console->buff[0][0])),' ',sizeof(console->buff));
          __clear_marked(console);
          console->currow = 0;
          console->curcol = 0;

          InvalidateRect(hwnd, NULL, FALSE);
          UpdateWindow(hwnd);
          break;
        }
        case W16CONS_CMD_PRINTSTR:
        {
          char ch;
          const char *text = (const char *)lParam;

          do{
            // check the validity of the current
            // cursor position, scrolling the window if necessary.

            if (console->currow < 0)
              console->currow = console->curcol = 0;
            else if (console->curcol < 0)
              console->curcol = 0;
            if (console->curcol >= W16CONS_WIDTH)
            {
              console->curcol = 0;
              console->currow++;
            }
            if (console->currow >= W16CONS_HEIGHT)
            {
              memmove( &(console->buff[0][0]),
                      (&(console->buff[0][0])) + W16CONS_WIDTH,
                        sizeof(console->buff) - W16CONS_WIDTH );
              memset( &(console->buff[W16CONS_HEIGHT-1][0]),' ',W16CONS_WIDTH);
              if (console->have_marked)
              {
                int row;
                if (console->mark_lastrow > 0)
                  --console->mark_lastrow;
                console->have_marked = 0;
                for (row = 0; row < (W16CONS_HEIGHT - 1); row++)
                {
                  console->marked_buff[row] = console->marked_buff[row+1];
                  if (console->marked_buff[row])
                    console->have_marked = 1;
                }
                console->marked_buff[W16CONS_HEIGHT-1]=0;
              }
              console->currow = W16CONS_HEIGHT - 1;
              console->curcol = 0;
            }

            //update the screen buffer

            ch = *text++;
            if (!ch)
              break;
            if (ch == 0x0A)         /* \n  new-line */
            { console->currow++; console->curcol = 0; }
            else if (ch == 0x0D)   /* \r    carriage return */
              console->curcol = 0;
            else if (ch == '\t')   /* \t    horizontal tab  */
              console->curcol += (console->curcol%8);
            else if (ch == '\a')   /* \a  bell (alert)      */
              MessageBeep(MB_OK);
            else if (ch == '\f')   /* \f    form-feed       */
            {
              memset((void *)(&(console->buff[0][0])),' ',sizeof(console->buff));
              console->currow = 0;
              console->curcol = 0;
            }
            else if (ch == '\b')   /* \b  backspace         */
            { if (console->curcol > 0) console->curcol--; }
            else if (ch == '\v')   /* \v  vertical tab      */
              console->currow++;
            else if (ch <= 26  )   /* don't print other ctrl-chars */
              ;
            else
            {
              console->buff[console->currow][console->curcol++] = ch;
            }
          } while (ch); /* always true */

          // force repaint
          InvalidateRect(hwnd, NULL, FALSE);
          //UpdateWindow(hwnd);
          break;
        }
        case W16CONS_CMD_ISKBHIT:
        {
          return ((console->keycount > 0)?(1):(0));
        }
        case W16CONS_CMD_GETSIZE:
        {
          return MAKELRESULT(W16CONS_HEIGHT,W16CONS_WIDTH);
        }
        case W16CONS_CMD_GETPOS:
        {
          return MAKELRESULT(console->currow,console->curcol);
        }
        case W16CONS_CMD_SETPOS:
        {
          int row = LOWORD(lParam);
          int col = HIWORD(lParam);

          if (row < 0)
            row = 0;
          else if (row >= W16CONS_HEIGHT)
            row = W16CONS_HEIGHT-1;
          if (col < 0)
            col = 0;
          else if (col >= W16CONS_WIDTH)
            col = W16CONS_WIDTH-1;

          console->currow = row;
          console->curcol = col;

          if (hwnd == GetFocus())
            __win16AdjustCaret(hwnd, console, 1 );
          break;
        }
        case W16CONS_CMD_GETCH:
        {
          LRESULT keyval = -1L;
          if (console->keycount > 0)
          {
            int c = console->keybuff[0];
            keyval = (LRESULT)(c & 0xff);
            if (!keyval)
              console->keybuff[0] = (((int)(c >> 8)) & 0xff);
            else
            {
              for (c = 1; c < console->keycount; c++)
                console->keybuff[c-1] = console->keybuff[c];
              --console->keycount;
            }
          }
          return keyval;
        }
      } /* switch (wParam) */
    }
    default:
    {
      #if (CLIENT_OS == OS_WIN32)
      static UINT taskbarCreatedMsg = WM_USER;
      if (taskbarCreatedMsg == WM_USER)
      {
        taskbarCreatedMsg = 0;
        if ((winGetVersion() % 2000) >= 400)
          taskbarCreatedMsg = RegisterWindowMessage("TaskbarCreated");
      }
      if (taskbarCreatedMsg && message == taskbarCreatedMsg)
      {
        __DoTrayStuff(console, hwnd, +1, "TaskbarCreated" );
        return 0;
      }
      #endif

      FLOWTRACE_OUT((0, "defwndproc: msg: 0x%04x, wParam: 0x%04x, lParam: 0x%08x\n", message, wParam, lParam ));
      return DefWindowProc(hwnd, message, wParam, lParam);
    }
  }
  return 0;
}
LRESULT CALLBACK __w16WindowFunc(HWND hwnd, UINT message,
                                 WPARAM wParam, LPARAM lParam)
{
  static nestinglevel = 0;
  LRESULT lResult;

  ++nestinglevel;
  FLOWTRACE_OUT((+1, "__w16WindowFunc: nesttinglevel=%d, msg: 0x%04x, wParam: 0x%04x, lParam: 0x%08x\n", nestinglevel, message, wParam, lParam ));
  lResult = __w16WindowFuncInternal(nestinglevel, hwnd, message, wParam, lParam );
  FLOWTRACE_OUT((-1, "__w16WindowFunc: lResult=%ld (0x%lx)\n", lResult, lResult ));
  --nestinglevel;

  return lResult;
}

/* ------------------------------------------------ */

void w16Yield(void)
{
  MSG msg;
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.asthread || constatics.devpipe || constatics.nativecons)
  {
    Sleep(1);
    return;
  }
  #endif
  while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE ))
  {
    if (!GetMessage(&msg, NULL, 0, 0))
      break;
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }
  return;
}

/* ------------------------------------------------ */

static void w16Sleep(unsigned int millisecs)
{
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.asthread || constatics.devpipe || constatics.nativecons)
  {
    Sleep(millisecs);
    return;
  }
  #endif
  if (millisecs > 50)
  {
    UINT hTimer = (UINT)SetTimer(NULL,0, (UINT)millisecs, NULL);
    if (hTimer)
    {
      MSG msg;
      while (GetMessage(&msg, NULL, 0, 0))
      {
        if (msg.message == WM_TIMER)
          break;
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
      KillTimer(NULL,hTimer);
      return;
    }
  }
  {
    DWORD now = 0, last = 0;
    do
    {
      w16Yield();
      if (millisecs)
      {
        now = GetTickCount();
        if (last)
        {
          DWORD diff = (now>=last)?(now-last):(now+(0xfffffffful-last)+1);
          if (diff >= millisecs)
            millisecs = 0;
          else
            millisecs -= diff;
        }
        last = now;
      }
      #if (CLIENT_OS == OS_WIN32)
      Sleep(50);
      #else
      Yield();
      #endif
    } while (millisecs);
  }
  return;
}

/* ------------------------------------------------ */

struct helperArg
{
  int nCmdShow;
  int nSuccess;
  int asthread;
  HWND hwnd;
  HINSTANCE hInstance;
};

/* ------------------------------------------------ */

static void __win16WinCreateHelper( void *xarg )
{
  struct helperArg *arg = (struct helperArg *)(xarg);

  int nCmdShow = arg->nCmdShow;
  int asthread = arg->asthread;
  HINSTANCE hInstance = arg->hInstance;
  LPCSTR wintitle;
  DWORD winstyle;
  HWND hwnd;
  MSG msg;

  if (asthread)
  {
    #if (CLIENT_OS == OS_WIN32)
    {
      //SetPriorityClass( GetCurrentProcess(), NORMAL_PRIORITY_CLASS );
      //SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_NORMAL );
      SetGlobalPriority(9);
    }
    #endif
  }

  wintitle = W32CLI_CONSOLE_NAME" ";
  //wintitle = "console";
  //if (winGetInstanceArgv())
  //  wintitle = (LPCSTR)(winGetInstanceArgv()[0]);

  constatics.createflag = -1;
  constatics.errorcode = 0;

  #if (CLIENT_OS == OS_WIN32)
  if (nCmdShow == SW_SHOWDEFAULT)
  {
    STARTUPINFO si;
    si.cb = sizeof(si);
    GetStartupInfo(&si);
    if (si.wShowWindow)
      nCmdShow = si.wShowWindow;
    if (nCmdShow == SW_SHOWDEFAULT)
      nCmdShow = SW_SHOWNORMAL;
  }
  #endif
  constatics.nCmdShow = nCmdShow;
  winstyle = WS_OVERLAPPEDWINDOW;
  #if 0
  if (nCmdShow == SW_MINIMIZE)
    winstyle |= WS_MINIMIZE;
  else if (nCmdShow == SW_MAXIMIZE)
    winstyle |= WS_MAXIMIZE;
  #endif

  /* create a window using that class */
  hwnd = CreateWindow( constatics.szClassName, wintitle, winstyle,
                       CW_USEDEFAULT, CW_USEDEFAULT, 0, 0,
                       NULL, NULL, hInstance, NULL );

  if (hwnd == NULL)
  {
    if (constatics.errorcode == 0)
      constatics.errorcode = W16CONS_ERR_CREATEWIN;
    arg->nSuccess = 0;
    return;
  }

  while (constatics.createflag == -1)
  {
    #if (CLIENT_OS == OS_WIN32)
    Sleep(1);
    #else
    Yield();
    #endif
  }

  if (constatics.createflag == 0)
  {
    DestroyWindow(hwnd);
    arg->nSuccess = 0;
    return;
  }

  arg->hwnd = hwnd;
  arg->nSuccess = 1;

  ShowWindow( hwnd, nCmdShow ); /* last state */
  //UpdateWindow(hwnd);


  if (asthread)
  {
    while (GetMessage(&msg, (HWND) NULL, 0, 0))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }

  return;
}

/* ------------------------------------------------ */

HWND w16ConsoleCreate(int nCmdShow)
{
  struct helperArg arg;
  int newclass = 0;
  WNDCLASS wcl;
  memset((void *)&wcl,0,sizeof(wcl));

  constatics.errorcode = 0;
  arg.hInstance = winGetInstanceHandle(); /* w32pre.cpp */

  if (!arg.hInstance)
  {
    constatics.errorcode = W16CONS_ERR_GETINST;
    return 0;
  }
  if (constatics.hwndList[0])
  {
    constatics.errorcode = W16CONS_ERR_NOSLOT;
    return 0;
  }

  if (constatics.szClassName[0]==0)
  {
    strcpy(constatics.szClassName,"DCTICLI");
    #ifdef __WINDOWS_386__ //win16 pmode - cannot share memory
    sprintf( constatics.szClassName, "DCTI%u", arg.hInstance );
    #endif

    constatics.hIcon = LoadIcon(arg.hInstance, MAKEINTRESOURCE(1));
    constatics.iconisstock = 0;
    if (!constatics.hIcon)
    {
      LoadIcon(NULL, MAKEINTRESOURCE(IDI_APPLICATION));
      constatics.iconisstock = 1;
    }

    /* define a window class */
    wcl.hInstance = arg.hInstance;
    wcl.lpszClassName = constatics.szClassName;
    wcl.lpfnWndProc = (WNDPROC)__w16WindowFunc;
    wcl.style = CS_BYTEALIGNCLIENT | CS_HREDRAW | CS_VREDRAW;
                 /* (CS_HREDRAW | CS_VREDRAW | CS_SAVEBITS |
                 CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW); */
    wcl.hIcon = constatics.hIcon;
    wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcl.lpszMenuName = NULL;
    wcl.cbClsExtra = 0;
    wcl.cbWndExtra = sizeof(void *); /* we need space for a pointer */
    wcl.hbrBackground = NULL; //(HBRUSH)GetStockObject(NULL_BRUSH);

    /* register the window class */
    if (!RegisterClass(&wcl))
    {
      constatics.szClassName[0]=0;
      if (!constatics.iconisstock)
        DestroyIcon( constatics.hIcon );
      constatics.hIcon = NULL;
      constatics.errorcode = W16CONS_ERR_REGCLASS;
      return 0;
    }
    newclass = 1;
  }

  arg.nSuccess = -1;
  arg.nCmdShow = nCmdShow;
  arg.asthread = 0;

  #if (CLIENT_OS == OS_WIN32)
  constatics.asthread = arg.asthread = 1;
  if (_beginthread( __win16WinCreateHelper, 0/*(1024*32)*/, (void *)&arg  ) == NULL)
  {
    //constatics.errorcode = W16CONS_ERR_CREATETHREAD;
    //arg.nSuccess = 0;
    constatics.asthread = arg.asthread = 0;
  }
  #endif
  if (arg.asthread == 0)
    __win16WinCreateHelper( (void *)&arg );

  while (arg.nSuccess == -1)
    w16Yield();

  if (arg.nSuccess == 0)
  {
    if (newclass && constatics.szClassName[0])
    {
      if (UnregisterClass( constatics.szClassName, arg.hInstance ) == 0)
      {
        constatics.szClassName[0] = 0;
        if (constatics.hIcon && !constatics.iconisstock)
          DestroyIcon( constatics.hIcon );
        constatics.hIcon = NULL;
      }
    }
    constatics.asthread = 0;
    return 0;
  }

  constatics.hwndList[0] = arg.hwnd;
  return arg.hwnd;
}

/* ------------------------------------------------ */

int w16ConsoleDestroy(void)
{
  HWND hwnd = constatics.hwndList[0];
  if (hwnd)
  {
    constatics.hwndList[0] = NULL;
    //this way because we can't directly send a WM_DESTROY to another thread
    SendMessage( hwnd, WM_USER_W16CONS, W16CONS_CMD_INDIRDESTROY, 0 );

    #if 0 /* doesn't work. why? */
    if (constatics.szClassName[0])
    {
      if (UnregisterClass( constatics.szClassName, winGetInstanceHandle() ) == 0)
      {
        constatics.szClassName[0] = 0;
        if (constatics.hIcon && !constatics.iconisstock)
          DestroyIcon( constatics.hIcon );
        constatics.hIcon = NULL;
      }
    }
    #endif
    return 1;
  }
  return 0;
}

/* ------------------------------------------------ */

void w16SetConsoleTitle(const char *name)
{
  w16Yield();
  if (constatics.hwndList[0] == NULL)
    return;
  SetWindowText( constatics.hwndList[0], (LPSTR)name );
}

/* ------------------------------------------------ */

int w16HaveWindow(void)
{
  return (constatics.hwndList[0] != NULL);
}

/* ------------------------------------------------ */

int w16ConsoleClear(void)
{
  w16Yield();
  if (constatics.hwndList[0] == NULL)
    return -1;
  SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_CLEARSCREEN, 0 );
  return 0;
}

/* ------------------------------------------------ */

int w16ConsolePrint(const char *text)
{
  unsigned int len;
  w16Yield();
  if (constatics.hwndList[0] == NULL)
    return -1;
  if (!text)
    return -1;
  if ((len = strlen(text)) == 0)
    return 0;
  SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_PRINTSTR, (LPARAM)(text) );
  return len;
}

/* ------------------------------------------------ */

int w16ConSetPos(int col, int row)
{
  w16Yield();
  if (constatics.hwndList[0] == NULL)
    return -1;
  SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_SETPOS, MAKELONG(row, col));
  return 0;
}

/* ------------------------------------------------ */

int w16ConGetPos(int *col, int *row)
{
  w16Yield();
  if (constatics.hwndList[0] == NULL)
    return -1;
  LRESULT cr = SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_GETPOS, 0);
  if (col) *col = HIWORD(cr);
  if (row) *row = LOWORD(cr);
  return 0;
}

/* ------------------------------------------------ */

int w16ConGetSize( int *width, int *height)
{
  if (constatics.hwndList[0] == NULL)
    return -1;
  LRESULT wh = SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_GETSIZE, 0);
  if (width)  *width  = HIWORD(wh);
  if (height) *height = LOWORD(wh);
  return 0;
}

/* ------------------------------------------------ */

int w16ConsoleKbhit(void)
{
  w16Yield();
  if (constatics.hwndList[0] == NULL)
    return 0;
  return SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_ISKBHIT, 0 );
}

/* ------------------------------------------------ */

int w16ConsoleGetch(void)
{
  LRESULT keyval = 0;
  while (constatics.hwndList[0])
  {
    if (SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_ISKBHIT, 0 ))
    {
      keyval = SendMessage( constatics.hwndList[0], WM_USER_W16CONS, W16CONS_CMD_GETCH, 0 );
      if (keyval != -1L) /* huh? no key! */
        break;
      keyval = 0;
    }
    w16Yield();
  }
  return (int)keyval;
}

/* ===================================================== */
/* ************* END OF GUI PRIMITIVES ***************** */
/* ===================================================== */

#if (CLIENT_OS == OS_WIN32)
static HANDLE __pipe_gethandle(int which)
{
  if (which == STD_INPUT_HANDLE)
  {
    if (constatics.devpipein) /* input end of anon pipe */
      return (HANDLE)constatics.devpipein;
  }
  return (HANDLE)constatics.devpipe;  /* pipe is r/w named pipe */
}
static int __pipe_kbhit(void)
{
  char buffer[8];
  DWORD bytesRead, totalBytesAvail, bytesLeftThisMessage;
  if (!PeekNamedPipe(__pipe_gethandle(STD_INPUT_HANDLE),
                       (LPVOID)&buffer[0],
                       sizeof(buffer),
                       &bytesRead,
                       &totalBytesAvail,
                       &bytesLeftThisMessage ))
  {
    if (GetLastError() == ERROR_BROKEN_PIPE)
    {
      RaiseExitRequestTrigger(); /* sigpipe */
      return -1;
    }
    PIPETRACE_OUT((0,"__pipe_kbhit: peekpipe failed"));
  }
  else if (totalBytesAvail)
    return 1;
  return 0;
}
static int __pipe_getchar(int *chP)
{
  char buffer[8];
  DWORD bytesRead;
  int ch = __pipe_kbhit();
  if (ch <= 0)
    return ch;
  if (!ReadFile(__pipe_gethandle(STD_INPUT_HANDLE),
                (LPVOID)&buffer[0], 1, &bytesRead, NULL))
  {
    if (GetLastError() == ERROR_BROKEN_PIPE)
    {
      RaiseExitRequestTrigger(); /* sigpipe */
      return -1;
    }
    PIPETRACE_OUT(( "__pipe_getchar: readpipe failed" ));
  }
  ch = (((int)buffer[0]) & 0xff);
  PIPETRACE_OUT(( "__pipe_getchar: %c (%d,0x%02x)", ch, ch, ch ));
  if (chP)
    *chP = ch;
  return +1;
}
static int __pipe_puts_noclear(const char *msg,unsigned int len)
{
  int totalBytesWritten = 0;
  HANDLE hPipe = __pipe_gethandle(STD_OUTPUT_HANDLE);
  do
  {
    DWORD numberOfBytesWritten = 0;
    if (!WriteFile(hPipe, (LPCVOID)msg, len, &numberOfBytesWritten, NULL))
    {
      // win9x and NT 3.x (ok on 4.x) have a broken pipe implementation
      // which causes WriteFile to _sometimes_ fail with an invalid file
      // handle.
      if (GetLastError() == ERROR_BROKEN_PIPE)
      {
        RaiseExitRequestTrigger(); /* sigpipe */
        return -1;
      }
      PIPETRACE_OUT(("__pipe_puts: writepipe failed"));
      FlushFileBuffers(hPipe); //block
      if (!WriteFile(hPipe, (LPCVOID)msg, len, &numberOfBytesWritten, NULL))
        break;
    }
    totalBytesWritten += numberOfBytesWritten;
    msg += numberOfBytesWritten;
    len -= numberOfBytesWritten;
  } while (len);
  return totalBytesWritten;
}
static int __pipe_puts(const char *msg,unsigned int len)
{
  int rc = 0;
  if (len) /* avoid overhead */
    rc = __pipe_puts_noclear(msg,len);
  if (rc >= 0)
  {
    while (__pipe_kbhit() > 0)
    {
      if (__pipe_getchar(NULL) < 0)
        break;
    }
  }
  return rc;
}
static int __pipe_putchar(int ch)
{
  char msg;
  msg = (char)ch;
  if (__pipe_puts(&msg,1) <= 0)
    return -1;
  return 1;
}
/*
// position and size is always one based,
// unless no tty, in which case the pipe server ansi returns 0,0
*/
static int __pipe_isatty_asserted_is = -1; /* not yet asserted */
static int __pipe_getsizeorpos(int assize, int *width, int *height)
{                                                          /* one based */
  if (__pipe_isatty_asserted_is == 0) /* already determined this */
  {
    if (*width) *width = 0;
    if (*height) *height = 0;
    return 0;
  }
  else
  {
    int which = 0, savex;
    if (!width)
      which = 1;
    for (; which < 2; which++)
    {
      const char *msg;
      while (__pipe_getchar(NULL)>0) /* flush input pipe */
        ;
      if (assize) /* getsize */
        msg = ((which == 0)?("\x1B""[X"):("\x1B""[Y"));
      else /* getpos */
        msg = ((which == 0)?("\x1B""[x"):("\x1B""[y"));
      if (__pipe_puts_noclear(msg,3) != 3)
        return -1; /* broken pipe */
      while (!CheckExitRequestTriggerNoIO())
      {
        int xy, kbstate;
        if ((kbstate = __pipe_getchar(&xy)) < 0)
          return -1; /* broken pipe */
        if (kbstate != 0)
        {
          if (__pipe_isatty_asserted_is < 0) /* haven't determined this yet */
            __pipe_isatty_asserted_is = (xy ? 1 : 0);
          if (which == 0) /* width loop */
          {
            if (xy == 0 && height)
              *height = 0;
            if (xy == 0 || !height)
            {
              if (width) /* should be always true */
                *width = xy;
              return 0; /* we're done */
            }
            savex = xy;
          }
          else           /* height loop */
          {
            if (height)
              *height = xy;
            if (width)
              *width = savex;
            return 0; /* we're done */
          }
          break; /* got an answer, don't wait */
        }
        Sleep(250);
      } /* while (!CheckExitRequestTriggerNoIO()) */
    }
  }
  return -1;
}
static int __pipe_isatty(void) /* < 0 = err, 0=no, >0=yes */
{
  if (__pipe_isatty_asserted_is < 0)
  { int xy;
    __pipe_getsizeorpos(1 /*assize*/, &xy, 0);
  }
  return __pipe_isatty_asserted_is;
}
/* pipe server supports an internal command set similar to ansi:
 * but instead of "esc[num;num'cmd" it uses "esc]cmd'opt...\0"
 * making it possible to send virtually anything
*/
static int __pipe_set_title(const char *title)
{
  char cmdbuffer[128];
  cmdbuffer[0]=((char)0x1B);
  cmdbuffer[1]=']'; /* note reversed bracket */
  cmdbuffer[2]='1'; /* cmd: setconsoletitle */
  strncpy(&cmdbuffer[3],title,sizeof(cmdbuffer)-3);
  cmdbuffer[sizeof(cmdbuffer)-1] = '\0'; /* also, our command char is '\0' */
  return __pipe_puts(cmdbuffer,2+strlen(&cmdbuffer[1]));
}
static int __pipe_advertise_hwnd(HANDLE hwnd)
{
  char cmdbuffer[128];
  cmdbuffer[0]=((char)0x1B);
  cmdbuffer[1]=']'; /* note reversed bracket */
  cmdbuffer[2]='2'; /* cmd: advertise handle */
  sprintf(&cmdbuffer[3],"%lu",(unsigned long)hwnd);
  return __pipe_puts(cmdbuffer,2+strlen(&cmdbuffer[1]));
}
static int __pipe_detach(void)
{
  return __pipe_putchar(0x03);
}
static void __pipe_sleep(unsigned int millisecs)
{
  Sleep(millisecs); /* nothing yet */
}
#endif

/* ===================================================== */
/* ************* END OF PRIMITIVES ********************* */
/* ===================================================== */

int w32DeinitializeConsole(int pauseonclose)
{
  pauseonclose = pauseonclose;
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.hmutex != NULL)
  {
    ReleaseMutex( constatics.hmutex );
    CloseHandle( constatics.hmutex );
    constatics.hmutex = NULL;
  }
  if (constatics.shimwatcher != NULL)
  {                                      // self destroying
    int sleeploops = 0;
    SendMessage( constatics.shimwatcher, WM_USER_W16CONS, W16CONS_CMD_INDIRDESTROY, 0 );
    while ((++sleeploops) < 20 && constatics.shimwatcher)
      Sleep(100);
  }
  if (constatics.devpipe)
  {
    __pipe_set_title(utilGetAppName());
    if (!constatics.devpipein) /* pipe is named pipe */
      CloseHandle(constatics.devpipe);
    else /* pipe is pipe pair of anonpipe */
      constatics.devpipein = NULL;
    constatics.devpipe = NULL;
  }
  else if (constatics.nativecons)
  {
    SetConsoleTitle(utilGetAppName());
    if (constatics.fstdout || constatics.fstdin)
      FreeConsole();
    if (constatics.fstdout)
      fclose(constatics.fstdout);
    constatics.fstdout = NULL;
    if (constatics.fstdin)
      fclose(constatics.fstdin);
    constatics.fstdin = NULL;
  }
  else
  #endif
  {
    if (w16HaveWindow())
    {
      if (pauseonclose)
      {
        int init = 0;
        time_t nowtime = 0, endtime = 0;
        int row = -1, height = 0;
        w16ConGetPos(NULL, &row);
        w16ConGetSize(NULL, &height);
        if (height > 2 && row != -1)
          w16ConSetPos(0, height-((row<(height-2))?(3):(1)));
        do
        {
          int sleeploops;
          nowtime = time(NULL);
          if (endtime == 0)
            endtime = nowtime + 15;
          for (sleeploops = 0;sleeploops < ((!init)?(1):(4));sleeploops++)
          {
            if (sleeploops)
              w16Sleep(250);
            if (w16ConsoleKbhit() || CheckExitRequestTriggerNoIO())
            {
              nowtime = endtime;
              break;
            }
          }
          if (nowtime < endtime)
          {
            char buffer[80];
            sprintf( buffer, "%sPress any key to continue... %d  ",
                     ((!init)?("\n\n"):("\r")), (int)(endtime-nowtime) );
            init = 1;
            w16ConsolePrint( buffer );
          }
        } while (nowtime < endtime);
      }
      w16ConsoleDestroy();
    }
  }

  return 0;
}


/* ---------------------------------------------------- */

#if (CLIENT_OS == OS_WIN32)
#if defined(__WATCOMC__)
static void __w32SigTriggerControl(int sig)
{
  signal(sig,__w32SigTriggerControl);
  RaiseExitRequestTrigger();
  return;
}
#endif
static BOOL WINAPI __w32NativeTriggerControl(DWORD dwCtrlType)
{
  if (dwCtrlType == CTRL_LOGOFF_EVENT && win32CliServiceRunning())
  {
    //should never happen since we don't open a console if running as a service
    return TRUE;
  }
  else if (dwCtrlType == CTRL_BREAK_EVENT)
  {
    RaiseRestartRequestTrigger();
    return TRUE;
  }
  else if ( dwCtrlType == CTRL_C_EVENT )
  {
    RaiseExitRequestTrigger();
    return TRUE;
  }
  else if ( dwCtrlType == CTRL_CLOSE_EVENT || /* totally fscked on Win9x */
            dwCtrlType == CTRL_SHUTDOWN_EVENT || /* none of these are called */
            dwCtrlType == CTRL_LOGOFF_EVENT )
  {
    /* http://support.microsoft.com/support/kb/articles/q130/7/17.asp */
    //no use calling RaiseExitRequestTrigger() from here. We have to terminate
    //here or win will throw up a message box and then call ExitProcess().
    //we have 5 seconds for CLOSE, and 20 secs for SHUTDOWN/LOGOFF
    RaiseExitRequestTrigger();
    while (constatics.client_run_startstop_level > 0)
      Sleep(500);
    //__w16ClientHardStop();
    ExitProcess(0);
    //return TRUE;
  }
  return FALSE; //DBG_CONTROL_C
}
//#define WIN9X_CLOSE_WATCH 5000 /* poll time in millisecs */
#if defined(WIN9X_CLOSE_WATCH) && (CLIENT_CPU == CPU_X86) /* win9x */
static void __win9xCloseWatchTimerHandler(int state)
{
  char cstate;
  if (state != 0)
  {
    cstate = ((state > 0)?(1):(0));
    printf("timer: hellooooooooooooooooo world %d\n", cstate );
    fflush(stdout);
    _asm mov ax, 168Fh
    _asm mov dh, 0 /* fxn: enable/disable sysmenu close */
    _asm mov dl, cstate /* enable or disable */
    _asm int 2fh
  }
  cstate = 0;
  _asm mov ax, 168Fh
  _asm mov dh, 1 /* fxn: check sysmenu close */
  _asm mov dl, 0
  _asm int 2fh  /* => 0=selected/not ack'd, 1=sel/ack'd, else not selected */
  _asm cmp ax, 2
  _asm sbb dx, dx
  _asm and ax, dx
  _asm xor dl, 2
  _asm and dl, 2
  _asm or  al, dl
  _asm mov cstate, al
  if (cstate < 2)
  {
    RaiseExitRequestTrigger();
    _asm mov ax, 168Fh
    _asm mov dh, 2 /* fxn: ack sysmenu close */
    _asm mov dl, 0
    _asm int 2fh
  }
  return;
}
#endif
LRESULT CALLBACK __w32ShimWatcherWProc(HWND hwnd, UINT message, WPARAM wParam,
                                     LPARAM lParam)
{
  static UINT dnetc_cmdmsg = 0;
  LRESULT lResult;
  if (dnetc_cmdmsg && message == dnetc_cmdmsg)
    message = WM_COMMAND;

  if (message == WM_CREATE)
    dnetc_cmdmsg = RegisterWindowMessage(W32CLI_CONSOLE_NAME);
  else if (message == WM_CLOSE)
  {
    RaiseExitRequestTrigger();
    return DNETC_WCMD_ACKMAGIC;
  }
  else if (__w16WindowHandle_DNETC_WCMD(hwnd,message,wParam,lParam,&lResult))
    return lResult;
  else if (message == WM_USER_W16CONS && wParam == W16CONS_CMD_INDIRDESTROY)
  {
    //SendMessage(hwnd, WM_DESTROY, 0, 0 );
    DestroyWindow(hwnd);
    PostQuitMessage(0);
  }
  return DefWindowProc(hwnd,message,wParam,lParam);
}
static void __win32ShimWatcher(void *) /* lives as long as the client */
{
  int normalstop = 0;
  HINSTANCE hInstance = winGetInstanceHandle(); /* w32pre.cpp */

  if (hInstance)
  {
    static int classisreg = 0;
    WNDCLASS wcl;
    /* define a window class */
    wcl.hInstance = hInstance;
    wcl.lpszClassName = "DCTICLISTUB";
    wcl.lpfnWndProc = (WNDPROC)__w32ShimWatcherWProc;
    wcl.style = 0; //CS_HREDRAW | CS_VREDRAW;
    wcl.hIcon = NULL;
    wcl.hCursor = NULL;
    wcl.lpszMenuName = NULL;
    wcl.cbClsExtra = 0;
    wcl.cbWndExtra = 0;
    wcl.hbrBackground = NULL;

    /* register the window class */
    if (RegisterClass(&wcl))
      classisreg = 1;
    if (classisreg)
    {
      HWND hwnd = CreateWindow( wcl.lpszClassName, W32CLI_CONSOLE_NAME,
                            0/*WS_POPUP|WS_CLIPSIBLINGS|WS_OVERLAPPED*/,
                            0, 0, 0, 0, NULL, NULL, wcl.hInstance, NULL );
      if (hwnd)
      {
        MSG msg;
        #if defined(WIN9X_CLOSE_WATCH) && (CLIENT_CPU == CPU_X86)
        UINT win9xclosewatchtimer = 0;
        if (winGetVersion()>=400 && winGetVersion()<2000)
        {
          win9xclosewatchtimer = SetTimer(NULL,0,WIN9X_CLOSE_WATCH,NULL);
          if (win9xclosewatchtimer) __win9xCloseWatchTimerHandler(+1);
        }
        #endif

        //ShowWindow(hwnd, SW_HIDE);

        if (constatics.devpipe)
          __pipe_advertise_hwnd(hwnd);

        constatics.shimwatcher = hwnd;
        while (GetMessage(&msg, hwnd, 0, 0))
        {
          #if defined(WIN9X_CLOSE_WATCH_ENABLED) && (CLIENT_CPU == CPU_X86)
          if (msg.message == WM_TIMER)
            __win9xCloseWatchTimerHandler(0);
          else
          #endif
          {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
          }
        }
        constatics.shimwatcher = NULL;

        if (constatics.devpipe)
          __pipe_advertise_hwnd(NULL);

        if (IsWindow(hwnd))
          DestroyWindow(hwnd);
        #if defined(WIN9X_CLOSE_WATCH_ENABLED) && (CLIENT_CPU == CPU_X86)
        if (win9xclosewatchtimer)
        {
          KillTimer(NULL,win9xclosewatchtimer);
          __win9xCloseWatchTimerHandler(-1);
        }
        #endif
      }
      if (UnregisterClass( wcl.lpszClassName, wcl.hInstance ))
        classisreg = 0;
    }
  }
  return;
}
#endif

/* ---------------------------------------------------- */

int w32InitializeConsole(int runhidden, int runmodes)
{
  const char *wintitle = W32CLI_CONSOLE_NAME;
  int isservicified = 0, retcode = 0;
  char *p; char scratch[256]; /* not smaller! */

  constatics.hidden = (!runmodes && runhidden);
  constatics.nativecons = 0;
  constatics.devpipe = 0;

  INITTRACE_OUT((+1,"w32InitializeConsole(hidden=%d, runmodes=%d)\n",runhidden,runmodes));

  //quickly change to a normal cursor
  SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));

  #if (CLIENT_OS == OS_WIN32)
  if (!constatics.hidden && retcode == 0)
  {
    if (win32CliServiceRunning())
    {
      isservicified = 1;
      constatics.hidden = 1;
      INITTRACE_OUT((+0,"service active. forcing hidden"));
    }
  }
  #endif

  // ------------------------------------
  // single instance check
  // ------------------------------------

  if (!runmodes && retcode==0 && getenv("dnetc_multiok")==NULL)
  {
    retcode = w32PostRemoteWCMD( DNETC_WCMD_EXISTCHECK );
    if ((retcode & 0x04)!=0) /* svc flag found */
    {
      if (win32CliServiceRunning()) /* we ourselves are the service */
        retcode ^= 0x04;
    }
    INITTRACE_OUT((+0,"other instance running?=0x%x\n", retcode ));
    if (retcode != 0)
      retcode = -1;
  }

  #if (CLIENT_OS == OS_WIN32)
  if (!runmodes && retcode == 0)
  {
    SECURITY_ATTRIBUTES sa;
    memset(&sa,0,sizeof(sa));
    sa.nLength = sizeof(sa);
    constatics.hmutex = CreateMutex(&sa, FALSE, W32CLI_MUTEX_NAME);
    if (!constatics.hmutex)
      retcode = -1;
  }
  #endif

  // ----------------------------
  // console as CUI or pipe?
  // ----------------------------

  #if (CLIENT_OS == OS_WIN32)
  if (retcode == 0)
  {
    #if defined(USE_NATIVE_CONSOLEIO)
    constatics.nativecons = 1;
    #endif
    if (retcode == 0 && !constatics.nativecons)
    {
      STARTUPINFO si;
      GetStartupInfo( &si );
      if ((si.dwFlags & STARTF_USESTDHANDLES)!=0)
      {
        constatics.nativecons = 1;
        SetStdHandle(STD_OUTPUT_HANDLE,si.hStdOutput);
        SetStdHandle(STD_INPUT_HANDLE,si.hStdInput);
        SetStdHandle(STD_ERROR_HANDLE,si.hStdError);
        SetConsoleTitle(wintitle);
      }
    }
    if (retcode == 0 && !constatics.nativecons)
    {
      DWORD lpMode;
      if (GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE),&lpMode) ||
          GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),&lpMode))
      {
        SetConsoleTitle(wintitle);
        constatics.nativecons = 1;
        //SetStdHandle(STD_OUTPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdout)));
        //SetStdHandle(STD_INPUT_HANDLE, (HANDLE)_get_osfhandle(fileno(stdin)));
        //SetStdHandle(STD_ERROR_HANDLE, (HANDLE)_get_osfhandle(fileno(stdout)));
      }
    }
    if (retcode == 0 && !constatics.nativecons)
    {
      if (GetEnvironmentVariable("dnetc.ttyhandles",scratch,sizeof(scratch)))
      {
        unsigned int which;
        HANDLE inouterr[3];
        p = scratch;
        for (which = 0; which < 3; which++)
        {
          HANDLE h = (HANDLE)atol(p);
          if (*p == '-')
            p++;
          if (!h && *p != '0')
            break;
          inouterr[which]=h;
          if (which == 0 || which == 1)
          {
            while (isdigit(*p))
              p++;
            while (*p && !isdigit(*p))
              p++;
          }
          else
          {
            if (inouterr[0] != INVALID_HANDLE_VALUE)
              SetStdHandle(STD_INPUT_HANDLE, inouterr[0]);
            if (inouterr[1] != INVALID_HANDLE_VALUE)
              SetStdHandle(STD_OUTPUT_HANDLE, inouterr[1]);
            if (inouterr[2] != INVALID_HANDLE_VALUE)
              SetStdHandle(STD_ERROR_HANDLE, inouterr[2]);
            constatics.nativecons = 1;
            SetConsoleTitle(wintitle);
          }
        }
      }
    }

    if (retcode == 0)
    {
      HANDLE pipe = INVALID_HANDLE_VALUE;
      HANDLE inpipe = INVALID_HANDLE_VALUE;
      int anonpipe = 0, gotpipeprompt = 0;

      if (winGetVersion() >= 2000 &&
        GetEnvironmentVariable("dnetc.namedpipe",scratch,sizeof(scratch))!=0)
      {
        gotpipeprompt = 1;
        pipe = CreateFile(scratch, GENERIC_READ|GENERIC_WRITE, 0,
                                   NULL, OPEN_EXISTING, 0, 0 );
      }
      if (!gotpipeprompt &&
        GetEnvironmentVariable("dnetc.apipe.out",scratch,sizeof(scratch))!=0)
      {
        gotpipeprompt = anonpipe = 1;
        if (isdigit(scratch[0]) || (scratch[0] == '-' && isdigit(scratch[1])))
        {
          pipe = (HANDLE)atol(scratch);
          if (pipe != INVALID_HANDLE_VALUE &&
              GetEnvironmentVariable("dnetc.apipe.in",scratch,sizeof(scratch)))
          {
            if (isdigit(scratch[0]) || (scratch[0]=='-' && isdigit(scratch[1])))
              inpipe = (HANDLE)atol(scratch);
          }
        }
        if (inpipe == INVALID_HANDLE_VALUE)
          pipe = INVALID_HANDLE_VALUE;
      }
      if (gotpipeprompt)
      {
        if (pipe != INVALID_HANDLE_VALUE)
        {
          DWORD numberOfBytesWritten;
          if (constatics.hidden || constatics.nativecons)
          {
            __pipe_detach(); /* close the pipe == fork() :) */
            p = "\x03";
            WriteFile(pipe, (LPCVOID)p, 1, &numberOfBytesWritten, NULL);
            if (!anonpipe)
              CloseHandle(pipe);
          }
          else
          {
            int handle;
            __pipe_set_title(W32CLI_CONSOLE_NAME);
            constatics.devpipe = (void *)pipe;
            if (anonpipe)
              constatics.devpipein = (void *)inpipe;
            else
              inpipe = pipe;
            SetStdHandle(STD_OUTPUT_HANDLE, pipe);
            SetStdHandle(STD_ERROR_HANDLE, pipe);
            //SetStdHandle(STD_INPUT_HANDLE, inpipe);
            handle = _open_osfhandle((long)pipe, O_WRONLY|O_TEXT);
            if (handle != -1)
            {
              dup2(handle,1);
              dup2(handle,2);
            }
          }
        }
        else if (!constatics.nativecons) /* no error if we already have a con */
        {
          if (!constatics.hidden)
            w32ConOutErr( "Unable to open client end of console pipe" );
          retcode = -1;
        }
      }
    }

    if (retcode == 0 && !constatics.nativecons &&
        !constatics.hidden && !constatics.devpipe )
    {                     //use guicode (don't allocconsole) if we're hidden
      if (winGetMyModuleFilename(scratch,sizeof(scratch)) > 4) /* win32pre.cpp */
      {
        if (strcmpi( &scratch[strlen(scratch)-4], ".com" ) == 0)
        {
          p = "Unable to create console window.";
          if (!AllocConsole())
            retcode = -1;
          else
          {
            FILE *hfin, *hfout;
            HWND hwnd;
            p = "Unable to open console for write.";
            retcode = 0;
            sprintf(scratch,"%s - %08x%08x",
                  wintitle, GetTickCount(), GetModuleHandle(NULL));
            SetConsoleTitle(scratch);
            while ((hwnd = FindWindow( NULL, scratch )) == NULL)
            {
              if ((++retcode) > 25)
                break;
              Sleep(40); //Delay needed for title to update
            }
            SetConsoleTitle(wintitle);
            if ( hwnd == NULL )
              retcode = -1;
            else if ((hfout = fopen("CONOUT$", "w+t")) == NULL)
              retcode = -1;
            else if ((hfin = fopen("CONIN$", "rt")) == NULL)
            {
              p = "Unable to open console for read.";
              fclose( hfout );
              retcode = -1;
            }
            else
            {
              retcode = 0;
              constatics.fstdout = hfout;
              constatics.fstdin = hfin;
              *stdout = *hfout;
              setvbuf(stdout, NULL, _IONBF, 0);
              *stderr = *hfout;
              setvbuf(stderr, NULL, _IONBF, 0);
              *stdin = *hfin;
              setvbuf(stdin, NULL, _IONBF, 0);
              constatics.nativecons = 1;
            }
            if (retcode != 0)
              FreeConsole();
          }
          if (p && retcode != 0 && !constatics.hidden)
            w32ConOutErr( p );
        }
      }
    }

    if (retcode == 0 && constatics.nativecons)
    {
      DWORD dwConMode;
      #if defined(__WATCOMC__)
      int sig;
      for (sig = 0; sig < 255; sig++)
        signal(sig, __w32SigTriggerControl );
      #endif
      dwConMode = ~ENABLE_WINDOW_INPUT;
      //if (GetConsoleMode( GetStdHandle(STD_INPUT_HANDLE),&dwConMode))
      {
        //if ((dwConMode & ENABLE_PROCESSED_INPUT)==0)
        {
          dwConMode |= ENABLE_PROCESSED_INPUT;
          SetConsoleMode( GetStdHandle(STD_INPUT_HANDLE), dwConMode );
        }
      }
      SetProcessShutdownParameters(0x100,SHUTDOWN_NORETRY);
      SetConsoleCtrlHandler((PHANDLER_ROUTINE)__w32NativeTriggerControl,TRUE);

      if (constatics.hidden)
        FreeConsole(); //fork() :)
    }
  }
  #endif /* (CLIENT_OS == OS_WIN32) */

  // ---------------------------
  // console as GUI?
  // ---------------------------

  if (retcode == 0 && !constatics.devpipe && !constatics.nativecons)
  {
    int nCmdShow = winGetInstanceShowCmd();
    if (constatics.hidden)
      nCmdShow = SW_HIDE | WS_DISABLED;

    if (w16ConsoleCreate(nCmdShow))
      w16SetConsoleTitle(wintitle);
    else
    {
      if (!constatics.hidden)
      {
        p = NULL;
        if (constatics.errorcode == W16CONS_ERR_CREATETHREAD)
          p = "create console thread";
        else if (constatics.errorcode == W16CONS_ERR_NOFONT)
         p = "assign font";
        else if (constatics.errorcode == W16CONS_ERR_NOMEM)
          p = "create window data area";
        else if (constatics.errorcode == W16CONS_ERR_CREATEWIN)
          p = "create window client area";
        else if (constatics.errorcode == W16CONS_ERR_NCCREATE)
          p = "create window non-client area";
        else if (constatics.errorcode == W16CONS_ERR_REGCLASS)
          p = "register class";
        else if (constatics.errorcode == W16CONS_ERR_GETINST)
          p = "get instance handle";
        else if (constatics.errorcode == W16CONS_ERR_NOSLOT)
          p = "find a window slot";
        sprintf(scratch,"Unable to create console window.%s%s)",
              ((!p)?(" (Unknown error"):("\n(Failed to ")),
              ((!p)?(""):(p)) );
        w32ConOutErr(scratch);
      }
      retcode = -1;
    }
  }

  if (retcode == 0)
  {
    //quickly change to a normal cursor
    SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)));

    //watch for ClientRun() start and stop events
    ClientEventAddListener( -1, __event_handler );

    #if (CLIENT_OS == OS_WIN32)
    if (constatics.devpipe || constatics.nativecons)
    {
      int havethread = 0;
      if (constatics.shimwatcher)
        havethread = 1;
      else if (_beginthread( __win32ShimWatcher, 512, NULL ))
      {
        while ((++havethread) < 20 && !constatics.shimwatcher)
          Sleep(100);
        if (!constatics.shimwatcher)
          havethread = 0;
      }
      if (havethread) //we don't want the window to be findable
        wintitle = W32CLI_CONSOLE_NAME" ";
      if (constatics.devpipe)
        __pipe_set_title(wintitle);
      else if (constatics.nativecons)
        SetConsoleTitle(wintitle);
    }
    #endif
  }
  #if (CLIENT_OS == OS_WIN32)
  else if (constatics.hmutex)
  {
    ReleaseMutex( constatics.hmutex );
    CloseHandle( constatics.hmutex );
    constatics.hmutex = NULL;
  }
  #endif

  INITTRACE_OUT((-1,"w32InitializeConsole() => retcode=%d\n",retcode));

  return (retcode);
}

/* ================================================================== */
/* ================================================================== */

int w32ConKbhit(void)
{
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.devpipe)
  {
    if (__pipe_kbhit() <= 0)
      return 0;
    return 1;
  }
  else if (constatics.nativecons)
    return (kbhit());
  #endif
  return w16ConsoleKbhit();
}

/* ---------------------------------------------------- */

int w32ConGetch(void)
{
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.devpipe)
  {
    static int hibyte = 0;
    int ch;
    if (hibyte != 0)
    {
      ch = hibyte;
      hibyte = 0;
      return ch;
    }
    while (!CheckExitRequestTriggerNoIO())
    {
      int kbstate = __pipe_getchar(&ch);
      if (kbstate < 0) /* broken pipe */
        break;
      if (kbstate != 0)
      {
        if (ch == 0)
        {
          kbstate = __pipe_getchar(&ch);
          if (kbstate < 0) /* broken pipe */
            break;
          hibyte = ch;
        }
        return ch;
      }
      Sleep(100);
    }
    return 0;
  }
  else if (constatics.nativecons)
  {
    return getch();
  }
  #endif
  return w16ConsoleGetch();
}

/* ---------------------------------------------------- */

static int __w32ConOutX(const char *text, int iserr)
{
  int handled = 0;
  if (win32CliServiceRunning())
  {
    handled = 1;
    if (iserr) /* we don't print anything if !err */
    {
      /* do log stuff for NT here */
      return 0;
    }
    return -1;
  }
  #if (CLIENT_OS == OS_WIN32)
  if (!handled)
  {
    if (constatics.nativecons)
      handled = 1;
    else
    {
      char filename[MAX_PATH+1];
      if (winGetMyModuleFilename(filename, sizeof(filename)) != 0) //w32pre.cpp
      {
        if (winIsGUIExecutable( filename )==0) /*w32util <0=err,0=cui,>0=gui*/
          handled = 1;
      }
    }
    if (handled)
    {
      FILE *file = stdout;
      if (!iserr)
        fprintf( file, "%s\n", text );
      else
      {
        file = stderr;
        fprintf(file,"%s: %s\n", utilGetAppName(), text);
      }
      fflush(file);
    }
  }
  if (!handled && (constatics.devpipe || getenv("dnetc.apipe.out")))
  {
    int needclose = 0;
    if (!constatics.devpipe)
    {
      HANDLE h = (HANDLE)atol(getenv("dnetc.apipe.out"));
      if (h && h!=INVALID_HANDLE_VALUE)
      {
        constatics.devpipe = (void *)h;
        needclose = 1;
      }
    }
    if (constatics.devpipe)
    {
      if (iserr)
      {
        __pipe_puts(utilGetAppName(),strlen(utilGetAppName()));
        __pipe_puts(": ",2);
      }
      __pipe_puts(text,strlen(text));
      __pipe_puts("\n",1);
      if (needclose)
        constatics.devpipe = NULL;
      handled = 1;
    }
  }
  #endif
  if (!handled)
  {
    /* note the spaces around the caption! Don't let this window be "findable" */
    MessageBox(NULL,text, " "W32CLI_CONSOLE_NAME" ",MB_OK|MB_TASKMODAL
                            |(iserr?MB_ICONHAND:MB_ICONINFORMATION));
  }
  return 0;
}

int w32ConOutErr(const char *text)
{ return __w32ConOutX(text, 1); }
int w32ConOutModal(const char *text)
{ return __w32ConOutX(text, 0); }

/* ---------------------------------------------------- */

int w32ConOut(const char *text)
{
  int len = (int)strlen(text);
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.devpipe)
  {
    return __pipe_puts(text,len);
  }
  else if (constatics.nativecons)
  {
    if (len)
      len = fwrite( text, sizeof(char), len, stdout);
    fflush(stdout);
    return len;
  }
  #endif
  if (len)
    len = w16ConsolePrint(text);
  return len;
}

/* ---------------------------------------------------- */

int w32ConIsScreen(void)
{
  if (!constatics.hidden)
  {
    #if (CLIENT_OS == OS_WIN32)
    if (constatics.devpipe)
    {
      if (__pipe_isatty() <= 0) /* <0=err, 0=no, >0=yes */
        return 0;
      return 1;
    }
    if (constatics.nativecons)
      return isatty(fileno(stdout));
    #endif
    return w16HaveWindow();
  }
  return 0;
}

/* ---------------------------------------------------- */

void w32Sleep(unsigned int millisecs)
{
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.devpipe)
  {
    __pipe_sleep(millisecs);
    return;
  }
  if (constatics.nativecons)
  {
    Sleep(millisecs);
    return;
  }
  #endif
  w16Sleep(millisecs);
  return;
}

/* ---------------------------------------------------- */

void w32Yield(void)
{
  #if (CLIENT_OS == OS_WIN32)
  if (constatics.nativecons || constatics.devpipe)
  {
    w32Sleep(1); /* millisecs */
    return;
  }
  #endif
  w16Yield();
  return;
}

/* ---------------------------------------------------- */

int w32ConGetSize( int *width, int *height) /* one based */
{
  if (!constatics.hidden)
  {
    #if (CLIENT_OS == OS_WIN32)
    if (constatics.devpipe)
    {
      int w = 1, h = 1;
      if (__pipe_getsizeorpos(1/*assize*/,((!width)?(NULL):(&w)),
                                          ((!height)?(NULL):(&h))) < 0)
        return -1;
      if (w == 0 || h == 0) /* size will be zero only if no tty */
        return -1;
      if (width)  *width = w;
      if (height) *height = h;
      return 0;
    }
    else if (constatics.nativecons)
    {
      HANDLE hStdout;
      CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

      hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
      if (hStdout == INVALID_HANDLE_VALUE)
        return -1;
      if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
        return -1;
      if (height) *height=csbiInfo.srWindow.Bottom - csbiInfo.srWindow.Top + 1;
      if (width) *width = csbiInfo.srWindow.Right - csbiInfo.srWindow.Left + 1;
      return 0;
    }
    #endif
    return w16ConGetSize( width, height );
  }
  return -1;
}

/* ---------------------------------------------------- */

int w32ConClear(void)
{
  if (!constatics.hidden)
  {
    #if (CLIENT_OS == OS_WIN32)
    if (constatics.devpipe)
    {
      if (__pipe_puts(("\x1B""[2J""\x1B""[1;1H"), 10 ) < 0)
        return -1;
      return 0;
    }
    else if (constatics.nativecons)
    {
      CONSOLE_SCREEN_BUFFER_INFO csbi;
      DWORD nLength, nWritten;
      COORD topleft = {0,0};
      HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
      if (hStdout == INVALID_HANDLE_VALUE)
        return -1;
      if (! GetConsoleScreenBufferInfo(hStdout, &csbi))
        return -1;
      nLength = csbi.dwSize.X * csbi.dwSize.Y;
      FillConsoleOutputCharacter(hStdout, (TCHAR) ' ', nLength, topleft, &nWritten);
      FillConsoleOutputAttribute(hStdout, csbi.wAttributes, nLength, topleft, &nWritten);
      SetConsoleCursorPosition(hStdout, topleft);
      return 0;
    }
    #endif
    return w16ConsoleClear();
  }
  return -1;
}

/* ---------------------------------------------------- */

int w32ConSetPos(int col, int row) /* zero based */
{
  if (!constatics.hidden)
  {
    #if (CLIENT_OS == OS_WIN32)
    if (constatics.devpipe)
    {
      char buffer[64];
      if (__pipe_puts(buffer,
          sprintf(buffer,"\x1B""[%d;%dH", row+1, col+1 )) < 0)
        return -1;
      return 0;
    }
    else if (constatics.nativecons)
    {
      HANDLE hStdout;
      COORD pos = {col,row};
      hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
      if (hStdout == INVALID_HANDLE_VALUE)
        return -1;
      SetConsoleCursorPosition(hStdout, pos);
      return 0;
    }
    #endif
    return w16ConSetPos(col, row);
  }
  return -1;
}

/* ------------------------------------------------ */

int w32ConGetPos(int *col, int *row) /* zero based */
{
  if (!constatics.hidden)
  {
    #if (CLIENT_OS == OS_WIN32)
    if (constatics.devpipe)
    {
      int c = 1, r = 1;
      if (__pipe_getsizeorpos(0/*as pos*/,((!col)?(NULL):(&c)), /* 1 based */
                                          ((!row)?(NULL):(&r))) < 0)
      if (c == 0 || r == 0) /* pos will be zero only if no tty */
        return -1;
      if (col) *col = --c;
      if (row) *row = --r;
      return 0;
    }
    else if (constatics.nativecons)
    {
      HANDLE hStdout;
      CONSOLE_SCREEN_BUFFER_INFO csbiInfo;

      hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
      if (hStdout == INVALID_HANDLE_VALUE)
        return -1;
      if (! GetConsoleScreenBufferInfo(hStdout, &csbiInfo))
        return -1;
      if (col) *col=(int)csbiInfo.dwCursorPosition.X;
      if (row) *row=(int)csbiInfo.dwCursorPosition.Y;
      return 0;
    }
    #endif
    return w16ConGetPos(col,row);
  }
  return -1;
}

/* ------------------------------------------------ */

int win32ConIsLiteUI(void) /* do we have a light GUI or a full GUI? */
{
  return 1;
}

/* ------------------------------------------------ */

struct __cbsendcmd
{
  UINT msg;
  WPARAM wParam;
  LPARAM lParam;
  int usepost;
  int foundcount;
  int ackcount;
  UINT dnetc_cmdmsg;
};

static BOOL CALLBACK __SendCmdEnumFunc(HWND hwnd,LPARAM lParam)
{
  char wintitle[128];
  if (GetWindowText( hwnd, wintitle, sizeof(wintitle) ))
  {
    int isours = 0, knowsdnet_cmdmsg = 0;
    if ( strcmp( wintitle, W32CLI_CONSOLE_NAME ) == 0 )
      isours = knowsdnet_cmdmsg = 1;
    else if ( strcmp( wintitle, W32CLI_OLD_CONSOLE_NAME ) == 0 )
      isours = 1;
    if (isours)
    {
      int iscui = 0;
      long ver = winGetVersion();
      struct __cbsendcmd *cbsc = (struct __cbsendcmd *)lParam;
      cbsc->foundcount++;

      if (ver >= 400 && GetClassName(hwnd,wintitle,sizeof(wintitle)))
        iscui = (!strcmp(wintitle,(ver>=2000)?("ConsoleWindowClass"):("tty")));

      if (!iscui)
      {
        int ishandled = 0;
        if (cbsc->msg == WM_COMMAND)
        {
          if (knowsdnet_cmdmsg && cbsc->dnetc_cmdmsg)
          {
            if (DNETC_WCMD_ACKMAGIC ==
              SendMessage(hwnd,cbsc->dnetc_cmdmsg,cbsc->wParam,cbsc->lParam))
            {
              cbsc->ackcount++;
              ishandled = 1;
            }
          }
          else if (cbsc->wParam == DNETC_WCMD_SHUTDOWN)
          {
            cbsc->usepost = 0;
            if (DNETC_WCMD_ACKMAGIC == SendMessage( hwnd, WM_CLOSE, 0, 0 ))
            {
              cbsc->ackcount++;
              ishandled = 1;
            }
          }
        }
        if (!ishandled)
        {
          if (cbsc->usepost)
            PostMessage( hwnd, cbsc->msg, cbsc->wParam, cbsc->lParam );
          else if (DNETC_WCMD_ACKMAGIC == SendMessage( hwnd, cbsc->msg,
                                    cbsc->wParam, cbsc->lParam ))
            cbsc->ackcount++;
        }
      }
      #if (CLIENT_OS == OS_WIN32)
      else if (cbsc->msg == WM_COMMAND &&
                (cbsc->wParam == DNETC_WCMD_RESTART ||
                cbsc->wParam == DNETC_WCMD_SHUTDOWN))
      {
        DWORD pid;
        if (GetWindowThreadProcessId(hwnd,&pid) != 0)
        {
          DWORD event = CTRL_BREAK_EVENT; /* restart */
          if (cbsc->msg == WM_CLOSE)
            event = CTRL_C_EVENT;
          if (GenerateConsoleCtrlEvent(event,pid))
            cbsc->ackcount++; /* assume it so */
        }
      }
      #endif
    }
  }
  return TRUE;
}

static int __findOtherClient(void) /* 0=none,0x1=bywindow,2=bymux,4=ntsvc*/
{
  int rc = 0;

  if (FindWindow( NULL, W32CLI_CONSOLE_NAME ))
    rc |= 0x01;
  else if (FindWindow( NULL, W32CLI_OLD_CONSOLE_NAME ))
    rc |= 0x01;

  #if (CLIENT_OS == OS_WIN32)
  {
    HANDLE hmutex;
    SECURITY_ATTRIBUTES sa;
    memset(&sa,0,sizeof(sa));
    sa.nLength = sizeof(sa);
    SetLastError(0);
    hmutex = CreateMutex(&sa, FALSE, W32CLI_MUTEX_NAME);
    if (hmutex)
    {
      if (GetLastError())  /* ie, it exists */
        rc |= 0x2;
      ReleaseMutex( hmutex );
      CloseHandle( hmutex );
    }
    /* this next part is a workaround: for some reason the mutex
    check above doesn't work to detect NT service when wanting
    to -shutdown etc, but does work for single-instance protection
    check when just starting normally.
    Detection is backwards compatible for NT service, but not for
    for w9x. However, win9x service will have been found by window
    name, (and the problem is NT only anyway) so its not an issue.
    */
    if (rc == 0)
    {
      extern int win32CliDetectRunningService(void);
      if (win32CliDetectRunningService() > 0) /* <0=err, 0=no, >0=yes */
        rc |= 0x04;
    }
  }
  #endif
  return rc;
}


int w32PostRemoteWCMD( int cmd ) /* returns <0 if not found, or */
{                                /* >0 = found+msgfailed, 0=found+msgok */
  int rc = -1;
  int foundflags = __findOtherClient();

  if (cmd == DNETC_WCMD_EXISTCHECK)
    return foundflags;

  if (foundflags) /* client is running */
  {
    rc = +1; /* assume msgfailed */

    #if (CLIENT_OS == OS_WIN32)
    /* take away focus from all windows - this is particularly critical
       for win9x console sessions since they hog cputime when active */
    //SetForegroundWindow(GetDesktopWindow());
    //SetActiveWindow(GetDesktopWindow());
    #endif

    #if (CLIENT_OS == OS_WIN32)
    if (winGetVersion()>=2000)  /* NT Only */
    {
      int svccmd = -1;
      if (cmd == DNETC_WCMD_SHUTDOWN)
        svccmd = SERVICE_CONTROL_STOP;
      else if (cmd == DNETC_WCMD_PAUSE)
        svccmd = SERVICE_CONTROL_PAUSE;
      else if (cmd == DNETC_WCMD_UNPAUSE)
        svccmd = SERVICE_CONTROL_CONTINUE;
      else if (cmd == DNETC_WCMD_RESTART)
        svccmd = CLIENT_SERVICE_CONTROL_RESTART; //128 //service control #
      if (svccmd != -1)
      {
        /* <0=err, 0=none running, >0=msg sent */
        if (win32CliSendServiceControlMessage(svccmd) > 0) /* msg sent */
          rc = 0; /* message went */
      }
    }
    #endif

    if ((foundflags & 0x01) != 0) /* do by window */
    {
      struct __cbsendcmd cbsc;
      cbsc.msg = WM_COMMAND;
      cbsc.wParam = (WPARAM)cmd;
      cbsc.lParam = 0;
      cbsc.usepost = 1;
      cbsc.foundcount = 0;
      cbsc.ackcount = 0;
      cbsc.dnetc_cmdmsg = RegisterWindowMessage(W32CLI_CONSOLE_NAME);

      cbsc.foundcount = 0;
      if ( EnumWindows( (WNDENUMPROC)__SendCmdEnumFunc, (LPARAM)&cbsc ) )
      {
        if ( cmd != DNETC_WCMD_SHUTDOWN || cbsc.foundcount == 0 ||
               cbsc.foundcount == cbsc.ackcount )
          rc = 0; /* assume success for all but DNETC_WCMD_SHUTDOWN */
        else
        {
          DWORD elapsedticks = 0, lasttick = 0;
          while (rc && elapsedticks < 5000)
          {
            DWORD nowticks = GetTickCount();
            if (nowticks == 0)
              nowticks++;
            if (lasttick == 0)
              ;
            else if (nowticks >= lasttick)
              elapsedticks += (nowticks - lasttick);
            else
              elapsedticks += (nowticks + (1+(0xfffffffful - lasttick)));
            lasttick = nowticks;

            if (__findOtherClient() == 0)
              rc = 0; /* success! */
            else
            {
              #if (CLIENT_OS == OS_WIN32)
              Sleep(500);
              #else
              Yield();
              #endif
            }
          }
        }
      }
    }
  }
  return rc;
}

