/****************************************************************************
Module name: Zaxis.C

Copyright (C) 1992-1996 Elo TouchSystems, Inc.
	105 Randolph Road
	Oak Ridge, TN  37830

Written by Gary Young
Upgraded for Windows 95/NT and MSVC 4.0 by Larry Adams, May 1 1996

Special Thanks:  To Jeffrey M. Richter for his "meter" custom control
which is supplied as a DLL for this demo.
*****************************************************************************/

#include <windows.h>
#include <winioctl.h>
#include <string.h>
#include "zaxis.h"  
#include "mmioctl.h"

#include <stdio.h>

// Global data
HWND	_hWndStats = NULL;
char	_szAppName[] = "Elo TouchSystems Z-Axis Demonstration";
HWND 	hDLG;									// Some handles
HWND 	hButton;
HANDLE	hDriver;
HANDLE	hndFile;
HANDLE	hinstance = NULL;				// Our instance handle
FARPROC _fpOrigWndProc;					// Used for subclassing button
LPRECT	lprc;
int		nOSVersion;						// GetOSVersion stores result here
BOOL	MouseState = FALSE;				// Simple binary state variable for
										// determining correct ZAXIS pushbutton state
MMDevIOHeader			MMIOHeader;
MMDevIOZAXIS			MMIOZAxis;
MMDevIOZAXIS_SUPPORT	MMIOZAxisSupport;

#define IDM_ABOUT       (0x0120)   		// Must be < 0xF000

#define DOWN	1
#define MOVE	2
#define UP		3

#define OS_NT	0
#define OS_95	1
#define OS_31	2

void GetOSVersion();		// sets global variable nOSVersion 
int GetZAxis();				// returns Z value (or 0)
BOOL GetZAxisSupport();		// returns true if current controller supports Z

// ***************************************************************************
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, 
					LPSTR lpszCmdLine, int nCmdShow) 
{
	MSG msg;
	HWND hWnd;
	HANDLE hLibMeter;

	hinstance = hInstance;					// Save handle globally

	GetOSVersion();
	if (nOSVersion == OS_31) return(0);

	if (hPrevInstance == NULL)
		if (!RegisterAppWndClass(hInstance))
			return(0);

	if (!(hLibMeter = LoadLibrary("METER.DLL"))) {
		MessageBox(NULL, "Could not find METER.DLL", "No METER.DLL", MB_OK);
		return(0);
	}

	hWnd = CreateWindow(_szAppName, _szAppName,
						WS_OVERLAPPED | WS_VISIBLE | WS_CLIPCHILDREN |
						WS_CAPTION | WS_SYSMENU, 
						CW_USEDEFAULT, nCmdShow, CW_USEDEFAULT, CW_USEDEFAULT,
						NULL, NULL, hInstance, 0);

	if (hWnd == NULL)
		return(0);

	while (GetMessage(&msg, NULL, 0, 0)) {
		if (!IsDialogMessage(_hWndStats, &msg)) {
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}
	}

	FreeLibrary(hLibMeter);

	return(0);
}


// ***************************************************************************
// This function registers the application's main window.

BOOL NEAR PASCAL RegisterAppWndClass (HANDLE hInstance) 
{
	WNDCLASS WndClass;

	WndClass.style         = 0;
	WndClass.lpfnWndProc   = (WNDPROC)AppWndProc;
	WndClass.cbClsExtra    = 0;
	WndClass.cbWndExtra    = 0;
	WndClass.hInstance     = hInstance;
	WndClass.hIcon         = LoadIcon(hInstance, "EloCalIco");
	WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
	WndClass.lpszMenuName  = NULL;
	WndClass.lpszClassName = _szAppName;
	return(RegisterClass(&WndClass));
}


// ***************************************************************************
// This function processes all messages sent to the modeless dialog box.

