///////////////////////////////////////////////////////////////////////
// SerialKey.cpp
//
// Applied Data Systems
// ------------------------------------------------------------------
// 720020-1125x
//
// Description
// -----------
// SerialKey allows you to simulate a device keyboard through a serial connection 
// application, such as Hyperteminal. This allows you to type on and navigate the 
// device using your PC's keyboard.  It also allows you to control the pointer (mouse)
// functions of the device, including drag and drop.
//
// SerialKey can be extremely useful if your device has a defective touchscreen, 
// has bad calibration data or any time when you need manipulate a device with a 
// display. Simply connect your system to your PC with Hyperterminal, put SerialKey 
// in a folder called StartUp on an ATA card, insert the card in you system and reboot.
//
// By default SerialKey attempts to connect to COM1, but the port can be changed through
// the Options dialog.  Tap on the tray icon to get to the dialog.
//
// History
// -------
// 720020-11251 - Sep 19, 2002 : ctacke
//   - Initial implementation
//
// 720020-11252 - Mar 12, 03 : ctacke
//   - added keyboard to mouse features
//   - added options dialog

#include "windows.h"
#include "resource.h"
#include <commctrl.h>

#define MAX_LOADSTRING 100

// Prototype of function that we need, but isn't in user level header files.
extern "C" BOOL IsAPIReady ( DWORD hAPI );

#define dim(x) (sizeof(x) / sizeof(x[0]))

#define MNU_OPTIONS 1000
#define MNU_EXIT 1001
#define MNU_DIV 1002

// Global Variables:
HINSTANCE		hInst			= NULL;
HWND			hWnd			= NULL;
HANDLE			hCommPort		= INVALID_HANDLE_VALUE;
HANDLE			dummy			= INVALID_HANDLE_VALUE;
HANDLE			mutex			= INVALID_HANDLE_VALUE;
HMENU			hmTrackPopup	= NULL;
HWND			hWndOptions		= NULL;
HWND			hWndPrecision	= NULL;
HWND			hWndMouse		= NULL;
HWND			hWndPort		= NULL;
HWND			hWndData		= NULL;
HWND			hWndMore		= NULL;
DWORD			g_precision		= 6;
DWORD			g_height		= 0;
DWORD			g_width			= 0;
DWORD			g_expwidth		= 0;
BOOL			g_more			= FALSE;
BOOL			g_mousedown		= FALSE;
BOOL			g_keytomouse	= FALSE;
NOTIFYICONDATA	nid;
TCHAR			szport[MAX_PATH];

static TCHAR *PORTS[] = 
{
	_T("COM1:"), 
	_T("COM2:"),
	_T("COM3:")
};

// Forward declarations of functions included in this code module:
ATOM				MyRegisterClass		(HINSTANCE, LPTSTR);
BOOL				InitInstance		(HINSTANCE, int);
LRESULT CALLBACK	WndProc				(HWND, UINT, WPARAM, LPARAM);
void				ShowOptions			();
BOOL				OpenCommPort		(LPWSTR, DWORD);
BOOL				CloseCommPort		(void);
DWORD WINAPI		CommReadThreadFunc	(LPVOID);
BOOL CALLBACK		OptionsWndProc		(HWND, UINT, WPARAM, LPARAM);
void				SetKeyToMouse		(BOOL);



