/* AN.c Japanese version

   This sample program illustrates the AnimatePenData, DrawPenData, and
   DrawPenDataEx APIs. It has not been written to be localizable: to do
   so, move strings to an.rc and use the Windows LoadString API.

   THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
   KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
   IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
   PURPOSE.

   Copyright (c) 1993-1995 Microsoft Corporation.  All Rights Reserved.
*/

/******************* Includes and Controlling Defines ***********************/
#include <windows.h>
#include <penwin.h>
#include <commdlg.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "anres.h"

/******************* Defines ************************************************/
#define cbSzTMax        256   // size of temp string buffers

#if (WINVER >= 0x0400)
#define cbSzFileMax        260   // max size of file names
#else
#define cbSzFileMax        128   // max size of file names
#endif

#define wExitError      1

#define CALLBACKNEVER   0
#define CALLBACKSHORT   1
#define CALLBACKLONG    2
#define CALLBACKSTROKE  3
#define SBMIN           0     // min scroll pos
#define SBMAX           10000 // ditto max
#define SBINC           10    // scroll increment
#define SBPGINC         100   // scroll page increment

#define cbPtBuf         32
#define cbBufMax        1024L // for file transfer

#define DPDEX           (IDM_DRAWEX - IDM_DRAWEX)
#define DPD             (IDM_DRAW - IDM_DRAWEX)
#define DPDPART         (IDM_DRAWPARTIAL - IDM_DRAWEX)

/******************* Typedefs ***********************************************/
// Note: be careful to use WORD size (16 bits) with IX_END:

typedef struct tagANDLG // dialog init [default values in brackets]
   {
   WORD uStrk0;         // first stroke to render [0]
   WORD uPt0;           // point offset in first stroke [0]
   WORD uStrk1;         // last stroke to render [IX_END]
   WORD uPt1;           // point offset in last stroke [IX_END]
   BOOL fSkipUp;        // [F] to animate upstrokes, T to skip them
   BOOL fAutoRepeat;    // [F] to end after one rendering, T to repeat to tap
   WORD uCBPeriodCode;  // callback Period code [CALLBACKNEVER]
   WORD uSpeedPct;      // speed of animation [100%]
   BOOL fTermTimeout;   // [T] to terminate input on timeout, F for rectbound
   BOOL fRenderScale;   // [T] to scale pendata to output window, F to clip
   }
   ANDLG, FAR *LPANDLG;

/******************* Macros *************************************************/
#define MenuEnable(hmenu, id, f) EnableMenuItem((HMENU)(hmenu), (id), \
   ((f)? MF_ENABLED: MF_DISABLED | MF_GRAYED))

#define EnableRedraw(hdlg) do {\
   if (IsWindow(hdlg))\
      EnableWindow(GetDlgItem(hdlg, IDD_PBREDRAW),\
         vhpndt != NULL && IsWindowVisible(vhwndOut));\
   } while (0)

#define ErrBox(sz, szTitle){\
   MessageBox(NULL, (LPSTR)(sz), (LPSTR)(szTitle),\
      MB_TASKMODAL|MB_ICONSTOP|MB_OK);\
   }

#define InfoBox(sz, szTitle)\
   MessageBox(NULL, (LPSTR)(sz), (LPSTR)(szTitle),\
      MB_TASKMODAL|MB_ICONEXCLAMATION|MB_OK)

// make callback code into a duration in ms:
#define MakeMs(u)\
   ((u)==CALLBACKSTROKE? AI_CBSTROKE:\
   (u)==CALLBACKLONG? 1000:\
   (u)==CALLBACKSHORT? 250: 0)

// draw only part of pendata:
#define DrawPenDataPartial(hdc, lprect, hpndt, s0, s1, p0, p1)\
   DrawPenDataEx(hdc, lprect, hpndt, s0, s1, p0, p1, NULL, NULL, 0)

#define Redraw()\
   PostMessage(vhwndOut, WM_USER, 0, 0)

/******************* Variables **********************************************/
HWND vhwndAN = NULL;             // Main wnd
HWND vhdlg = NULL;               // modeless dialog
HWND vhwndIn = NULL;             // input window
HWND vhwndOut = NULL;            // drawing window

HINSTANCE vhInstanceCur = NULL;
HINSTANCE vhPenWin = NULL;

ANIMATEPROC vlpfnAnimateProc = NULL;

HPENDATA vhpndt = NULL;             // input pendata
HPCM vhpcmInp = NULL;               // collection

PSTR vpszWndMain = "擖椡敞菽蕹";         // main window title
PSTR vpszWndIn = "_ANIn";           // input window title
PSTR vpszWndOut = "弌椡敞菽蕹";         // draw window title
PSTR vpszClassMain = "ANclass";     // class name
PSTR vpszClassIn = "ANInclass";     // class name of drawing window
PSTR vpszClassOut = "ANOutclass";   // class name of drawing window
PSTR vpszEmpty = "";                // empty string

DLGPROC vlpDlgProc = NULL;

char *vszIniFile = "animpd.ini";
ANDLG vandlg =
   {
   0,             // uStrk0         =from first stroke
   0,             // uPt0           =from first point
   IX_END,        // uStrk1         =to last stroke
   IX_END,        // uPt1           =to last point
   FALSE,         // fSkipUp        =wait during up periods too
   FALSE,         // fAutoRepeat    =cycle only once
   CALLBACKSHORT, // uCBPeriodCode  =callback every 250 ms
   100,           // uSpeedPct      =original speed
   TRUE,          // fTermTimeout   =terminate ink input on timeout
   FALSE          // fRenderScale   =render as drawn, not scaled to window
   };

short nDrawProc = DPDEX;
UINT vcCB = 0;                // for callback display count
BOOL vfCB = FALSE;            // enable callback display, initially off
BOOL vfDrawing = FALSE;       // TRUE when drawing

// user requests during callback:
BOOL vfReqCxl = FALSE;        // dialog redraw cancel
BOOL vfReqClear = FALSE;      // set to TRUE on request to clear pendata
BOOL vfReqExit = FALSE;       // set to TRUE on request to exit app
BOOL vfReqCloseOut = FALSE;   // set to TRUE on request to close output
short vnReqLoadSave = -1;     // set to 0 or 1 on request to do file i/o

RECT vrectOut;                // location of output window

char vszFile[cbSzFileMax];
char *vszSaveFileDef = "an.pdt";

/******************* Export Prototypes **************************************/
BOOL CALLBACK ANDlgProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK AnimateProc(HPENDATA, UINT, UINT, UINT FAR*, LPARAM);
LRESULT CALLBACK ANInWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ANOutWndProc(HWND, UINT, WPARAM, LPARAM);
LRESULT CALLBACK ANWndProc(HWND, UINT, WPARAM, LPARAM);