LRESULT CALLBACK ZaxisDlgProc (HWND hDlg, WORD wMsg, WORD wParam, LONG lParam) 
{
	BOOL fProcessed = TRUE;
	FARPROC fpButtonSubClassProc;
	
	switch (wMsg) {
		case WM_INITDIALOG:
			
			hDLG = hDlg;

			// Tell Meter control that the job consists of 255 parts.
			SendDlgItemMessage(hDlg, ID_METER, MM_SETPARTSINJOB, 255, 0);

			// Tell Meter control that the zero parts of the job are complete.
			SendDlgItemMessage(hDlg, ID_METER, MM_SETPARTSCOMPLETE, 0, 0);

			_fpOrigWndProc = (FARPROC) GetWindowLong(GetDlgItem(hDlg, IDZAXIS),
													 GWL_WNDPROC);
			fpButtonSubClassProc = MakeProcInstance((FARPROC) ButtonSubClassProc, 
													hinstance);
			SetWindowLong(GetDlgItem(hDlg, IDZAXIS), GWL_WNDPROC,
						  (LONG) fpButtonSubClassProc);

			SetFocus (GetDlgItem(hDlg, IDZAXIS));

			return (FALSE);

		default:
			fProcessed = FALSE;
			break;
	}

	return(fProcessed);
}


// ***************************************************************************
// This function processes all messages sent to the app's main window.

LRESULT CALLBACK AppWndProc (HWND hWnd, WORD wMsg, WORD wParam, LONG lParam) 
{
	BOOL	fCallDefProc = FALSE;
	BOOL	MonitorMouse = FALSE;
	LONG	lResult = 0;
	RECT	rc, rcStatDlg;
	FARPROC	fpProc;
	HMENU	hMenu;

	switch (wMsg) {
		case WM_DESTROY:
			PostQuitMessage(0);
			break;

		case WM_CREATE:

			//
			// Open Win32 touchscreen device(MMDev)
			//

			if (nOSVersion == OS_NT) 			// Open the Device "MMDev"
    			hndFile = CreateFile("\\\\.\\MMDev", GENERIC_READ, 
										FILE_SHARE_READ,
										NULL, OPEN_EXISTING, 0, NULL);
			else								// Open the Device "MONMOUSE"
				hndFile = CreateFile("\\\\.\\MONMOUSE", 0, 0, 0, 
										OPEN_EXISTING, 
										FILE_ATTRIBUTE_NORMAL, 0);

	   		if (hndFile == INVALID_HANDLE_VALUE) {		// Was the device opened?
				MessageBox(NULL, "Unable to open touchscreen.  Is driver loaded?",
						   "Driver Loaded?", MB_OK);
				return(0);
			}

			// See if controller supports Z before going any further

			// init the header for all 95 driver io control calls
			MMIOHeader.DriverVersion = VERSIONLOCK;
			MMIOHeader.ApplicationVersion = VERSIONLOCK;
			MMIOHeader.TouchScreenNumber = 0;

			MMIOZAxis.Header = MMIOHeader;
			MMIOZAxisSupport.Header = MMIOHeader;

			if (!GetZAxisSupport()) { 				// No z
				MessageBox(NULL, 
					"This controller does not support Z reporting capabilities",
					"NO Z", MB_OK | MB_ICONEXCLAMATION);
				DestroyWindow(hWnd);				// Quit
				break;
			}

			// Display Z-Axis custom controls
			// Try to create modeless dialog box.

			_hWndStats = CreateDialog(hinstance, "Zaxis", hWnd, 
										(DLGPROC) ZaxisDlgProc) ;
			if (_hWndStats == NULL) {
				// If modeless dialog box couldn't be created, tell
				// Windows not to create the window and return.
				lResult = -1;
				break;
			}

			// Change the window dimensions so that it exactly
			// surrounds the modeless dialog box.  The position 
			// on the screen should not be altered.

			GetWindowRect(hWnd, &rc);
			GetWindowRect(_hWndStats, &rcStatDlg);
			MoveWindow(hWnd, rc.left, rc.top,
            rcStatDlg.right - rcStatDlg.left,
            rcStatDlg.bottom - rcStatDlg.top +
            GetSystemMetrics(SM_CYCAPTION), FALSE);

			// Get handle to the System Menu.
			// Append separator bar & two options.

			hMenu = GetSystemMenu(hWnd, 0);

			// Append separator bar & two options.
	
			AppendMenu(hMenu, MF_SEPARATOR, 0, 0);
			AppendMenu(hMenu, MF_STRING, IDM_ABOUT, "&About...");

			DrawMenuBar(hWnd);

	 		lResult = 0;   // Window has been created OK.
			break;

      case WM_SYSCOMMAND:

			// Any option's that we appended to System menu should be
			// processed by Zaxis and NOT passed to DefWindowProc.

			switch (wParam & 0xfff0) {
				case IDM_ABOUT:

					// Display about box

					fpProc = MakeProcInstance(AboutProc, hinstance);
					DialogBox(hinstance, "AboutBox", hWnd, fpProc);
					FreeProcInstance(fpProc);
					break;

				case IDCANCEL:
					FreeLibrary(hDriver);
					break;

				case SC_RESTORE:
					return 0;

				default:

					// Any option's that we do not process should be 
					// passed to DefWindowProc.

					fCallDefProc = TRUE;
					break;
			}

			break;

		default:
			fCallDefProc = TRUE;
			break;
	}

	if (fCallDefProc)
		lResult = DefWindowProc(hWnd, wMsg, wParam, lParam);

	return(lResult);
}


