/* IP.c InkPut sample

	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, 1994  Microsoft Corporation.  All Rights Reserved.


	This file demonstrates how to use Pen Windows 2.0 input and
	inking ("inkput") APIs, and Handwriting Recognizer (HRC) APIs.

	For new APIs, a pendata is created for the Polling example, but
	recognition is started for the event example.

	Tabs are set at 1, 4, 7, ...
*/


/******************* Includes and Controlling Defines ***********************/
#include <windows.h>
#include <penwin.h>
#include "ip.h"
#include <string.h>

/******************* Defines ************************************************/

#define chNull							'\0'	// terminator
#define DEFTIMEOUT					1000	// timeout in ms
#define CBCHBUF						1024
#define CSYVMAX						64		// symbol chunk

#define INDX_MIOLDAPI				0
#define INDX_MINEWAPI				1
#define INDX_MIDODEFAULT	2

#define INDX_NEW_BEGIN				3
#define INDX_MIAPPRELEASES			3
#define INDX_MIAPPINKS				4
#define INDX_MIDOPOLLING			5
#define INDX_NEW_END					5

#define INDX_OLD_BEGIN				6
#define INDX_MIRECOGNIZE			6
#define INDX_MIRECOGNIZEDATA		7
#define INDX_MIPROCESSWRITING		8
#define INDX_MINULLRECOG			9
#define INDX_OLD_END					9

#define INDX_DEPENDENT_BEGIN		3
#define INDX_DEPENDENT_END			9

#define BUFF_SIZE						64
#define RGBAPP							RGB(255,0,0)	// app inks in blue
#define RGBDEF							RGB(0,0,255)	// default ink is red

/******************* Macros *************************************************/

#define Unused(x)						x

// check if a menu option is checked:
#define FMenu(i)			(vrgfMenu[i].fFlag)

// check a menu item, if both its flag and condition f are TRUE:
#define MenuCheck(hmenu, indx, f) \
	CheckMenuItem(hmenu, vrgfMenu[indx].miMenuId, \
	MF_BYCOMMAND | (((f) && FMenu(indx))?\
		MF_CHECKED: MF_UNCHECKED))

// enable a menu item, if condition f is TRUE:
#define MenuEnable(hmenu, indx, f) \
	EnableMenuItem(hmenu, vrgfMenu[indx].miMenuId, \
		MF_BYCOMMAND | ((f)? MF_ENABLED: (MF_DISABLED|MF_GRAYED)))

// invalidate & update:
#define RefreshWindow(hwnd)	do {\
	InvalidateRect(hwnd, NULL, TRUE);\
	UpdateWindow(hwnd);\
} while (0)

// clear pendata:
#define ClearVhpndt() do {\
	if (vhpndt)\
		DestroyPenData(vhpndt);\
	vhpndt = NULL;\
} while (0)

// clear text:
#define ClearText()	vrgcBuff[0] = chNull

/******************* Typedefs ***********************************************/

// menu management struct:
typedef struct tagMENUFLAGS
   {
   UINT miMenuId;
   BOOL fFlag;
   }
	MENUFLAGS;

/******************* Variables **********************************************/
// global variables have a 'v' prefix

// menu options:
MENUFLAGS vrgfMenu[] =
   {
   miOldApi,				TRUE,		// default to PenWin 1.0 APIs on startup
   miNewApi,				FALSE,	// use PenWin 2.0 APIs
   miDoDefault,	FALSE,	// use DoDefault
   miAppReleases,			FALSE,	// app controls when input ends
   miAppInks,				FALSE,	// app does inking
   miDoPolling,			FALSE,	// input by polling, not events
   miRecognize,			FALSE,	// 1.0 Recognize API
   miRecognizeData,		FALSE,	// 1.0 RecognizeData API
   miProcessWriting,		FALSE,	// 1.0 ProcessWriting API
   miNullRecog,			FALSE		// 1.0 ink collection (null recognizer)
   };