/******************* Local prototypes ***************************************/
VOID NEAR PASCAL CancelDrawing(VOID);
VOID NEAR PASCAL ClearAppQueue(VOID);
VOID NEAR PASCAL ClearOutputWindow(VOID);
BOOL NEAR PASCAL FBeginPenInput(HWND, DWORD);
BOOL NEAR PASCAL FGetFileName(HWND, BOOL, LPSTR);
BOOL NEAR PASCAL FInitAN(HINSTANCE, HINSTANCE, LPSTR);
BOOL NEAR PASCAL FInitInstance(HINSTANCE, HINSTANCE, int);
WORD NEAR PASCAL GetEditVal(HWND, int, WORD);
VOID NEAR PASCAL InitDlgItemInt(HWND, int, UINT);
VOID NEAR PASCAL LoadSave(BOOL);
BOOL NEAR PASCAL MakeOutputWindow(HWND, int, int, int, int, int);
HPENDATA NEAR PASCAL ReadPenData(HFILE hfile);
VOID NEAR PASCAL ShowCancel(BOOL);
VOID NEAR PASCAL TermAN(VOID);
VOID NEAR PASCAL TPtoCP(HWND, LPPOINT, int, LPRECT);
BOOL NEAR PASCAL WritePenData(HFILE hfile, HPENDATA hpndt);

/******************* EXPORT FUNCTIONS ***************************************/

/*+-------------------------------------------------------------------------*/
BOOL CALLBACK           // ret T if handled
ANDlgProc(              // main modal dialog
   HWND hdlg,           // will become vhdlg
   UINT message,
   WPARAM wParam,
   LPARAM lParam)
   {
   WORD wNotifyCode;
   WORD u;
   HWND hSBSpeed = GetDlgItem(hdlg, IDD_SBSPEED);
   static RECT rectDlg;

   switch (message)
      {
   case WM_INITDIALOG:  // in lieu of WM_CREATE
      InitDlgItemInt(hdlg, IDD_ETSTRK0, (UINT)vandlg.uStrk0);
      InitDlgItemInt(hdlg, IDD_ETPT0, (UINT)vandlg.uPt0);
      InitDlgItemInt(hdlg, IDD_ETSTRK1, (UINT)vandlg.uStrk1);
      InitDlgItemInt(hdlg, IDD_ETPT1, (UINT)vandlg.uPt1);

      CheckDlgButton(hdlg, IDD_CHSKIPUP, vandlg.fSkipUp);
      CheckDlgButton(hdlg, IDD_CHAUTOREPEAT, vandlg.fAutoRepeat);

      CheckRadioButton(hdlg, IDD_RBCBNEVER, IDD_RBCBSTROKE,
         IDD_RBCBNEVER + vandlg.uCBPeriodCode);

      InitDlgItemInt(hdlg, IDD_ETSPEED, (UINT)vandlg.uSpeedPct);

      SetScrollRange(hSBSpeed, SB_CTL, SBMIN, SBMAX, FALSE);
      SetScrollPos(hSBSpeed, SB_CTL, vandlg.uSpeedPct, TRUE);

      CheckRadioButton(hdlg, IDD_RBTERMTIMEOUT, IDD_RBTERMRECT,
         IDD_RBTERMTIMEOUT + !vandlg.fTermTimeout);
      CheckRadioButton(hdlg, IDD_RBRENDERSCALE, IDD_RBRENDERCLIP,
         IDD_RBRENDERSCALE + !vandlg.fRenderScale);
      if (!IsRectEmpty(&rectDlg))
         SetWindowPos(hdlg, NULL, rectDlg.left, rectDlg.top, 0, 0,
            SWP_NOSIZE | SWP_NOZORDER);

      EnableRedraw(hdlg);
      EnableWindow(GetDlgItem(hdlg, IDD_PBCLEAR), vhpndt != NULL);
      return FALSE;     // let system set focus

   case WM_COMMAND:     // user actions
#ifdef WIN32
      wNotifyCode = HIWORD(wParam);
#else
      wNotifyCode = HIWORD(lParam);
#endif

      switch (LOWORD(wParam))
         {
      case IDD_ETSTRK0:
         if (wNotifyCode == EN_KILLFOCUS)
            vandlg.uStrk0 = GetEditVal(hdlg, LOWORD(wParam), vandlg.uStrk0);
         break;

      case IDD_ETPT0:
         if (wNotifyCode == EN_KILLFOCUS)
            vandlg.uPt0 = GetEditVal(hdlg, LOWORD(wParam), vandlg.uPt0);
         break;

      case IDD_ETSTRK1:
         if (wNotifyCode == EN_KILLFOCUS)
            vandlg.uStrk1 = GetEditVal(hdlg, LOWORD(wParam), vandlg.uStrk1);
         break;

      case IDD_ETPT1:
         if (wNotifyCode == EN_KILLFOCUS)
            vandlg.uPt1 = GetEditVal(hdlg, LOWORD(wParam), vandlg.uPt1);
         break;

      case IDD_CHSKIPUP:
         vandlg.fSkipUp = IsDlgButtonChecked(hdlg, IDD_CHSKIPUP)==1;
         break;

      case IDD_CHAUTOREPEAT:
         vandlg.fAutoRepeat = IsDlgButtonChecked(hdlg, IDD_CHAUTOREPEAT)==1;
         break;

      case IDD_RBCBNEVER:
      case IDD_RBCB250MS:
      case IDD_RBCB1SEC:
      case IDD_RBCBSTROKE:
         for (u = IDD_RBCBSTROKE - IDD_RBCBNEVER; u > 0; u--)
            if (IsDlgButtonChecked(hdlg, IDD_RBCBNEVER+u))
               break;
         vandlg.uCBPeriodCode = u;
         break;

      case IDD_ETSPEED:
         if (wNotifyCode == EN_KILLFOCUS)
            vandlg.uSpeedPct = GetEditVal(hdlg, LOWORD(wParam), vandlg.uSpeedPct);            
         if (vandlg.uSpeedPct > SBMAX){
            vandlg.uSpeedPct = SBMAX;
            SetDlgItemInt(hdlg, IDD_ETSPEED, vandlg.uSpeedPct, FALSE);
         } else if (vandlg.uSpeedPct < SBMIN){
            vandlg.uSpeedPct = SBMIN;
            SetDlgItemInt(hdlg, IDD_ETSPEED, vandlg.uSpeedPct, FALSE);
         }

         if ((WORD)GetScrollPos(hSBSpeed, SB_CTL) != vandlg.uSpeedPct)
#ifdef WIN32
            PostMessage(hdlg, WM_HSCROLL,
               MAKEWPARAM(SB_THUMBPOSITION, vandlg.uSpeedPct),
               (LPARAM)hSBSpeed);
#else
            PostMessage(hdlg, WM_HSCROLL, SB_THUMBPOSITION,
               MAKELPARAM(vandlg.uSpeedPct, hSBSpeed));
#endif // WIN32
         break;

      case IDD_SBSPEED:
         // see WM_HSCROLL
         break;

      case IDD_RBTERMTIMEOUT:
      case IDD_RBTERMRECT:
         vandlg.fTermTimeout = IsDlgButtonChecked(hdlg, IDD_RBTERMTIMEOUT)==1;
         break;

      case IDD_RBRENDERSCALE:
      case IDD_RBRENDERCLIP:
         vandlg.fRenderScale = IsDlgButtonChecked(hdlg, IDD_RBRENDERSCALE)==1;
         break;

      case IDD_PBCLEAR:
         if (vfDrawing)
            {
            CancelDrawing();
            vfReqClear = TRUE;   // after DrawPenDataEx ret
            }
         else
            ClearOutputWindow();
         break;

      case IDD_PBREDRAW:
         /*
            The Redraw() macro really invalidates, so pressing the button
            multiple times only invalidates once. It doesn't redraw
            the same number of times the button is pushed.
         */
         Redraw();
         break;

      case IDD_PBEXIT:
         PostMessage(hdlg, WM_CLOSE, 0, 0L);
         break;

      case IDCANCEL:
         CancelDrawing();
         break;

      default:
         return TRUE;
         }
      return FALSE;  // WM_COMMAND processed

   case WM_HSCROLL:
#ifdef WIN32
      if ((HWND)lParam == hSBSpeed)
#else
      if ((HWND)HIWORD(lParam) == hSBSpeed)
#endif // WIN32
         {
         short jT, j = (short)vandlg.uSpeedPct;

         switch (LOWORD(wParam))
            {
         case SB_LEFT:
            j = SBMIN;
            break;

         case SB_RIGHT:
            j = SBMAX;
            break;

         case SB_LINELEFT:
            j = j-SBINC > SBMIN? j-SBINC: SBMIN;
            break;

         case SB_LINERIGHT:
            j = j+SBINC < SBMAX? j+SBINC: SBMAX;
            break;

         case SB_PAGELEFT:
            j = j-SBPGINC > SBMIN? j-SBPGINC: SBMIN;
            break;

         case SB_PAGERIGHT:
            j = j+SBPGINC < SBMAX? j+SBPGINC: SBMAX;
            break;

         case SB_THUMBTRACK:
         case SB_THUMBPOSITION:
#ifdef WIN32
            j = (short)HIWORD(wParam);
#else
            j = (short)LOWORD(lParam);
#endif // WIN32

            // adjust to nearest:
            if (jT = (j-SBMIN) % SBINC)
               j += SBINC - jT < jT? SBINC - jT: -jT;
            break;

            }
         SetScrollPos(hSBSpeed, SB_CTL, vandlg.uSpeedPct = (WORD)j, TRUE);

         if ((WORD)j != GetEditVal(hdlg, IDD_ETSPEED, j))
            SetDlgItemInt(hdlg, IDD_ETSPEED, vandlg.uSpeedPct, FALSE);
         }
      break;   // WM_HSCROLL

   case WM_CLOSE:
      PostMessage(vhwndAN, WM_COMMAND, IDM_DLG, 0L);  // toggle dlg off
      break;

   case WM_DESTROY:
      GetWindowRect(hdlg, &rectDlg);
      vhdlg = NULL;
      break;

   default:
      break;
      }

   return FALSE;
   }

