#define  _WIN32_WINNT 0x0400
#include <windows.h>

HMODULE hookInstance = NULL;
HHOOK   mouseHook = NULL;
HHOOK   keyboardHook = NULL;
HANDLE  mouseHookThread = NULL;
HANDLE  keyboardHookThread = NULL;

/*
 * DLL entry point for persisting the module handle
 */
BOOL WINAPI DllMain(HINSTANCE module, DWORD reason, LPVOID reserved)
{
	if (reason == DLL_PROCESS_ATTACH)
		hookInstance = module;
	else if (reason == DLL_PROCESS_DETACH)
	{
		TerminateThread(mouseHookThread, 0);
		TerminateThread(keyboardHookThread, 0);

		mouseHookThread    = NULL;
		keyboardHookThread = NULL;
	}

	return TRUE;
}

/*
 * Hook handler to mouse movements that will drop all locally generated
 * mouse movements
 */
LRESULT CALLBACK mouse_hook_handler(int code,
		WPARAM w, LPARAM l)
{
	if (code == HC_ACTION)
	{
		MSLLHOOKSTRUCT *hook = (MSLLHOOKSTRUCT *)l;

		// Return the as if the mouse event had been passed if it's
		// generated by the mouse
		if (!(hook->flags & LLMHF_INJECTED))
			return TRUE;
	}

	return CallNextHookEx(mouseHook, code, w, l);
}

/*
 * Handles mouse hook notifications
 */
DWORD mouse_hook_msg_thread(LPVOID na)
{
	DWORD result = ERROR_SUCCESS;
	MSG msg;

	if (!(mouseHook = SetWindowsHookEx(WH_MOUSE_LL,
			mouse_hook_handler, hookInstance, 0)))
		result = GetLastError();

	while (GetMessage(&msg, 0, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return result;
}

/*
 * Hook handler to keyboard events that will drop all locally generated
 * keyboard events
 */
LRESULT CALLBACK keyboard_hook_handler(int code,
		WPARAM w, LPARAM l)
{
	if (code == HC_ACTION)
	{
		KBDLLHOOKSTRUCT *hook = (KBDLLHOOKSTRUCT *)l;

		// Return the as if the keyboard event had been passed if it's
		// generated by the keyboard
		if (!(hook->flags & LLMHF_INJECTED))
			return TRUE;
	}

	return CallNextHookEx(keyboardHook, code, w, l);
}

/*
 * Handles keyboard hook notifications
 */
DWORD keyboard_hook_msg_thread(LPVOID na)
{
	DWORD result = ERROR_SUCCESS;
	MSG msg;

	if (!(keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL,
			keyboard_hook_handler, hookInstance, 0)))
		result = GetLastError();

	while (GetMessage(&msg, 0, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return result;
}

/*
 * Enable/disable mouse movement
 */
DWORD __declspec(dllexport) enable_mouse_input(BOOL enable)
{
	DWORD result = ERROR_SUCCESS;

	// If the requestor would like to enable a mouse input and it's currently
	// disabled, enable it.
	if ((enable) &&
	    (mouseHook))
	{
		if (!UnhookWindowsHookEx(mouseHook))
			result = GetLastError();
		
		TerminateThread(mouseHookThread, 0);

		mouseHookThread = NULL;
	}
	// Otherwise, if they would like to disable it but it's currently enabled,
	// disable it.
	else if ((!enable) &&
	         (!mouseHook))
	{
		DWORD threadId;

		mouseHookThread = CreateThread(NULL, 0, 
				(LPTHREAD_START_ROUTINE)mouse_hook_msg_thread, NULL,
				0, &threadId);
	}

	return result;
}

/*
 * Enable/disable keyboard movement
 */
DWORD __declspec(dllexport) enable_keyboard_input(BOOL enable)
{
	DWORD result = ERROR_SUCCESS;

	// If the requestor would like to enable a keyboard input and it's currently
	// disabled, enable it.
	if ((enable) &&
	    (keyboardHook))
	{
		if (!UnhookWindowsHookEx(keyboardHook))
			result = GetLastError();

		TerminateThread(keyboardHookThread, 0);

		keyboardHookThread = NULL;
	}
	// Otherwise, if they would like to disable it but it's currently enabled,
	// disable it.
	else if ((!enable) &&
	         (!keyboardHook))
	{
		DWORD threadId;

		keyboardHookThread = CreateThread(NULL, 0, 
				(LPTHREAD_START_ROUTINE)keyboard_hook_msg_thread, NULL,
				0, &threadId);
	}

	return result;
}