// ***************************************************************************
// This function processess all messages sent to the About dialog box.

LRESULT CALLBACK AboutProc (HWND hDlg, WORD wMsg, WORD wParam, LONG lParam) 
{
	char szBuffer[100];
	BOOL fProcessed = TRUE;

	switch (wMsg) {

		case WM_INITDIALOG:

			// Set version static window to have date and time of compilation.

			wsprintf(szBuffer, "%s at %s", (LPSTR) __DATE__, (LPSTR) __TIME__);
			SetWindowText(GetDlgItem(hDlg, ID_VERSION), szBuffer);
			break;

		case WM_COMMAND:
			switch (LOWORD(wParam)) {
				case IDOK:
				case IDCANCEL:
					if (HIWORD(wParam) == BN_CLICKED)
					EndDialog(hDlg, wParam);
					break;

				default:
					break;
			}
			break;

		default:
			fProcessed = FALSE;
			break;
	}
	return(fProcessed);
}

/****************************************************************************
 This procedure is the subclass procedure for the standard "button" control.
 It allows mouse events to obtain Z-Axis values.
*****************************************************************************/
LRESULT CALLBACK ButtonSubClassProc (HWND hDlg, WORD wMsg,
					 WORD wParam, LONG lParam)
{

	LONG lResult = 0;
	BOOL fCallOrigWndProc = TRUE;
	int Z;

	switch (wMsg) {
		case WM_LBUTTONDOWN:

			// We will always use a button down to determine the
			// start of a correct sequence.  When we get a mouse move
			// message, this state variable will be checked, and if
			// not true this means we have dropped a button down or
			// a button down occurred outside of the ZAXIS button.

			MouseState = TRUE;

			// Call the driver to get Z values.  A IOCTL code is used
			// to interface to the driver.  Note that if IoctlResult returns
			// 0, we had a mouse event and will indicate 100% for this.
			// Otherwise, we had a valid touch and the Z value will 
			// be passed to the meter control.

			Z = GetZAxis();

			if (!Z)								// We had a real mouse event
				Z = 255;						// Set Z to 100%

			SendDlgItemMessage(hDLG, ID_METER, MM_SETPARTSCOMPLETE,	Z, 0);
	
			fCallOrigWndProc = TRUE;

			break;

		case WM_LBUTTONDBLCLK:

			MouseState = TRUE;

			//
			// Call the driver to get Z values
			//

			Z = GetZAxis();

			if (!Z)       							// We had a real mouse event
				Z = 255;							// Set Z to 100%

			SendDlgItemMessage(hDLG, ID_METER, MM_SETPARTSCOMPLETE, Z, 0);
	
			fCallOrigWndProc = TRUE;

			break;


		case WM_MOUSEMOVE:

			// Tells the Meter control the new number of parts that
			// are complete.  This is an example of the Meter control
			// allows for the "parts-complete" value to go both
			// up and down.
						
			// If MouseState is not TRUE(we have had a button down and
			// no button up event), we have dropped a button down or
			// the user began touching outside of the ZAXIS button
			// and then slid his finger onto the button.  Just send
			// zero to the Meter control if this is the case.

			if (MouseState) {

				Z = GetZAxis();
				
				if (!Z)
					Z = 255;		// Set Z to 100%

				SendDlgItemMessage(hDLG, ID_METER, MM_SETPARTSCOMPLETE, Z, 0);

			}

			fCallOrigWndProc = TRUE;

			break;

		case WM_LBUTTONUP:

			// Reset variable for the next mouse event sequence

			MouseState = FALSE;

			// Reset meter to 0
			SendDlgItemMessage(hDLG, ID_METER, MM_SETPARTSCOMPLETE,	0, 0);

			fCallOrigWndProc = FALSE;

			break;
	}

	// Call the default Button Window Proc

	lResult = CallWindowProc(_fpOrigWndProc, hDlg, wMsg, wParam, lParam);

	return(lResult);

}