/*+-------------------------------------------------------------------------*/
BOOL CALLBACK        // ret LRESULT; NB _export to ensure correct ds
AnimateProc(                  // animation callback proc
   HPENDATA hpndt,            // pendata
   UINT wStroke,              // current stroke
   UINT cPnts,                // number of points yet to draw
   UINT FAR *lpuSpeedPct,     // addr of speed pct
   LPARAM lParam)             // app value
   {
   BOOL fRet = !vfReqCxl;     // set in dialog and File menu
   hpndt, wStroke, cPnts, lParam;   // unused

   if (fRet)
      {
      char sz[cbSzTMax];

      if (!vcCB)
         ShowCancel(TRUE);

      ClearAppQueue();     // handle message backlog in app queue

      *lpuSpeedPct = vandlg.uSpeedPct;    // get latest speed setting

      wsprintf((LPSTR)sz, (LPSTR)"CB=%u", ++vcCB);
      if (vfCB && vhwndOut)
         SetWindowText(vhwndOut, (LPSTR)sz);

      // vfReqCxl may have gotten set in ANOutWndProc's WM_PAINT, if
      // the user changed the window size during a callback for example:
      fRet = !vfReqCxl;
      }

   return fRet;
   }

/*+-------------------------------------------------------------------------*/
LRESULT CALLBACK        // ret LRESULT
ANInWndProc(            // input window proc
   HWND hwnd,           // == vhwndIn
   UINT message,
   WPARAM wParam,
   LPARAM lParam)
   {
   DWORD dwExtraInfo;
   int iRet = 0;
   STROKEINFO si;
   RECT r;
   POINT rgPnt[cbPtBuf];

   switch (message)
      {
   case WM_CHAR:
      if (wParam == VK_ESCAPE)
         vfReqCxl = TRUE;
      break;

   case WM_LBUTTONDOWN:
      if (vfDrawing)
         {
         MessageBeep(0);
         return 0L;
         }

      dwExtraInfo = GetMessageExtraInfo();
      if (IsPenEvent(message, dwExtraInfo))
         {
         if (FBeginPenInput(hwnd, dwExtraInfo))
            {
            if (IsWindow(vhdlg))
               {
               EnableRedraw(vhdlg);
               EnableWindow(GetDlgItem(vhdlg, IDD_PBCLEAR), vhpndt != NULL);
               CancelDrawing();
               }
            return 1L;
            }
         return 0L;
         }
      break;

   case WM_PENEVENT:
      switch (SubPenMsgFromWpLp(wParam, lParam))
         {
      case PE_PENDOWN:
      case PE_PENUP:
      case PE_PENMOVE:
      case PE_TERMINATING:
         while ((iRet = GetPenInput(vhpcmInp, rgPnt, NULL, 0,
            cbPtBuf, &si)) > 0)
            {
            TPtoCP(hwnd, rgPnt, iRet, &r);

            /* The user may want to terminate without using timeout.
               In this case, the pen continues to generate up points
               and we ignore them if the "Skip upstrokes" checkbox is
               set (during *input*):
            */
            if ((si.wPdk & PDK_DOWN)   // always add all down points
               || !vandlg.fSkipUp)     // don't add upstrokes if flag set
                  AddPointsPenData(vhpndt, rgPnt, NULL, &si);
            }
         return 1L;  // we handled it, do not allow into DefWindowProc

      case PE_TERMINATED:
         InvalidateRect(hwnd, NULL, TRUE);
         UpdateWindow(hwnd);
         Redraw();
         return 1L;  // we handled it

      default:
         return 0L;
         }
      break;

   default:
      break;
      }

   return DefWindowProc(hwnd, message, wParam, lParam);
   }