int WINAPI WinMain(	HINSTANCE hInstance,
					HINSTANCE hPrevInstance,
					LPTSTR    lpCmdLine,
					int       nCmdShow)
{
	DWORD	written = 0;
	TCHAR	outbuffer[MAX_PATH];
    MSG     msg;
	int		count;
	
	if(_tcslen(lpCmdLine) < 5)
	{
		// default to COM1:
		_stprintf(szport, _T("COM1:"));
	}

	// To detect app is already running or not
	mutex = CreateMutex(NULL, TRUE, TEXT("SerialKey"));
	if (GetLastError() == ERROR_ALREADY_EXISTS)
	{
		_stprintf(outbuffer, _T("SerialKey already running\r\n"));
		WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
		return FALSE;
	}

	dummy = CreateEvent(NULL, FALSE, FALSE, NULL);

	for (count = 0;count < 10;count++) {
		if (IsAPIReady(SH_SHELL))
			break;
		else
			WaitForSingleObject(dummy, 1000);
	}

	if (count == 10) {
		_stprintf(outbuffer, _T("SerialKey can not run without Shell\r\n"));
		WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
		CloseHandle(dummy);
		return FALSE;
	}

	// Perform application initialization:
	if (!InitInstance (hInstance, nCmdShow)) {
		CloseHandle(dummy);
		return FALSE;
	}

	// Create the popup menu.
	hmTrackPopup = CreatePopupMenu();

	AppendMenu(hmTrackPopup, MF_STRING, MNU_EXIT, _T("Exit"));
	AppendMenu(hmTrackPopup, MF_STRING | MF_SEPARATOR, MNU_DIV, _T(""));
	AppendMenu(hmTrackPopup, MF_STRING, MNU_OPTIONS, _T("Options"));

	if(!OpenCommPort(szport, CBR_38400)) 
	{
		ShowOptions();
	}

	// output a user menu
	_stprintf(outbuffer, _T("\r\nADS Serial KeyBoard Emulation Program\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
	_stprintf(outbuffer, _T("-------------------------------------\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
	_stprintf(outbuffer, _T("F1 : Simulate START button\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
	_stprintf(outbuffer, _T("F2 : Simulate ALT key\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
	_stprintf(outbuffer, _T("F3 : Display Task List\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
	_stprintf(outbuffer, _T("F4 : Toggle cursor-to-mouse mode (enter = click)\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);

	while ( GetMessage(&msg, NULL, 0, 0) != FALSE ) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	_stprintf(outbuffer, _T("\r\nApplication Stopped\r\n"));
	WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);

	Shell_NotifyIcon(NIM_DELETE, &nid);
	CloseHandle(mutex);
	CloseHandle(dummy);
	RETAILMSG(1, (TEXT("SerialKey Terminated.\r\n")));

	// close the communications port
	CloseCommPort();

	DestroyMenu(hmTrackPopup);

    return(msg.wParam);
}

BOOL OpenCommPort(LPWSTR port, DWORD speed)
{
	COMMTIMEOUTS ct;
	DCB dcb;
	HANDLE hCommReadThread;

	hCommPort = CreateFile (port, GENERIC_READ | GENERIC_WRITE,
			0,             // COM port cannot be shared
            NULL,          // Always NULL for Windows CE
            OPEN_EXISTING, // For communication resource
            0,             // Non-overlapped operation only
            NULL);         // Always NULL for Windows CE
	if(hCommPort == INVALID_HANDLE_VALUE) {
		RETAILMSG(1, (TEXT("Error Opening Comms Port.\r\n")));
		return FALSE;
	}

	ct.ReadIntervalTimeout = MAXDWORD; 
    ct.ReadTotalTimeoutMultiplier = MAXDWORD; 
    ct.ReadTotalTimeoutConstant = 500; 
    ct.WriteTotalTimeoutMultiplier = MAXDWORD; 
    ct.WriteTotalTimeoutConstant = 0; 

	if(!SetCommTimeouts(hCommPort, &ct)) {
		RETAILMSG(1, (TEXT("Error Setting comm. timeouts.\r\n")));
		CloseCommPort();
		return FALSE;
	}

	dcb.DCBlength = sizeof(DCB);
	if(!GetCommState(hCommPort, &dcb)) {
		RETAILMSG(1, (TEXT("Error Getting Comms. State.\r\n")));
		CloseCommPort();
		return FALSE;
	}

	dcb.BaudRate		= speed;		// set baud rate 
	dcb.fOutxCtsFlow	= FALSE;
	dcb.fRtsControl     = RTS_CONTROL_DISABLE;
	dcb.fDtrControl     = DTR_CONTROL_DISABLE;
	dcb.fOutxDsrFlow    = FALSE;
	dcb.fOutX           = FALSE; // no XON/XOFF control
	dcb.fInX            = FALSE;
	dcb.ByteSize        = 8;
	dcb.Parity          = NOPARITY;
	dcb.StopBits        = ONESTOPBIT;

	if(!SetCommState(hCommPort, &dcb)) {
		RETAILMSG(1, (TEXT("Error Setting Comms. State.\r\n")));
		CloseCommPort();
		return FALSE;
	}

	hCommReadThread = CreateThread(NULL, 0, CommReadThreadFunc, NULL, 0, NULL);

	if(hCommReadThread == INVALID_HANDLE_VALUE)	{
		RETAILMSG(1, (TEXT("Error Creating Thread.\r\n")));
		CloseCommPort();
		return FALSE;
	}
	else {
		CloseHandle(hCommReadThread);
	}
	return TRUE;
}

BOOL CloseCommPort(void)
{
	if(hCommPort != INVALID_HANDLE_VALUE) {
		CloseHandle(hCommPort);
		hCommPort = INVALID_HANDLE_VALUE;
	}
	return TRUE;
}

DWORD WINAPI CommReadThreadFunc(LPVOID lpParam)
{
	enum { FIRST, SECOND, THIRD } state;
	char	ch;
	DWORD	dwBytesRead;
	DWORD	fdwCommMask;
	TCHAR	data[10];

	state = FIRST;
	SetCommMask (hCommPort, EV_RXCHAR);
	while(hCommPort != INVALID_HANDLE_VALUE) 
	{
		WaitCommEvent (hCommPort, &fdwCommMask, 0);
		SetCommMask (hCommPort, EV_RXCHAR);

		do {
			SetLastError(ERROR_TIMEOUT);
			if (!ReadFile(hCommPort, &ch, 1, &dwBytesRead, NULL)) {
				if (GetLastError() != ERROR_INVALID_HANDLE)
					RETAILMSG(1, (TEXT("Reading comms port.\r\n")));
				ExitThread(0);
			}
			else if(dwBytesRead > 0) {
				if(g_more)
				{
					// a ListBox holds a max of 32768 items
					if(SendMessage(hWndData, LB_GETCOUNT, 0, 0) > 32760)
						SendMessage(hWndData, LB_RESETCONTENT, 0, 0);

					// if the Data box is shown on the UI, show the data
					_stprintf(data, _T("0x%02x"), ch);
					SendMessage(hWndData, LB_INSERTSTRING, 0, (LPARAM)data);
				}

				switch (state) {
				case	FIRST:
					if (ch == VK_ESCAPE)
						state = SECOND;
					else {
						switch (ch) {
						case VK_RETURN:
							// Process Enter
							if(g_keytomouse)
							{
								mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
								mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
								g_mousedown = FALSE;
							}
							else
							{
								keybd_event(VK_RETURN, 0, 0, 0);
							}
							break;
						case 0x0a:
							// CTRL-ENTER - this begins/ends drag and drop
							if(g_keytomouse)
							{
								g_mousedown = !g_mousedown;
								if(g_mousedown)
								{
									mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
								}
								else
								{
									mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
								}
							}
							break;
						case VK_BACK:
							// Process BackSpace
							keybd_event(VK_BACK, 0, 0, 0);
							break;
						case VK_TAB:
							// Process Tab
							keybd_event(VK_TAB, 0, 0, 0);
							break;
						case VK_SPACE:
							keybd_event(VK_SPACE, 0, 0, 0);
							break;
						case ',':
							keybd_event(VK_COMMA, 0, 0, 0);
							break;
						case '.': 
							keybd_event(VK_DECIMAL, 0, 0, 0);
							break;
						case '/': 
							keybd_event(VK_SLASH, 0, 0, 0);
							break;
						case ';': 
							keybd_event(VK_SEMICOLON, 0, 0, 0);
							break;
						case ':': 
							keybd_event(VK_SHIFT, 0, 0, 0);
							keybd_event(VK_SEMICOLON, 0, 0, 0);
							keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
							break;
						case '-': 
							keybd_event(VK_SUBTRACT, 0, 0, 0);
							break;
						default:
							if((ch >= 65) && (ch <= 90)) // cap A - Z
							{
								keybd_event(VK_SHIFT, 0, 0, 0);
								keybd_event(ch, 0, 0, 0);
								keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
							}
							else if((ch >= 97) && (ch <= 122)) // a - z
							{
								keybd_event((ch - 32), 0, 0, 0);
							}
							else if((ch >= 48) && (ch <= 57)) // 0 - 9
							{
								keybd_event(ch, 0, 0, 0);
							}
							else
							{
								RETAILMSG(1, (TEXT("unmapped FIRST key 0x%x\r\n"), ch));
							}
						break;
						}
					}
					break;
				case	SECOND:
					if ((ch == VK_LWIN) || (ch == 0x4F))
						state = THIRD;
					else
						state = FIRST;
					break;
				case	THIRD:
					if (ch == 0x41)
					{
						if(g_keytomouse)
						{
							mouse_event(MOUSEEVENTF_MOVE, 0, g_precision * -1, 0, 0);
						}
						else
						{
							keybd_event(VK_UP, 0, 0, 0);
						}
					}
					else if (ch == 0x42)
					{
						if(g_keytomouse)
						{
							mouse_event(MOUSEEVENTF_MOVE, 0, g_precision, 0, 0);
						}
						else
						{
							keybd_event(VK_DOWN, 0, 0, 0);
						}
					}
					else if (ch == 0x43) 
					{
						if(g_keytomouse)
						{
							mouse_event(MOUSEEVENTF_MOVE, g_precision, 0, 0, 0);
						}
						else
						{
							keybd_event(VK_RIGHT, 0, 0, 0);
						}
					}
					else if (ch == 0x44)
					{
						if(g_keytomouse)
						{
							mouse_event(MOUSEEVENTF_MOVE, g_precision * -1, 0, 0, 0);
						}
						else
						{
							keybd_event(VK_LEFT, 0, 0, 0);
						}
					}
					else if (ch == 0x50) 
					{
						// F1
						keybd_event(VK_LWIN, 0, 0, 0);
						// To give delay.
						WaitForSingleObject(dummy, 100);
						RETAILMSG(1, (TEXT("WIN Key Pressed\r\n")));
						keybd_event(VK_LWIN, 0, KEYEVENTF_KEYUP, 0);
					}
					else if (ch == 0x51) 
					{
						// F2
						keybd_event(VK_LMENU, 0, 0, 0);
						RETAILMSG(1, (TEXT("Menu Key Pressed\r\n")));
						WaitForSingleObject(dummy, 100);
						keybd_event(VK_LMENU, 0, KEYEVENTF_KEYUP, 0);
					}
					else if (ch == 0x52) 
					{
						// F3
						keybd_event(VK_LMENU, 0, 0, 0);
						RETAILMSG(1, (TEXT("Task List Key Pressed\r\n")));
						WaitForSingleObject(dummy, 100);
						keybd_event(VK_TAB, 0, 0, 0);
						WaitForSingleObject(dummy, 100);
						keybd_event(VK_LMENU, 0, KEYEVENTF_KEYUP, 0);
					}
					else if (ch == 0x53) 
					{	//F4
						g_keytomouse = !g_keytomouse;
						g_mousedown = FALSE;
						SetKeyToMouse(g_keytomouse);
					}
					else
					{
						RETAILMSG(1, (TEXT("unmapped THIRD key %i\r\n"), ch));
					}

					state = FIRST;
					break;
				}
			}
			else if ((dwBytesRead == 0) && (GetLastError() == ERROR_TIMEOUT) && (ch == 0x1b)) {
				// Process ESC
				RETAILMSG(1, (TEXT("Processing ESC\r\n")));
				keybd_event(VK_ESCAPE, 0, 0, 0);
				state = FIRST;
			}

		} while(dwBytesRead == 1);
	}

	return 0;
}



//
//  FUNCTION: MyRegisterClass()
//
//  PURPOSE: Registers the window class.
//
//  COMMENTS:
//
//    It is important to call this function so that the application 
//    will get 'well formed' small icons associated with it.
//
ATOM MyRegisterClass(HINSTANCE hInstance, LPTSTR szWindowClass)
{
	WNDCLASS	wc;

    wc.style			= CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc		= (WNDPROC) WndProc;
    wc.cbClsExtra		= 0;
    wc.cbWndExtra		= 0;
    wc.hInstance		= hInstance;
    wc.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_SERIALKEY));
    wc.hCursor			= 0;
    wc.hbrBackground	= (HBRUSH) GetStockObject(WHITE_BRUSH);
    wc.lpszMenuName		= 0;
    wc.lpszClassName	= szWindowClass;

	return RegisterClass(&wc);
}

//
//  FUNCTION: InitInstance(HANDLE, int)
//
//  PURPOSE: Saves instance handle and creates main window
//
//  COMMENTS:
//
//    In this function, we save the instance handle in a global variable and
//    create and display the main program window.
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
	HICON	hTaskIcon;
	TCHAR	szTitle[MAX_LOADSTRING];			// The title bar text
	TCHAR	szWindowClass[MAX_LOADSTRING];		// The window class name

	hInst = hInstance;		// Store instance handle in our global variable
	// Initialize global strings
	MyRegisterClass(hInstance, szWindowClass);

	hWnd = CreateWindow(szWindowClass, szTitle, 0,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL);

	if (!hWnd) return FALSE;

	// Notify Shell

	hTaskIcon = (HICON)LoadImage(hInstance, MAKEINTRESOURCE(IDI_SERIALKEY), IMAGE_ICON, 16,16,0);
	if (hTaskIcon == NULL) {
		RETAILMSG(1, (TEXT("Run without Taskbar Icon\r\n")));
	}
	else {
		nid.cbSize = sizeof(NOTIFYICONDATA);
		nid.hWnd = hWnd;
		nid.uID = 1;
		nid.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
		nid.uCallbackMessage = WM_USER+20;
		nid.hIcon = hTaskIcon;
		Shell_NotifyIcon(NIM_ADD, &nid);
	}

	return TRUE;
}

//
//  FUNCTION: WndProc(HWND, unsigned, WORD, LONG)
//
//  PURPOSE:  Processes messages for the main window.
//
//  WM_COMMAND	- process the application menu
//  WM_PAINT	- Paint the main window
//  WM_DESTROY	- post a quit message and return
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
		case WM_CREATE:
			break;
		case WM_DESTROY:
			Shell_NotifyIcon(NIM_DELETE, &nid);
			CloseHandle(mutex);
			CloseHandle(dummy);
			RETAILMSG(1, (TEXT("SerialKey Terminated.\r\n")));
			PostQuitMessage(0);
			break;
		case WM_USER+20:
			POINT cursorpos;
			GetCursorPos(&cursorpos);

			TrackPopupMenuEx(hmTrackPopup, TPM_RIGHTALIGN | TPM_BOTTOMALIGN, cursorpos.x, cursorpos.y, hWnd, NULL);
			break;
		case WM_COMMAND:
			if(wParam == MNU_EXIT)
			{
				PostQuitMessage(0);
			}
			else
			{
				ShowOptions();
			}
			break;
		case WM_VSCROLL:
			RETAILMSG(1, (TEXT("Scroll...\r\n")));
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

void SetKeyToMouse(BOOL enabled)
{
	DWORD	written = 0;
	TCHAR	outbuffer[MAX_PATH];

	if(enabled)
	{
		_stprintf(outbuffer, _T("Key-To-Mouse mapping on \r"));
		WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
		RETAILMSG(1, (TEXT("Key-To-Mouse mapping on \r\n")));
		SendMessage(hWndMouse, BM_SETCHECK, 1, 0);
	}
	else
	{
		_stprintf(outbuffer, _T("Key-To-Mouse mapping off\r"));
		WriteFile(hCommPort, outbuffer, _tcslen(outbuffer) * sizeof(TCHAR), &written, 0);
		RETAILMSG(1, (TEXT("Key-To-Mouse mapping off\r\n")));
		SendMessage(hWndMouse, BM_SETCHECK, 0, 0);
	}
}

void ShowOptions()
{
	// create the dialog
	if(hWndOptions == NULL)
	{
		TCHAR	*port = NULL;
		RECT rc, rc2;
		InitCommonControls();

		hWndOptions = CreateDialog(hInst, MAKEINTRESOURCE(IDD_OPTIONS), NULL, OptionsWndProc);

		if(hWndOptions == NULL)
		{
			long e;
			e = GetLastError();
			DEBUGMSG(TRUE, (_T("%i\r\n"), e));
			return;
		}

		// store handles that we'll be updating
		hWndPrecision	= GetDlgItem(hWndOptions, IDC_PRECISION);
		hWndData		= GetDlgItem(hWndOptions, IDC_DATA);
		hWndMouse		= GetDlgItem(hWndOptions, IDC_MOUSE);
		hWndPort		= GetDlgItem(hWndOptions, IDC_PORT);
		hWndMore		= GetDlgItem(hWndOptions, IDC_MORE);

		// determine size for expansion/shrink on More
		GetWindowRect(hWndOptions, &rc);
		GetWindowRect(hWndData, &rc2);
		g_height = rc.bottom - rc.top;
		g_expwidth = rc.right - rc.left;
		g_width = g_expwidth - (rc2.right - rc2.left) - 10;
		SetWindowPos(hWndOptions, NULL, 0, 0, g_width, g_height, SWP_NOMOVE | SWP_NOZORDER);
		
		// set the range for the mouse precision slider
		SendMessage(hWndPrecision, TBM_SETRANGE, TRUE, MAKELPARAM(1, 10));
		SendMessage(hWndPrecision, TBM_SETTICFREQ, 0, 2);
		SendMessage(hWndPrecision, TBM_SETLINESIZE, 0, 1);
		SendMessage(hWndPrecision, TBM_SETPAGESIZE, 0, 2);

		SendMessage(hWndPrecision, TBM_SETPOS, TRUE, g_precision);

		// add port strings to dropdown
		port = PORTS[0];
		for(int i = 0 ; i < dim(PORTS) ; i++)
		{
			SendMessage(hWndPort, CB_ADDSTRING, 0, (LPARAM)port);
			port += (_tcslen(port) + 1);
		}
		SendMessage(hWndPort, CB_SELECTSTRING, -1, (LPARAM)szport);
	}

	SendMessage(hWndMouse, BM_SETCHECK, g_keytomouse, 0);

	// show the dialog
	SetActiveWindow(hWndOptions);
	SetWindowPos(hWndOptions, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
	UpdateWindow(hWndOptions);
}

BOOL CALLBACK OptionsWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
	static NMHDR nm;
	static TCHAR x[255], x2[255];

	switch(wMsg)
	{
		case WM_CLOSE:
			DEBUGMSG(TRUE, (_T("WM_CLOSE: %i\r\n"), hWnd));
		break;
		case WM_COMMAND:
			switch(wParam)
			{
				case IDOK:
					ShowWindow(hWndOptions, SW_HIDE);
					break;
				case IDC_MOUSE:
					if(SendMessage(hWndMouse, BM_GETCHECK, 0, 0) == BST_CHECKED)
					{
						g_keytomouse = TRUE;
					}
					else
					{
						g_keytomouse = FALSE;						
					}
					SetKeyToMouse(g_keytomouse);
					break;
				case IDC_MORE:
					g_more = !g_more;
					if(g_more)
					{
						SetWindowPos(hWndOptions, NULL, 0, 0, g_expwidth, g_height, SWP_NOMOVE | SWP_NOZORDER);
						SetWindowText(hWndMore, _T("<< Less"));
					}
					else
					{
						SetWindowPos(hWndOptions, NULL, 0, 0, g_width, g_height, SWP_NOMOVE | SWP_NOZORDER);
						SetWindowText(hWndMore, _T("More >>"));
					}
					break;
				default:
					if(LOWORD(wParam) == IDC_PORT)
					{
						// port dropdown selected
						int i = SendMessage((HWND)lParam, CB_GETCURSEL, 0, 0);
						SendMessage((HWND)lParam, CB_GETLBTEXT, i, (LPARAM)x);
						
						if(_tcsicmp(x, szport))
						{
							// port changed
							CloseCommPort();

							if(!OpenCommPort(x, CBR_38400)) 
							{
								// failed to reopen, so revert to previous port						
								OpenCommPort(szport, CBR_38400);
								
								// revert dropdown
								SendMessage(hWndPort, CB_SELECTSTRING, -1, (LPARAM)szport);
							}
							else
							{
								_tcscpy(szport, x);
							}
						}

					}
					else
					{
						DEBUGMSG(TRUE, (_T("WM_COMMAND: %i\r\n"), hWnd));
					}
					break;
			}
		break;
		case WM_NOTIFY:
			DEBUGMSG(TRUE, (_T("WM_NOTIFY: %i\r\n"), hWnd));

			// handle slider mesages
			memcpy(&nm, (void *)lParam, sizeof(nm));
			if(nm.idFrom == IDC_PRECISION)
			{
				g_precision = SendMessage(hWndPrecision, TBM_GETPOS, 0, 0);
			}

		break;
	}
	
	return FALSE;
}