//*****************************************************************************
// NT, 95 or 3.1???

void GetOSVersion()
{
	int		nVerTemp;

	nVerTemp = GetVersion();
	if (nVerTemp & 0x80000000) {
		if ((nVerTemp & 0x000000FF) == 3)
			nOSVersion = OS_31;
		else 
			nOSVersion = OS_95;
	}	
	else 
		nOSVersion = OS_NT;
}

//******************************************************************************
// Get the last z axis value from the driver. 
// Note different structures for NT and 95
int GetZAxis()
{
	LONG 	IOBuffer = 0;			// Input/output system buffer (NT)
	LONG	IoctlCode;				// Function to perform
	ULONG	DataLength = 4;			// Output buffer data length (NT)
	DWORD	ReturnedLength;			// Actual number of bytes returned
	BOOL	IoctlResult;			// RUE if IO control call successful

	IoctlCode = IOCTL_MM_ZAXIS;

	if (nOSVersion == OS_NT) {
		IoctlResult = DeviceIoControl(
						hndFile,            // Handle to device
						IoctlCode,          // IO Control code for Read
						NULL,				// We don't need input data
						0,  				// No input buffer required, NULL bytes
						&IOBuffer,	        // Buffer from driver.
						DataLength,			// Length of buffer in bytes.
						&ReturnedLength,    // Bytes placed in DataBuffer.
						NULL                // NULL means wait till op. completes.
					) ;
		if (!IoctlResult)
			IOBuffer = 0;
	}
	else {
		IoctlResult = DeviceIoControl(
						hndFile,            // Handle to device
						IoctlCode,          // IO Control code for Read
						&MMIOZAxis,			// Input data (header needed)
						SIZEOF_MMDevIOZAXIS,// Input buffer size
						&MMIOZAxis,	        // Output data from driver.
						SIZEOF_MMDevIOZAXIS,// Length of buffer in bytes.
						&ReturnedLength,    // Bytes placed in DataBuffer.
						NULL                // NULL means wait till op. completes.
					) ;
		if (IoctlResult) {
			if (MMIOZAxis.Touching)
				IOBuffer = MMIOZAxis.zaxis;
			else
				IOBuffer = 0;
		}
	}
	return (int)IOBuffer;
}

//*****************************************************************************
// Get Z axis support. Note different structures for NT and 95

BOOL GetZAxisSupport()
{
	LONG 	IOBuffer = 0;			// Input/output system buffer (NT)
	LONG	IoctlCode;				// Function to perform
	ULONG	DataLength = 4;			// Output buffer data length (NT)
	DWORD	ReturnedLength;			// Actual number of bytes returned
	BOOL	IoctlResult = FALSE;	// TRUE if IO control call successful

	IoctlCode = IOCTL_MM_ZAXIS_SUPPORT;

	if (nOSVersion == OS_NT) {
		IoctlResult = DeviceIoControl(
	                    hndFile,			// Handle to device
    	                IoctlCode,			// IO Control code for Read
						NULL,				// We don't need input data
						0,					// No input buffer required, NULL bytes
						&IOBuffer,	        // Buffer from driver.
						DataLength,			// Length of buffer in bytes.
						&ReturnedLength,	// Bytes placed in DataBuffer.
						NULL				// NULL means wait till op. completes.
					) ;
		return (IoctlResult);
	}
	else {
		IoctlResult = DeviceIoControl(
						hndFile,            // Handle to device
						IoctlCode,          // IO Control code for Read
						&MMIOZAxisSupport,			// Input data (header needed)
						SIZEOF_MMDevIOZAXIS_SUPPORT,// Input buffer size
						&MMIOZAxisSupport,	        // Output data from driver.
						SIZEOF_MMDevIOZAXIS_SUPPORT,// Length of buffer in bytes.
						&ReturnedLength,    // Bytes placed in DataBuffer.
						NULL                // NULL means wait till op. completes.
					) ;
		return (MMIOZAxisSupport.Supported);
	}
}
// *****************************  End of zaxis.c  ******************************