/*+-------------------------------------------------------------------------*/
LRESULT CALLBACK        // ret LRESULT
ANOutWndProc(           // drawing window proc
   HWND hwnd,           // == vhwndOut
   UINT message,
   WPARAM wParam,
   LPARAM lParam)
   {
   LRESULT lRet = 1L;
   static char const szBoxTitle[] = "奼挘惷巭夋張棟";

   switch (message)
      {
   case WM_USER:  // for Redraw() macro
      InvalidateRect(hwnd, NULL, TRUE);
      if (IsWindow(vhdlg))
         EnableWindow(GetDlgItem(vhdlg, IDD_PBCLEAR), vhpndt != NULL);
      SetFocus(vhwndOut);     // catch Esc
      break;

   case WM_CHAR:
      if (wParam == VK_ESCAPE)
         vfReqCxl = TRUE;
      break;

   case WM_PAINT:
      if (vfDrawing)
         {
         // since vfDrawing is TRUE, we are repainting during a
         // callback, so we have to cancel the current animation first:
         vfReqCxl = TRUE;  // this is picked up in AnimateProc()
         break;
         }

      EnableRedraw(vhdlg);

      if (vhpndt)
         {
         PAINTSTRUCT ps;
         HDC hdc = BeginPaint(hwnd, &ps);

         vfDrawing = TRUE;       // set semaphore

         if (hdc)
            {
            RECT r;
            int iRet;
            COLORREF crDPD = RGB(0, 255, 255);  // cyan default
            ANIMATEINFO ai =
               {
               sizeof(ANIMATEINFO),                   // struct size
               vandlg.uSpeedPct,                      // speed percent
               MakeMs(vandlg.uCBPeriodCode),          // callback Period in ms
               vandlg.fSkipUp? AI_SKIPUPSTROKES: 0,   // options
               0L,                                    // lParam
               0L                                     // reserved
               };

            if (vandlg.fRenderScale)            // scale to window
               GetClientRect(hwnd, &r);
            else // clip
               {
               GetPenDataAttributes(vhpndt, &r, GPA_RECTBOUND);
               TPtoDP((LPPOINT)&r, 2);          // pendata is in tablet coords
               }

            switch (nDrawProc)
               {
            case DPDEX:    // animation
               vcCB = 0;                        // animate callback counter
               vfReqCxl = FALSE;             // reset
               ShowCancel(vandlg.uCBPeriodCode != CALLBACKNEVER);

               iRet = DrawPenDataEx(
                  hdc,                          // handle to DC
                  &r,                           // rect for scaling/clipping
                  vhpndt,                       // the pendata
                  vandlg.uStrk0,                // first stroke
                  vandlg.uStrk1,                // last stroke
                  vandlg.uPt0,                  // first point in first stroke
                  vandlg.uPt1,                  // last point in last stroke
                  vlpfnAnimateProc,             // Animate Callback function
                  &ai,
                  0);

               if (iRet < 0 && iRet >= -10)
                  {
                  static char *szErr[] =
                     {
                     "PDR_ERROR",         // -1  PDR_ERROR
                     "PDR_PNDTERR",       // -2  PDR_PNDTERR
                     "PDR_VERSIONERR",    // -3  PDR_VERSIONERR
                     "PDR_COMPRESSED",    // -4  PDR_COMPRESSED
                     "PDR_STRKINDEXERR",  // -5  PDR_STRKINDEXERR
                     "PDR_PNTINDEXERR",   // -6  PDR_PNTINDEXERR
                     "PDR_MEMERR",        // -7  PDR_MEMERR
                     "PDR_INKSETERR",     // -8  PDR_INKSETERR
                     "PDR_ABORT",         // -9  PDR_ABORT
                     "PDR_NA",            // -10 PDR_NA
                     };

                  ErrBox(szErr[-iRet-1], szBoxTitle);
                  CancelDrawing();
                  }

               if (!iRet && !vfReqCxl)
                  {
                  InfoBox("PDR_CANCEL: 喊偈薤笍垪潅獚s偒偢傑傝傑偟偨丅", szBoxTitle);
                  CancelDrawing();
                  }

               if (!vandlg.fAutoRepeat)
                  ShowCancel(FALSE);            // hide cancel button again

               if (IsWindow(vhdlg)
                  && (iRet == PDR_STRKINDEXERR || iRet == PDR_PNTINDEXERR))
                  {
                  SetFocus(vhdlg);  // required
                  SetFocus(GetDlgItem(vhdlg,
                     iRet == PDR_STRKINDEXERR?
                        IDD_ETSTRK0:
                        IDD_ETPT0));
                  }

               vfReqCxl = FALSE;
               break;

#ifndef WIN32
            case DPD:      // 1.0 style drawing
               {
               HPEN hpen, hpenOld;

               hpen = CreatePen(PS_SOLID, 1, crDPD);
               hpenOld = SelectObject(hdc, hpen);
               DrawPenData(hdc,     // DC
                  &r,               // rect for scaling/clipping
                  vhpndt);          // the pendata
               SelectObject(hdc, hpenOld);
               DeleteObject(hpen);
               break;
               }
#endif // WIN32

            case DPDPART:  // partial drawing
            default:
               DrawPenDataPartial(hdc, // DC
                  &r,               // rect for scaling/clipping
                  vhpndt,           // the pendata
                  vandlg.uStrk0,    // first stroke to draw
                  vandlg.uStrk1,    // last stroke to draw
                  vandlg.uPt0,      // first point in first stroke to draw
                  vandlg.uPt1);     // last point in last stroke to draw
               break;
               }

            ClearAppQueue();  // handle message backlog if any

            if (vandlg.fAutoRepeat)
               Redraw();
            }

         EndPaint(hwnd, &ps);
         vfDrawing = FALSE;
         }

      // user requested exit or file i/o during callback; now we can do it:
      if (vfReqExit)       // check this one first
         {
         PostMessage(vhwndAN, WM_CLOSE, 0, 0L);
         break;
         }

      if (vfReqCloseOut)
         {
         vfReqCloseOut = FALSE;
         PostMessage(vhwndOut, WM_CLOSE, 0, 0L);
         break;
         }

      if (vnReqLoadSave != -1)
         {
         BOOL fLoad = vnReqLoadSave > 0;

         vnReqLoadSave = -1;     // reset global first
         LoadSave(fLoad);
         }

      if (vfReqClear)
         {
         vfReqClear = FALSE;
         ClearOutputWindow();
         }

      break;

   case WM_CLOSE:
      if (vfDrawing)
         {
         vfReqCloseOut = TRUE;   // can't close it just yet
         return 0;
         }
      break;

   case WM_DESTROY:
      GetWindowRect(hwnd, &vrectOut);  // save position
      vhwndOut = NULL;
      EnableRedraw(vhdlg);
      break;

   default:
      break;
      }

   return DefWindowProc(hwnd, message, wParam, lParam);
   }