// misc globals, autoinit as NULL:
HANDLE vhwndMain;						// main window
HANDLE vhinstCur;						// current instance
HPENDATA vhpndt;						// the pendata
POINT vrgPt[BUFF_SIZE];				// place to buffer points
POINT vptLast;							// the last point
HDC vhdcScreen;						// screen DC
HPEN vhpenDraw;						// drawing GDI pen
HPEN vhpenScreen;						// saved old pen when select vhpenDraw
HPCM vhpcm;								// handle to pen collection mode
int vnWid2Pen;							// halfwidth of pen nib
HRC vhrc;								// handle to a handwriting context
HRCRESULT vhrcresult;				// handle to a recognition result
SYV vrgsyv[CSYVMAX];					// symbol result buffer

char *vszInkputClass = "InkputSampleClass";	// main window class name
char *vszInkputWnd = "Inkput Sample";			// main window name
char vrgcBuff[CBCHBUF];								// char buffer for recog results


/******************* Local prototypes ***************************************/

LPARAM	FAR  PASCAL	InkputWndProc(HWND, UINT, WPARAM, LPARAM);
int		     PASCAL	WinMain(HANDLE, HANDLE, LPSTR, int);

VOID     NEAR PASCAL DoPolling(HWND);
BOOL		NEAR PASCAL	FInitApp(HANDLE);
BOOL		NEAR PASCAL	FInitInstance(HANDLE, HANDLE, int);
BOOL		NEAR PASCAL FTermInstance(VOID);
BOOL		NEAR PASCAL FWMCommandHandler(HWND, WORD, LONG);
LRESULT  NEAR PASCAL LDoNewApis(HWND, UINT);
LRESULT  NEAR PASCAL LDoOldApis(HWND);
LRESULT  NEAR PASCAL LPeneventHandler(HWND, WORD, LONG);
VOID     NEAR PASCAL ManageMenuItems(UINT);
VOID     NEAR PASCAL TPtoCP(HWND, LPPOINT, int, LPRECT);


/******************* PUBLIC FUNCTIONS ***************************************/