/*+-------------------------------------------------------------------------*/
LRESULT CALLBACK        // ret LRESULT
ANWndProc(              // main window proc
   HWND hwnd,           // == vhwndAN
   UINT message,
   WPARAM wParam,
   LPARAM lParam)
   {
   LRESULT lRet = 1L;

   switch (message)
      {
   case WM_SETFOCUS:
      if (IsWindow(vhwndIn))
         SetFocus(vhwndIn);
      return lRet;

   case WM_SIZE:
      if (IsWindow(vhwndIn))
			{
         MoveWindow(vhwndIn, 0, 0, LOWORD(lParam), HIWORD(lParam), FALSE);
			InvalidateRect(vhwndIn, NULL, TRUE);
			UpdateWindow(vhwndIn);
			}
      break;

   case WM_INITMENU:
      MenuEnable((HMENU)wParam, IDM_OUTPUT, vhwndOut == NULL);
      CheckMenuItem((HMENU)wParam, IDM_CBDISP, vfCB? MF_CHECKED: MF_UNCHECKED);
      break;

   case WM_COMMAND:
      {
      HMENU hmenu = GetMenu(vhwndAN);

      switch (LOWORD(wParam))
         {
      case IDM_DRAWEX:        // animate with DrawPenDataEx
      case IDM_DRAW:          // DrawPenData
      case IDM_DRAWPARTIAL:   // DrawPenDataEx
         CheckMenuItem(hmenu, IDM_DRAWEX+nDrawProc, MF_UNCHECKED);
         nDrawProc = LOWORD(wParam) - IDM_DRAWEX;
         CheckMenuItem(hmenu, LOWORD(wParam), MF_CHECKED);
         Redraw();
         return 0;

      case IDM_DLG:
         {
         BOOL fCheck = GetMenuState(hmenu, LOWORD(wParam), MF_BYCOMMAND) == MF_CHECKED;

         if (fCheck)
            DestroyWindow(vhdlg);   // sets vhdlg to NULL
         else
            {
            vhdlg = CreateDialog(vhInstanceCur,
               MAKEINTRESOURCE(IDD_ANIMATE), hwnd, vlpDlgProc);
            }

         CheckMenuItem(hmenu, LOWORD(wParam), fCheck? MF_UNCHECKED: MF_CHECKED);
         }
         return 0;

      case IDM_OUTPUT:
         MakeOutputWindow(vhwndAN,
            vrectOut.left,
            vrectOut.top,
            vrectOut.right - vrectOut.left,
            vrectOut.bottom - vrectOut.top,
            SW_SHOW);
         return 0;

      case IDM_CBDISP:        // set callback display
         vfCB = !vfCB;
         if (!vfCB)           // turn off: restore title
            SetWindowText(vhwndOut, (LPSTR)vpszWndOut);
         return 0;

      case IDM_OPEN:
      case IDM_SAVE:
         if (vfDrawing)       // defer file i/o until ret from drawing
            {
            CancelDrawing();
            vnReqLoadSave = (LOWORD(wParam) == IDM_OPEN);   // 1 to load, 0 to save
            }
         else
            LoadSave(LOWORD(wParam) == IDM_OPEN);
         return 0;

      case IDM_EXIT:
         PostMessage(hwnd, WM_CLOSE, 0, 0L);
         return 0;

      default:
         break;
         }
      }
      break;

   case WM_ERASEBKGND:
      return 1L;  // skip irritating flash

   case WM_PENMISC:
      if (wParam == PMSC_LOADPW && lParam == PMSCL_UNLOADING)
         PostQuitMessage(0);
      break;

   case WM_CLOSE:
      if (vfDrawing)
         {
         CancelDrawing();
         vfReqExit = TRUE; // defer exit until ret from drawing
         return 0;         // abort close
         }
      break;               // go to termination

   case WM_DESTROY:
      TermAN();
      vhwndAN = NULL;
      PostQuitMessage(0);
      break;

   default:
      break;
      }

   return DefWindowProc(hwnd, message, wParam, lParam);
   }