/*--------------------------------------------------------------------------*/
LPARAM FAR PASCAL	// wndproc ret; EXPORTed
InkputWndProc(		// Window Procedure for app's main window
	HWND hwnd,		// standard wndproc parameters
	UINT message,
	WPARAM wParam,
	LPARAM lParam)
	{
	LPARAM lRet = 0L;
	DWORD dwExtraInfo;

	switch (message)
		{
	case WM_LBUTTONDOWN:
		// get extra info assoc with click/tap event:
		dwExtraInfo = GetMessageExtraInfo();

		if (IsPenEvent(message, dwExtraInfo))	// checks PDK bits
			{
			// use 1.0 APIs:
			if (FMenu(INDX_MIOLDAPI))
				return LDoOldApis(hwnd);

			// use 2.0 inkput APIs:
			if (FMenu(INDX_MINEWAPI))
				return LDoNewApis(hwnd, LOWORD(dwExtraInfo));

			// do the default thing, including recognition:
			DoDefaultPenInput(hwnd, LOWORD(dwExtraInfo));
			}
      break;


	case WM_PENEVENT:	// generated by inkput process
		if (!FMenu(INDX_MIOLDAPI)
			&& (lRet = LPeneventHandler(hwnd, wParam, lParam)))
				return lRet;
		break;   		// do DefWindowProc


	case WM_RCRESULT:	// generated only by 1.0 APIs
		{
		LPRCRESULT lpr = (LPRCRESULT)lParam; // ptr to RCRESULT struct
		int iCh;

		ClearText();						// preterminate
		if (lpr->cSyv
			&& (lpr->wResultsType == RCRT_DEFAULT || lpr->wResultsType==RCRT_UNIDENTIFIED))
			{
			SymbolToCharacter(lpr->lpsyv, lpr->cSyv, vrgcBuff, &iCh);
			vrgcBuff[iCh] = chNull;		// terminate
			RefreshWindow(hwnd);
			}
		else if (lpr->hpendata)			// Null Recognizer
			{
			POINT pt;
			HPENDATA hpndt = DuplicatePenData(lpr->hpendata, 0);

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

			// move the pendata to effective client origin:
			OffsetPenData(hpndt, -pt.x, -pt.y);

			if (vhpndt)	// already exists? append
				{
				InsertPenData(vhpndt, hpndt, IX_END);
				DestroyPenData(hpndt);	// don't need our local copy now
				}
			else
				vhpndt = hpndt;			// assume title
			}
		}
		return 1L;	// continue


	case WM_PAINT:
		{
		HDC hdc;
		PAINTSTRUCT ps;

		InvalidateRect(hwnd, NULL, TRUE);	// ensure whole window
		if (hdc = BeginPaint(hwnd, &ps))
			{
			if (vhpndt)
				{
				if (FMenu(INDX_MIAPPINKS))
					{
					HPEN hpenOld = SelectObject(hdc, vhpenDraw);
					PENDATAHEADER ph;

					GetPenDataInfo(vhpndt, &ph, NULL, NULL);
					TPtoDP((LPPOINT)&ph.rectBound, 2);			// to screen coords
					DrawPenData(hdc, &ph.rectBound, vhpndt);	// redraw it
					SelectObject(hdc, hpenOld);					// all done
					}
				else
					RedisplayPenData(hdc, vhpndt, NULL, NULL, 2, RGBAPP);
				}

			if (vrgcBuff[0])
				TextOut(hdc, 0, 0, vrgcBuff, _fstrlen(vrgcBuff));

			}
		EndPaint(hwnd, &ps);
		}
		break;


	case WM_COMMAND:
		if (FWMCommandHandler(hwnd, wParam, lParam))
			return 0L;	// we handled it
		break;


	case WM_DESTROY:
		PostQuitMessage(wParam);
		return 1L;


	default:
		break;
		}

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



/*--------------------------------------------------------------------------*/
int PASCAL						// ret errorlevel 0 if successful, else 1
WinMain(							// Windows entry point
	HANDLE hInstance,			// instance handle
	HANDLE hPrevInstance,	// previous instance if any
	LPSTR lpszCommandLine,	// pointer to command line string
	int cmdShow)				// ShowWindow parameter
	{
	MSG msg;

	Unused(lpszCommandLine); 
	msg.wParam = 1;	// assume error

	if (GetSystemMetrics(SM_PENWINDOWS))	// assumes pen Windows already loaded
		{
		RegisterPenApp(RPA_DEFAULT, PENVER);
		if (hPrevInstance || FInitApp(hInstance))
			{
			if (FInitInstance(hInstance, hPrevInstance, cmdShow))
				{
				while (GetMessage((LPMSG)&msg, NULL, 0, 0))
					{
         		if (msg.message == WM_QUIT)
            		{
            		msg.wParam = 0;	// ok
            		break;
            		}
					TranslateMessage((LPMSG)&msg);
					DispatchMessage((LPMSG)&msg);
					}
				FTermInstance();
      		}
			}
		RegisterPenApp(0, 0);
		}
	else
		MessageBox(NULL,
			(LPSTR)"This application requires Pen Windows 2.0",
			NULL, MB_ICONSTOP | MB_OK);

	return msg.wParam;
	}


/******************* PRIVATE FUNCTIONS **************************************/


/*--------------------------------------------------------------------------*/
VOID NEAR PASCAL	// no ret
DoPolling(			// shows how to use polling model to gather pen input
	HWND hwnd)		// handle to window
   {
	int iRet = 0;
	STROKEINFO si;
	POINT pt;
	RECT rectCl;
	MSG msg;

	for (;;)
		{
		// Be a good Windows app, and yield to other apps:
		if (PeekMessage(&msg, NULL, NULL, NULL, TRUE))
			{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
			}

		// Gather input:
		if ((iRet = GetPenInput(vhpcm, vrgPt, NULL, 0, BUFF_SIZE, &si)) > 0)
			{
			RECT rect;

			// If app wants to release do the required processing:
			if (FMenu(INDX_MIAPPRELEASES) && (si.wPdk & PDK_DOWN))
				{
				pt = vrgPt[0];
				GetClientRect(hwnd, &rectCl);
				TPtoDP(&pt, 1);
				ScreenToClient(hwnd, &pt);
				if (!PtInRect(&rectCl, pt))
					{
					StopPenInput(vhpcm, si.cbPnts, PCMR_APPTERMINATED);
					break;
					}
				}

			/* This is where points could be given to:
			** a) A recognizer
			** b) Multiple recognizers
			** c) Target
			** d) Control
			** e) ... or merely create hpendata:
			*/

			TPtoCP(hwnd, vrgPt, iRet, &rect);
			AddPointsPenData(vhpndt, vrgPt, NULL, &si);

			// if app wants to ink:
			if (FMenu(INDX_MIAPPINKS) && vhdcScreen)
				{
				if (si.wPdk & PDK_DOWN)
					{
					if (!(si.wPdk & PDK_TRANSITION))
						{
						MoveTo(vhdcScreen, vptLast.x, vptLast.y);
						LineTo(vhdcScreen, vrgPt[0].x, vrgPt[0].y);
						}

					if (iRet > 1)
						Polyline(vhdcScreen, vrgPt, iRet);
					else
						{
						MoveTo(vhdcScreen, vrgPt[0].x, vrgPt[0].y);
						LineTo(vhdcScreen, vrgPt[0].x+1, vrgPt[0].y);
						}
					vptLast = vrgPt[iRet-1];
					}
				}
			}

		else if (iRet < 0)
			{
			/* Input terminated.
			** App would get the results here if recognition was
			** in progress, and display them if it wanted to.
			*/

			if (FMenu(INDX_MIAPPINKS) && vhdcScreen)
				{
				if (vhpenScreen)
					{
					SelectObject(vhdcScreen, vhpenScreen);
					vhpenScreen = NULL;
					}
				ReleaseDC(vhwndMain, vhdcScreen);
				vhdcScreen = NULL;
				}
			break;
			}
		}
	}



/*--------------------------------------------------------------------------*/
BOOL NEAR PASCAL			// ret T if successful
FInitApp(					// register class - called only once
	HANDLE hInstance)
	{
	WNDCLASS wc;

	wc.hCursor			= LoadCursor(NULL, IDC_PEN);
	wc.hIcon				= LoadIcon(hInstance, MAKEINTRESOURCE(iconInkput));
	wc.lpszMenuName	= MAKEINTRESOURCE(menuMain);
	wc.lpszClassName	= (LPSTR)vszInkputClass;
	wc.hbrBackground	= (HBRUSH)COLOR_WINDOW+1;
	wc.hInstance		= hInstance;
	wc.style				= CS_VREDRAW | CS_HREDRAW;
	wc.lpfnWndProc		= InkputWndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;

	return RegisterClass((LPWNDCLASS) &wc);
	}



/*--------------------------------------------------------------------------*/
BOOL NEAR PASCAL				// ret T if successful
FInitInstance(					// init data for instance and create windows
	HANDLE hInstance,			// handle to instance
	HANDLE hPrevInstance,	// handle to previous instance or NULL
	int cmdShow)				// ShowWindow param
	{
	Unused(hPrevInstance);
   vhinstCur = hInstance;	// set global

	// main window:
	if (vhwndMain = CreateWindow(
		(LPSTR)vszInkputClass, 
		(LPSTR)vszInkputWnd,	  
		WS_SYSMENU | WS_CLIPCHILDREN | WS_OVERLAPPED | WS_THICKFRAME,
		CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		NULL))
      {
      POINT pt;

      pt.x = 2;
		pt.y = 0;
      DPtoTP(&pt,1);

      vhpenDraw = CreatePen(PS_SOLID, pt.x, RGBDEF);
      vnWid2Pen = pt.x / 2 + pt.x % 2;
      ManageMenuItems(INDX_MIOLDAPI);

		ShowWindow(vhwndMain, cmdShow);
		UpdateWindow(vhwndMain);
		}

	return vhwndMain != NULL;
	}



/*--------------------------------------------------------------------------*/
BOOL NEAR PASCAL			// ret T if successful
FTermInstance(				// clean up at end of instance
	VOID)						// no params
	{
	ClearVhpndt();			// goodbye pen data

	if (vhdcScreen)
		{
		if (vhpenScreen)
			{
			SelectObject(vhdcScreen, vhpenScreen);
			vhpenScreen = NULL;
			}
		ReleaseDC(vhwndMain, vhdcScreen);
		vhdcScreen = NULL;
		}

	if (vhpenDraw)
		{
		DeleteObject(vhpenDraw);
		vhpenDraw = NULL;
		}

	return TRUE;
	}

/*--------------------------------------------------------------------------*/
BOOL NEAR PASCAL		// ret TRUE if handled, else FALSE
FWMCommandHandler(	// handles WM_COMMAND message for menu choices
	HWND hwnd,			// handle to window
	WORD wParam,		// first wndproc param
	LONG lParam)		// second wndproc param
	{
	switch (wParam)
		{
	case miClearAll:
		{
		ClearVhpndt();
		ClearText();
		RefreshWindow(hwnd);
		}
		break;

	case miExit:
		DestroyWindow(hwnd);
		break;

	case miNewApi:
		ManageMenuItems(INDX_MINEWAPI);
		ClearText();
		RefreshWindow(hwnd);
		break;

	case miDoPolling:
		ManageMenuItems(INDX_MIDOPOLLING);
		break;

	case miAppReleases:
		ManageMenuItems(INDX_MIAPPRELEASES);
		break;

	case miAppInks:
		ManageMenuItems(INDX_MIAPPINKS);
		break;

	case miOldApi:
		ManageMenuItems(INDX_MIOLDAPI);
		break;

	case miRecognize:
		ManageMenuItems(INDX_MIRECOGNIZE);
		ClearText();
		RefreshWindow(hwnd);
		break;

	case miRecognizeData:
		ManageMenuItems(INDX_MIRECOGNIZEDATA);
		if (FMenu(INDX_MIRECOGNIZEDATA))
			LDoOldApis(hwnd);							// recognize now
		ManageMenuItems(INDX_MIRECOGNIZEDATA);	// switch back to Recognize
		break;

	case miNullRecog:
		ManageMenuItems(INDX_MINULLRECOG);
		ClearText();
		RefreshWindow(hwnd);
		break;

	case miProcessWriting:
		ManageMenuItems(INDX_MIPROCESSWRITING);
		ClearText();
		RefreshWindow(hwnd);
		break;

	case miDoDefault:
		ManageMenuItems(INDX_MIDODEFAULT);
		ClearText();
		RefreshWindow(hwnd);
		break;

	default:
		return FALSE;
		}

   return TRUE;
   }



/*--------------------------------------------------------------------------*/
LRESULT NEAR PASCAL	// wndproc ret to WM_LBUTTONDOWN
LDoNewApis(				// demo inkput APIs
	HWND hwnd,			// handle to window
	UINT idEvent)	// pointer to data
	{
	PCMINFO pcminfo;	// pen collection mode structure
	int errCode;

	/* Options:
   ** a) Do event driven data collection or polling
   ** b) Have the pen driver release input automatically or
	** 	keep it under application's control
	** c) Have pen/display driver handle inking or leave it under
	** 	application's control.
	*/

	// if app wants to release peninput, don't set any automatic
	// input release conditions, otherwise init fields & bits:

	if (FMenu(INDX_MIAPPRELEASES))
		pcminfo.dwPcm = 0L;	// no pen termination conditions set
	else
		{
		pcminfo.dwPcm = PCM_RECTBOUND | PCM_TIMEOUT;	// default to timeout+rect
		pcminfo.dwTimeout = (DWORD)DEFTIMEOUT;			// 1 second

		// set inclusion rect to client area, in screen coords:
		GetClientRect(hwnd, &pcminfo.rectBound);
		ClientToScreen(hwnd, (LPPOINT)&pcminfo.rectBound);
		ClientToScreen(hwnd, (LPPOINT)&pcminfo.rectBound.right);
		}

	// if app wants to do polling, set the PCM_DOPOLLING bit:
	if (FMenu(INDX_MIDOPOLLING))
		pcminfo.dwPcm |= PCM_DOPOLLING;


	// Start gathering input:
	///////////////////////////////////////////////////////////////////////
	if (!(vhpcm = StartPenInput(vhwndMain, idEvent, &pcminfo, &errCode)))
      return 0L;
	///////////////////////////////////////////////////////////////////////

	if (FMenu(INDX_MIAPPINKS))
		{
		RECT rect;

		/* App wants to do inking by itself. Get all the corresponding GDI
		** objects. DC could be obtained using GetDC or CreateDC.
		** DC will be released in DoPolling() or LPeneventHandler():
		*/

		if (vhdcScreen = GetDC(vhwndMain))
			{
			SetMapMode(vhdcScreen, MM_ANISOTROPIC);
			GetClientRect(vhwndMain, &rect);
			SetViewportExt(vhdcScreen, rect.right, rect.bottom);
			DPtoTP((LPPOINT)&rect.right, 1);
			SetWindowExt(vhdcScreen, rect.right, rect.bottom);

			if (vhpenDraw)
				vhpenScreen = SelectObject(vhdcScreen, vhpenDraw);
			}
		}
	else 	// system inking:
	///////////////////////////////////////////////////////////////////////
		StartInking(vhpcm, idEvent, NULL);
	///////////////////////////////////////////////////////////////////////

	/* This is where an app would start:
	** a) A new HRC for recognition
	** b) Multiple HRCs
	** c) Targeting
	** d) ... or merely create an hpendata:
	*/

	if (!vhpndt)
		vhpndt = CreatePenData(NULL, 0, PDTS_STANDARDSCALE, 0);

	// create global HRC handle here:
	vhrc = CreateCompatibleHRC(
		NULL,		// no default template
		NULL);	// use system default recognizer


	// If app wants to poll for points fall into a polling loop:
	if (FMenu(INDX_MIDOPOLLING))
		DoPolling(hwnd);

	return 1L;
	}



/*--------------------------------------------------------------------------*/
LRESULT NEAR PASCAL	// WndProc ret
LDoOldApis(				// use penwin 1.0 APIs
	HWND hwnd)			// handle to window
	{
	RC rc;	// Recog Context

	InitRC(hwnd, &rc);
	GetClientRect(hwnd, &rc.rectBound);
	ClientToScreen(hwnd, (LPPOINT)&rc.rectBound);
	ClientToScreen(hwnd, (LPPOINT)&rc.rectBound.right);

	rc.lRcOptions |= RCO_NOHIDECURSOR;

	if (FMenu(INDX_MIRECOGNIZE))
		{
		ClearVhpndt();
		Recognize(&rc);
		}

	else if (FMenu(INDX_MIRECOGNIZEDATA))
		{
		if (vhpndt)
			{
			RecognizeData(&rc, vhpndt);
			ClearVhpndt();
			RefreshWindow(hwnd);
			}
		}

	else if (FMenu(INDX_MINULLRECOG))
		{
		rc.hrec = NULL;
		Recognize(&rc);
		}

	else if (FMenu(INDX_MIPROCESSWRITING))
		{
		ClearVhpndt();
		ProcessWriting(hwnd, &rc);
		}

	return 1L;
	}



/*--------------------------------------------------------------------------*/
LRESULT NEAR PASCAL	// ret value used by WndProc
LPeneventHandler(		// handles WM_PENEVENT messages during pen input
	HWND hwnd,			// handle to a window
	WORD wParam,		// first wndproc param
	LONG lParam)		// second wndproc param
   {
	int iRet;
	STROKEINFO si;
	POINT pt;
   HPCM hpcm;

   // If we are doing Default Input, deal only with PE_ENDDATA message
   if (FMenu(INDX_MIDODEFAULT) && (wParam != PE_ENDDATA))
      return 0L;

   hpcm = HpcmFromLparam(lParam);
	switch (wParam)
		{
	case PE_PENDOWN:
		if (FMenu(INDX_MIAPPRELEASES))
			{
			RECT rectCl;

			/* If app wants to release input on its own - our example 
			** releases if pen down outside client rect; the user will see
			** the pen cursor change to an arrow outside the client area:
			*/

			// Examine this [pendown] penpacket without dequeueing it:
			iRet = PeekPenInput(hpcm, LOWORD(lParam), &pt, NULL, 0);

			// Check if it falls within the client bounds:
			GetClientRect(hwnd, &rectCl);
			TPtoDP(&pt, 1);
			ScreenToClient(hwnd, &pt);
			if (!PtInRect(&rectCl, pt))	// end pen input at this packet
				{
				StopPenInput(hpcm, LOWORD(lParam), PCMR_APPTERMINATED);
				break;
				}
			}
			// Fall through
	case PE_PENUP:
	case PE_MOREPACKETS:
	case PE_TERMINATING:
		// Get all the points so far:
		while ((iRet = GetPenInput(hpcm, vrgPt, NULL, 0, BUFF_SIZE, &si)) > 0)
			{
			RECT rect;

			/* Do whatever needs to be done with the points.
			** They could be given to:
			** a) A recognizer (this is what we do here)
			** b) Multiple recognizers
			** c) Target
			** d) Control
			** ... or merely create an hpendata
			*/

			AddPenInputHRC(vhrc, vrgPt, NULL, 0, &si);
			ProcessHRC(vhrc, PH_DEFAULT);

			TPtoCP(hwnd, vrgPt, iRet, &rect);
			AddPointsPenData(vhpndt, vrgPt, NULL, &si);

			if (FMenu(INDX_MIAPPINKS) && vhdcScreen)
				{
				// If app wants to do its own inking:
				if (si.wPdk & PDK_DOWN)
					{
					if (!(si.wPdk & PDK_TRANSITION))
						{
						MoveTo(vhdcScreen, vptLast.x, vptLast.y);
						LineTo(vhdcScreen, vrgPt[0].x, vrgPt[0].y);
						}

					if (iRet > 1)
						Polyline(vhdcScreen, vrgPt, iRet);
					else
						{
						MoveTo(vhdcScreen, vrgPt[0].x, vrgPt[0].y);
						LineTo(vhdcScreen, vrgPt[0].x+1, vrgPt[0].y);
						}

					vptLast = vrgPt[iRet-1];
					}
				}
			}
		break;

	case PE_TERMINATED: // pen input has been terminated for this application
   case PE_ENDDATA:    // Default Pen Input terminated

		/* Input terminated.
		** 
		** Get the results here and display them:
		*/
      if (wParam == PE_TERMINATED)
         {
   		// if app was inking, clean up the GDI objects:
	   	if (FMenu(INDX_MIAPPINKS) && vhdcScreen)
		   	{
			   if (vhpenScreen)
				   {
   				SelectObject(vhdcScreen, vhpenScreen);
	   			vhpenScreen = NULL;
		   		}
			   ReleaseDC(vhwndMain, vhdcScreen);
   			vhdcScreen = NULL;
	   		}
         }
      else
         vhrc = (HRC)(((LPTARGET)lParam)->dwExtraInfo);

		// terminate recognition and display results:
		EndPenInputHRC(vhrc);						// tell HRC end of ink
		ProcessHRC(vhrc, PH_MAX);					// finish recog

		// get a handle to the results:
		if (GetResultsHRC(vhrc, GRH_ALL, &vhrcresult, 1) == HRCR_OK)
			{
			int i = 0, cSyv = 0;

			// get a chunk of symbols:
			while ((cSyv = GetSymbolsHRCRESULT(vhrcresult, i, vrgsyv, CSYVMAX))
				> 0)
				{
				if (i + cSyv + 1 > CBCHBUF)		// don't overflow char buff
					cSyv = CBCHBUF - i - 1;
				if (cSyv > 0)							// still have something?
					{
					SymbolToCharacter(vrgsyv, cSyv, vrgcBuff + i, NULL);
					i += cSyv;
					}
				if (i + 1 >= CBCHBUF)
					break;
#ifndef APIREVIEWTODO	// do only once until API officially changes
				break;
#endif //!APIREVIEWTODO
				}
			vrgcBuff[i] = chNull;	// terminate string
			}

		DestroyHRCRESULT(vhrcresult);				// we're finished with result

      if (wParam == PE_TERMINATED)
   		DestroyHRC(vhrc);							// ditto this HRC session

		RefreshWindow(hwnd);							// view the text
		break;

	default:
		return 0L;
		}

	return 1L;
   }



/*--------------------------------------------------------------------------*/
VOID NEAR PASCAL	// no ret
ManageMenuItems(	// Set up appropriate flags for doing pen APIs
	UINT miIndx)	// index of item
	{
	int i;
	HMENU hMenu = GetMenu(vhwndMain);
	BOOL fFlagSav = FMenu(miIndx);
	BOOL f;
	BOOL fTypeChange = ((miIndx==INDX_MIOLDAPI)
		||(miIndx==INDX_MINEWAPI)
		||(miIndx==INDX_MIDODEFAULT));

	for (i = INDX_OLD_BEGIN; i <= INDX_OLD_END; ++i)
		vrgfMenu[i].fFlag = FALSE;

	if (!fTypeChange)
		{
		vrgfMenu[miIndx].fFlag = fFlagSav ^ 1;

		// If it is one of the penwin1.0 APIs:
		if (miIndx >= INDX_OLD_BEGIN && miIndx <= INDX_OLD_END)
         vrgfMenu[FMenu(miIndx)? miIndx: INDX_MIRECOGNIZE].fFlag = TRUE;
      }
	else
		{
		for (i = 0; i < INDX_DEPENDENT_BEGIN; ++i)
			vrgfMenu[i].fFlag = FALSE;

		vrgfMenu[miIndx].fFlag = TRUE;

		if (vrgfMenu[INDX_MIOLDAPI].fFlag)
			vrgfMenu[INDX_MIRECOGNIZE].fFlag = TRUE;

		if (FMenu(INDX_MINEWAPI))
			for (i = INDX_NEW_BEGIN; i <= INDX_NEW_END; ++i)
				vrgfMenu[i].fFlag = FALSE;
		}

	// Set check marks in the "Type" menuitems:
	MenuCheck(hMenu, INDX_MIOLDAPI, TRUE);
	MenuCheck(hMenu, INDX_MINEWAPI, TRUE);
	MenuCheck(hMenu, INDX_MIDODEFAULT, TRUE);

	f = FMenu(INDX_MINEWAPI);
	for (i = INDX_NEW_BEGIN; i <= INDX_NEW_END; ++i)
		{
		MenuCheck(hMenu, i, f);
		MenuEnable(hMenu, i, f);
		}

	f = FMenu(INDX_MIOLDAPI);
	for (i = INDX_OLD_BEGIN; i <= INDX_OLD_END; ++i)
		{
		MenuCheck(hMenu, i, f);
		MenuEnable(hMenu, i, f);
		}
	}



/*--------------------------------------------------------------------------*/
VOID NEAR PASCAL 			// no ret
TPtoCP(						// Convert tablet to client coords
	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
	{
	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);
	}