/******************* LOCAL FUNCTIONS ****************************************/

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL  // no ret
CancelDrawing(    // sets cancel-drawing flag and updates dialog
   VOID)          // no params
   {
   vfReqCxl = TRUE;
   if (vandlg.fAutoRepeat)
      {
      vandlg.fAutoRepeat = FALSE;
      if (IsWindowVisible(vhdlg))
         CheckDlgButton(vhdlg, IDD_CHAUTOREPEAT, vandlg.fAutoRepeat);
      }
   ShowCancel(FALSE);   // update button face
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL        // no ret
ClearAppQueue(          // cycles through pending messages to yield.
   VOID)                // no params
   {
   MSG msg;

   while (PeekMessage(&msg, (HWND)NULL, 0, 0, PM_REMOVE))
      {
      if (!vhdlg || !IsDialogMessage(vhdlg, &msg))
         {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
         }
      }
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL        // no ret
ClearOutputWindow(      // clears vhwndOut
   VOID)                // no params
   {
   if (vhpndt)
      {
      DestroyPenData(vhpndt);
      vhpndt = NULL;
      Redraw();
      }
   }

/*+-------------------------------------------------------------------------*/
BOOL NEAR PASCAL        // ret T if collection & pendata was started ok
FBeginPenInput(         // establishes the collection session, creates pendata
   HWND hwnd,           // Handle to a window
   DWORD dwExtraInfo)   // Extra info associated with input device message
/*------------------------
:  This function demonstrates usage of INKPUT calls to create and
:  fill a pen data.
------------------------*/
   {
   PCMINFO pcminfo;
   INKINGINFO inkinginfo;
   int errCode;
   UINT wEventRef = LOWORD(dwExtraInfo);
   RECT r;
   HRGN hrgnT;

#ifdef WIN32
   memset((LPVOID)&pcminfo, sizeof(pcminfo), 0);
   memset((LPVOID)&inkinginfo, sizeof(inkinginfo), 0);
#else
   _fmemset((LPVOID)&pcminfo, sizeof(pcminfo), 0);
   _fmemset((LPVOID)&inkinginfo, sizeof(inkinginfo), 0);
#endif // WIN32
   pcminfo.cbSize = sizeof(PCMINFO);
   pcminfo.dwPcm = 0;   // messages by default
   if (vandlg.fTermTimeout)
      {
      UINT uTimeout;

      pcminfo.dwPcm |= PCM_TIMEOUT;
      GetPenMiscInfo(PMI_TIMEOUT, (LPARAM)(UINT FAR*)&uTimeout);
      pcminfo.dwTimeout = uTimeout;
      }
   else
      {
      pcminfo.dwPcm |= PCM_RECTBOUND;
      GetClientRect(hwnd, &pcminfo.rectBound);
      ClientToScreen(hwnd, (LPPOINT)&pcminfo.rectBound.left);
      ClientToScreen(hwnd, (LPPOINT)&pcminfo.rectBound.right);
      }

   // start input:
   if (!(vhpcmInp = StartPenInput(hwnd, wEventRef, &pcminfo, &errCode)))
      return FALSE;

   if (vhpndt)
      {
      DestroyPenData(vhpndt);
      vhpndt = NULL;
      }

   if (!(vhpndt = CreatePenDataEx((LPPENINFO)NULL, PDTS_STANDARDSCALE, 0, 0)))
      return FALSE;

   // set up inking not to overwrite sibling & children windows (scrn coords):
   GetClientRect(vhwndIn, &r);
   ClientToScreen(vhwndIn, (LPPOINT)&r.left);
   ClientToScreen(vhwndIn, (LPPOINT)&r.right);
   inkinginfo.hrgnClip = CreateRectRgnIndirect(&r);   // main inking region

   if (IsWindow(vhwndOut))
      {
      GetWindowRect(vhwndOut, &r);
      hrgnT = CreateRectRgnIndirect(&r);  // output window
      CombineRgn(inkinginfo.hrgnClip, inkinginfo.hrgnClip, hrgnT, RGN_DIFF);
      }

   if (IsWindow(vhdlg))
      {
      GetWindowRect(vhdlg, &r);
      hrgnT = CreateRectRgnIndirect(&r);  // dialog window
      CombineRgn(inkinginfo.hrgnClip, inkinginfo.hrgnClip, hrgnT, RGN_DIFF);
      }

   inkinginfo.cbSize = sizeof(INKINGINFO);
   inkinginfo.tip.cbSize = sizeof(PENTIP);
   inkinginfo.wFlags = PII_INKCLIPRGN;

   StartInking(vhpcmInp, wEventRef, &inkinginfo);

   DeleteObject(inkinginfo.hrgnClip);  // a copy was made so kill this one
   DeleteObject(hrgnT);

   return TRUE;
   }

/*+-------------------------------------------------------------------------*/
BOOL NEAR PASCAL        // ret T if user selected a file
FGetFileName(           // get save/load filename
   HWND hwnd,           // owner
   BOOL fRead,          // T read F write
   LPSTR lpszFile)      // addr of filename
/*------------------------
:  see <commdlg.h> and MS C7 Prog Ref/2 p.414
------------------------*/
   {
   static OPENFILENAME ofn;
   char szPath[cbSzTMax];
   char szFile[cbSzTMax];
   char szDlgTitle[cbSzTMax];
   char szFilter[cbSzTMax];
   char chReplace;
   UINT i, cb;
   BOOL fRet = FALSE;

   if (!LoadString(vhInstanceCur, fRead? RS_OFNREADTITLE: RS_OFNWRITETITLE,
      (LPSTR)szDlgTitle, cbSzTMax))
         return FALSE;
   if (!(cb = LoadString(vhInstanceCur, RS_FILTER, (LPSTR)szFilter, cbSzTMax)))
      return FALSE;
   chReplace = szFilter[cb-1];   // retrieve wild character
   for (i = 0; szFilter[i]; i++)
      if (szFilter[i] == chReplace)
         szFilter[i] = '\0';

   if (!ofn.lpstrInitialDir)
      GetSystemDirectory(szPath, cbSzTMax);

   GetFileTitle(lpszFile, (LPSTR)szFile, cbSzTMax);   // strip path

   // setup info for common dialog:
   ofn.lStructSize = sizeof(ofn);
   ofn.hwndOwner = hwnd;
   ofn.hInstance = vhInstanceCur;
   ofn.lpstrFilter = (LPSTR)szFilter;     // file types
   ofn.lpstrCustomFilter = NULL;
   ofn.nFilterIndex = 0;                  // index of first one
   ofn.nMaxCustFilter = 0;
   ofn.lpstrFile = (LPSTR)szFile;         // initial file suggested
   ofn.nMaxFile = cbSzTMax;
   ofn.lpstrTitle = (LPSTR)szDlgTitle;    // dlg title
   ofn.lpstrInitialDir = (LPSTR)szPath;   // initial path
   ofn.Flags = OFN_SHOWHELP | OFN_HIDEREADONLY
      | OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_SHAREAWARE;
   ofn.lpstrDefExt = "*";                 // default extension
   ofn.lpfnHook = NULL;

   fRet = fRead? GetOpenFileName((LPOPENFILENAME)&ofn):
      GetSaveFileName((LPOPENFILENAME)&ofn);

   if (fRet)
      lstrcpy(lpszFile, (LPSTR)ofn.lpstrFile);

   return fRet;
   }

/*+-------------------------------------------------------------------------*/
BOOL NEAR PASCAL           // ret T if successful init
FInitAN(                   // init class[es], icon
   HINSTANCE hInstance,    // this instance
   HINSTANCE hPrevInstance,// prev instance if any
   LPSTR lpszCommandLine)  // ptr to command line
   {
   WNDCLASS wndClass;
   char szIcon[] = "iconAN";
   WORD wSysFlags;

   if (!hPrevInstance)
      {
      vhInstanceCur = hInstance;

      // Main Window
      wndClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
      wndClass.hIcon          = LoadIcon(hInstance, szIcon);
      wndClass.lpszMenuName   = NULL;
      wndClass.lpszClassName  = vpszClassMain;
      wndClass.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
      wndClass.hInstance      = hInstance;
      wndClass.style          = CS_VREDRAW | CS_HREDRAW;
      wndClass.lpfnWndProc    = ANWndProc;
      wndClass.cbClsExtra     = 0;
      wndClass.cbWndExtra     = 0;

      if (!RegisterClass(&wndClass))
         return FALSE;

      // Input Window: use pen cursor if actual pen is present:
      GetPenMiscInfo(PMI_SYSFLAGS, (LPARAM)(LPWORD)&wSysFlags);
      wndClass.hCursor        = wSysFlags & PWF_PEN?
         LoadCursor(vhPenWin, IDC_PEN): LoadCursor(NULL, IDC_ARROW);
      wndClass.hIcon          = NULL;
      wndClass.lpszMenuName   = NULL;
      wndClass.lpszClassName  = vpszClassIn;
      wndClass.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
      wndClass.hInstance      = hInstance;
      wndClass.style          = CS_VREDRAW | CS_HREDRAW;
      wndClass.lpfnWndProc    = ANInWndProc;
      wndClass.cbClsExtra     = 0;
      wndClass.cbWndExtra     = 0;

      if (!RegisterClass(&wndClass))
         return FALSE;

      // Output Window
      wndClass.hCursor        = LoadCursor(NULL, IDC_ARROW);
      wndClass.hIcon          = NULL;
      wndClass.lpszMenuName   = NULL;
      wndClass.lpszClassName  = vpszClassOut;
      wndClass.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
      wndClass.hInstance      = hInstance;
      wndClass.style          = CS_VREDRAW | CS_HREDRAW;
      wndClass.lpfnWndProc    = ANOutWndProc;
      wndClass.cbClsExtra     = 0;
      wndClass.cbWndExtra     = 0;

      if (!RegisterClass(&wndClass))
         return FALSE;
      }

   return TRUE;
   }

/*+-------------------------------------------------------------------------*/
BOOL NEAR PASCAL              // ret T if init successful
FInitInstance(                // create windows and init
   HINSTANCE hInstance,       // this instance
   HINSTANCE hPrevInstance,   // prev instance
   int cmdShow)               // show command
   {
   DWORD dwStyle = WS_OVERLAPPEDWINDOW;
   DWORD dwExStyle = 0; // could be topmost
   HMENU hMenu = NULL;
   int xWnd = 0;
   int yWnd = 224;
   int cxWnd = GetSystemMetrics(SM_CXSCREEN) / 3;
   int cyWnd = GetSystemMetrics(SM_CYSCREEN) / 4;

   hPrevInstance; // noref

   hMenu = LoadMenu(hInstance, "ANMenu");
   vlpDlgProc = (DLGPROC)MakeProcInstance((FARPROC)ANDlgProc, vhInstanceCur);

   vlpfnAnimateProc = (ANIMATEPROC)MakeProcInstance(
      (FARPROC)AnimateProc, vhInstanceCur);

   // Create main window:
   if (!(vhwndAN = CreateWindowEx(
      dwExStyle,        // extended style
      vpszClassMain,    // class name
      vpszWndMain,      // window title
      dwStyle,          // main style
      xWnd, yWnd,       // pos
      cxWnd, cyWnd,     // size
      NULL,             // no parent
      hMenu,            // menu if any
      hInstance,        // instance handle
      NULL              // no params
      )))
      return FALSE;

   // Create CHILD input window:
   if (!(vhwndIn = CreateWindow(
      vpszClassIn,      // class name
      NULL,             // window title
      WS_CHILD,         // style
      0, 0,
      0, 0,
      vhwndAN,
      NULL,             // no menu
      hInstance,        // instance handle
      NULL              // no params
      )))
      return FALSE;

   // Create output window:
   if (!MakeOutputWindow(vhwndAN, xWnd, yWnd + cyWnd + 1, 0, 0, cmdShow))
      return FALSE;

   // start with dialog open:
   PostMessage(vhwndAN, WM_COMMAND, IDM_DLG, 0L);

   ShowWindow(vhwndAN, cmdShow);
   ShowWindow(vhwndIn, cmdShow);
   UpdateWindow(vhwndAN);
   UpdateWindow(vhwndIn);
   return TRUE;
   }

/*+-------------------------------------------------------------------------*/
WORD NEAR PASCAL
GetEditVal(
   HWND hdlg,
   int id,
   WORD u)
   {
   char sz[cbSzTMax];
   LPSTR lpsz = (LPSTR)sz;
   LONG lValue;
   GetDlgItemText(hdlg, id, lpsz, cbSzTMax);

   if (!isdigit(*lpsz) && *lpsz != '-')
      goto errGEV;      // first char not digit or minus sign

   for (++lpsz; *lpsz; lpsz++)
      if (!isdigit(*lpsz))
         goto errGEV;   // other char not digit

   lValue = atol(sz);
   if(lValue < 0){
      goto errGEV;      // Parameters must not be negative
   }else if(lValue > (LONG)0xFFFF){
      goto errGEV;      // Parameters must not be larger than 16bit UINT max
   }

   return (WORD)lValue;  // good value

errGEV:
   // error in input: restore old value:
   SetDlgItemInt(hdlg, id, u, FALSE);
   MessageBeep(0);
   return u;
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL
InitDlgItemInt(
   HWND hdlg,
   int id,
   UINT i)  
   {
   HWND hwndEdit = GetDlgItem(hdlg, id);
   HRC hrc = (HRC)SendMessage(hwndEdit, WM_PENMISC, PMSC_GETHRC, 0L);

   SetDlgItemInt(hdlg, id, i, FALSE);

   // set up for numeric input:
   if (hrc)
      {
      SetAlphabetHRC(hrc, ALC_NUMERIC | ALC_PUNC, NULL);
      SendMessage(hwndEdit, WM_PENMISC, PMSC_SETHRC, (LPARAM)hrc);
      DestroyHRC(hrc);  // our copy
      }
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL
LoadSave(
   BOOL fOpen)
   {
   HCURSOR hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
   HFILE hfile;
   static char const szOpenTitle[] = "抬操傪奐偔";
   static char const szSaveTitle[] = "抬操傪曐懚";

   if (!*vszFile)
      lstrcpy((LPSTR)vszFile, vszSaveFileDef);

   if (FGetFileName(vhwndAN, fOpen, vszFile))
      {
      OFSTRUCT of;

      if ((hfile = OpenFile((LPSTR)vszFile, &of,
         fOpen? OF_READ: OF_CREATE)) != HFILE_ERROR)
         {
         if (fOpen)
            {
            if (vhpndt)
               DestroyPenData(vhpndt);
            vhpndt = ReadPenData(hfile);
            Redraw();
            }
         else
            WritePenData(hfile, vhpndt);
         _lclose(hfile);
         }
      else ErrBox("抬操偑奐偗傑偣傫偱偟偨丅", szOpenTitle);
      }
   else if (fOpen)
      InfoBox("抬操偑奐偗傑偣傫偱偟偨丅", szOpenTitle);
   else
      InfoBox("抬操偑曐懚偱偒傑偣傫偱偟偨丅", szSaveTitle);

   SetCursor(hCursor);
   }

/*+-------------------------------------------------------------------------*/
BOOL NEAR PASCAL        // ret T if the window is successfully created
MakeOutputWindow(       // Creates a popup window into which to draw pendatas
   HWND hwndParent,     // Parent
   int x,               // x location
   int y,               // y location
   int cx,              // Window width, or 0 for default
   int cy,              // Window height, or 0 for default
   int nCmdShow)        // how to show window
   {
   DWORD dwExStyle = 0; // could be topmost
   DWORD dwStyle = WS_OVERLAPPEDWINDOW;

   if (!(vhwndOut = CreateWindowEx(
      dwExStyle,     // extended style
      vpszClassOut,  // class name
      vpszWndOut,    // window title
      dwStyle,       // style
      x, y,
      cx? cx: GetSystemMetrics(SM_CXSCREEN) / 3,
      cy? cy: GetSystemMetrics(SM_CYSCREEN) / 4,
      hwndParent,
      NULL,          // no menu
      vhInstanceCur, // instance handle
      NULL           // no params
      )))
      return FALSE;

   ShowWindow(vhwndOut, nCmdShow);
   UpdateWindow(vhwndOut);
   return TRUE;
   }

/*+-------------------------------------------------------------------------*/
HPENDATA NEAR PASCAL ReadPenData(   // ret handle to pendata
   HFILE hfile)   // Handle of open file
/*------------------------
:  Reads pen data from a file. The file format at this point is
:  a LONG integer value representing the size of the pendata, followed
:  by that many bytes of pendata.
:
:  Before calling this function, the caller should have already
:  opened the file specified by hfile and ensured that the
:  file pointer is offset to the beginning of the pen data.
:  When the function returns, the file pointer will be offset
:  to the end of the pen data in the file.
------------------------*/
   {
   HPENDATA hpndt = NULL;
   LONG     cb, cbRead, cbHpndt;
   BYTE     lpbBuf[cbBufMax];    // buffer
   DWORD    dwState = 0L;        // required init
   BOOL     fError = FALSE;

   if (!hfile
      || (cb = _lread(hfile, &cbHpndt, (UINT)sizeof(LONG))) == HFILE_ERROR
      || cb != sizeof(LONG))
         return NULL;

   while (cbHpndt > 0)
      {
      if ((cbRead = _lread(hfile, lpbBuf, (UINT)min(cbHpndt, cbBufMax)))
         == HFILE_ERROR
         || PenDataFromBuffer(&hpndt, 0, lpbBuf, cbBufMax, &dwState) < 0)
         {
         if (hpndt)
            DestroyPenData(hpndt);
         return NULL;
         }
      cbHpndt -= cbRead;
      }

   return hpndt;
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL  // no ret
ShowCancel(       // hides the redraw button and shows the cancel button in the options dialog
   BOOL fShow)    // T show cancel button, F show redraw
/*------------------------
:  both buttons occupy the same place. Escape is a keyboard accelerator
:     for Cancel.
------------------------*/
   {
   if (IsWindow(vhdlg))
      {
      HWND hwndShow = GetDlgItem(vhdlg, fShow? IDCANCEL: IDD_PBREDRAW);
      HWND hwndHide = GetDlgItem(vhdlg, fShow? IDD_PBREDRAW: IDCANCEL);

      if (fShow != IsWindowVisible(GetDlgItem(vhdlg, IDCANCEL)))
         {
         ShowWindow(hwndShow, SW_SHOW);
         ShowWindow(hwndHide, SW_HIDE);
         InvalidateRect(hwndShow, NULL, FALSE);
         UpdateWindow(hwndShow);
         }
      }
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL        // no ret
TermAN(                 // terminate app
   VOID)                // no params
   {
   static BOOL fTerminated = FALSE;

   if (!fTerminated)
      {
      FreeProcInstance((FARPROC)vlpDlgProc);
      vlpDlgProc = NULL;
      fTerminated = TRUE;
      }
   }

/*+-------------------------------------------------------------------------*/
VOID NEAR PASCAL        // no ret
TPtoCP(                 // tablet to client
   HWND hwnd,           // Handle to client window
   LPPOINT lppt,        // Pointer to an array of points
   int cpt,             // Count of points
   LPRECT lprect)       // Bounding rectangle calculated, or NULL
/*------------------------
:  converts an array of points in tablet coords to client
:  coordinates and optionally calculates a bounding rectangle
:  in client coordinates.
------------------------*/
   {
   POINT pt;
   BOOL fFirst = TRUE;

   // get upper left corner of client in tablet coords:
   pt.x = pt.y = 0;
   ClientToScreen(hwnd, &pt);
   DPtoTP(&pt, 1);

   for (lppt = lppt + cpt - 1; cpt > 0; --cpt, --lppt)
      {
      lppt->x -= pt.x;
      lppt->y -= pt.y;

      if (lprect)
         {
         if (fFirst)
            {
            lprect->left = lprect->right = lppt->x;
            lprect->top = lprect->bottom = lppt->y;
            fFirst = FALSE;
            }

         if (lppt->x > lprect->right)
            lprect->right = lppt->x;
         else if (lppt->x < lprect->left)
            lprect->left = lppt->x;

         if (lppt->y > lprect->bottom)
            lprect->bottom = lppt->y;
         else if (lppt->y < lprect->left)
            lprect->top = lppt->y;
         }
      }

   if (lprect)
      TPtoDP((LPPOINT)lprect, 2);
   }

/*+-------------------------------------------------------------------------*/
BOOL NEAR PASCAL WritePenData(   // ret T if successful
   HFILE hfile,         // Handle to open file
   HPENDATA hpndt)      // pendata to write
/*------------------------
:  Writes pen data into a file, preceded by a LONG integer consisting of
:  the size of the pen data in bytes.
:
:  Before calling this function, the caller should have
:  already opened the file specified by hfile and ensured that
:  the file pointer is correctly placed.  When the function
:  returns, the file pointer will be offset to the end of the
:  pen data in the file. The function fails if the pen data is
:  larger than 64K.
------------------------*/
   {
   BYTE lpbBuf[cbBufMax];
   DWORD dwState = 0L;  // required init
   LONG cb;
   LONG cbSize;

   if (!hfile || !hpndt)
      return FALSE;

   if (GetPenDataAttributes(hpndt, (LPVOID)&cbSize, GPA_SIZE) < 0)
      return FALSE;

   if (_lwrite(hfile, (LPCSTR)&cbSize, sizeof(LONG)) == HFILE_ERROR)
      return FALSE;

   while ((cb = PenDataToBuffer(hpndt, lpbBuf, cbBufMax, &dwState)) > 0L)
      if (_lwrite(hfile, lpbBuf, (UINT)cb) == HFILE_ERROR)
         return FALSE;

   return cb >= 0;
   }

/******************* WINMAIN ************************************************/

/*+-------------------------------------------------------------------------*/
int PASCAL                 // ret errorlevel
WinMain(                   // Windows entry point; proto in <windows.h>
   HINSTANCE hInstance,    // current instance
   HINSTANCE hPrevInstance,// prev instance if any, or NULL
   LPSTR lpszCommandLine,  // ptr to commandline args
   int cmdShow)            // WM_SHOW param
   {
   MSG msg;

   /* Even though this application links to penwin.lib, we still
      have to check that Pen Windows was properly initialized (i.e.
      loaded as an installable driver). GetSystemMetrics() returns
      a valid module handle in this case:
   */
   if (!(vhPenWin = (HINSTANCE)GetSystemMetrics(SM_PENWINDOWS)))
      {
      MessageBox(NULL,
         (LPSTR)"偙偺碧哓拱籍輦蛠AWindows95 For Pen Servises 偺霓撞兽偑郯棉ㄝ皋偝傟偰偄傞昁梫偑偁傝傑偡丅",
         (LPSTR)"瓦輷鼦蜁`夋惠踢偬咣皋籽", MB_TASKMODAL|MB_ICONSTOP|MB_OK);
      return wExitError;
      }

	// to ensure compatability using 3.x compilers (this is not
	// required if your RC compiler marks code as 4.0):
	SetPenAppFlags(RPA_DEFAULT, PENVER);

   if (!FInitAN(hInstance, hPrevInstance, lpszCommandLine))
      exit(wExitError);

   if (FInitInstance(hInstance, hPrevInstance, cmdShow)) // show main window
      {
      while (GetMessage((LPMSG)&msg, NULL, 0, 0) )
         {
         if (!vhdlg || !IsDialogMessage(vhdlg, &msg))
            {
            TranslateMessage((LPMSG)&msg);
            DispatchMessage((LPMSG)&msg);
            }
         }
      }
   else
      msg.wParam = wExitError;   // error

	SetPenAppFlags(0, 0);			// see note above
   TermAN();   // insurance
   return msg.wParam;
   }
