/**
 * $Id$
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/**

 * $Id$
 * Copyright (C) 2001 NaN Technologies B.V.
 * @author	Maarten Gribnau
 * @date	May 7, 2001
 */

#include <iostream>

#include "GHOST_SystemWin32.h"
#include "GHOST_EventDragnDrop.h"

#define WIN32_LEAN_AND_MEAN
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#define _WIN32_IE 0x0501
#include <windows.h>
#include <shlobj.h>

// win64 doesn't define GWL_USERDATA
#ifdef WIN32
#ifndef GWL_USERDATA
#define GWL_USERDATA GWLP_USERDATA
#define GWL_WNDPROC GWLP_WNDPROC
#endif
#endif

/*
 * According to the docs the mouse wheel message is supported from windows 98 
 * upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the 
 * wheel detent value are undefined.
 */
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif // WM_MOUSEWHEEL
#ifndef WHEEL_DELTA
#define WHEEL_DELTA 120	/* Value for rolling one detent, (old convention! MS changed it) */
#endif // WHEEL_DELTA

/* 
 * Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
 * MSDN: Declared in Winuser.h, include Windows.h 
 * This does not seem to work with MinGW so we define our own here.
 */
#ifndef XBUTTON1
#define XBUTTON1 0x0001
#endif // XBUTTON1
#ifndef XBUTTON2
#define XBUTTON2 0x0002
#endif // XBUTTON2
#ifndef WM_XBUTTONUP
#define WM_XBUTTONUP 524
#endif // WM_XBUTTONUP
#ifndef WM_XBUTTONDOWN
#define WM_XBUTTONDOWN 523
#endif // WM_XBUTTONDOWN

#include "GHOST_Debug.h"
#include "GHOST_DisplayManagerWin32.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventCursor.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventWheel.h"
#include "GHOST_EventNDOF.h"
#include "GHOST_TimerTask.h"
#include "GHOST_TimerManager.h"
#include "GHOST_WindowManager.h"
#include "GHOST_WindowWin32.h"
#include "GHOST_NDOFManager.h"

// Key code values not found in winuser.h
#ifndef VK_MINUS
#define VK_MINUS 0xBD
#endif // VK_MINUS
#ifndef VK_SEMICOLON
#define VK_SEMICOLON 0xBA
#endif // VK_SEMICOLON
#ifndef VK_PERIOD
#define VK_PERIOD 0xBE
#endif // VK_PERIOD
#ifndef VK_COMMA
#define VK_COMMA 0xBC
#endif // VK_COMMA
#ifndef VK_QUOTE
#define VK_QUOTE 0xDE
#endif // VK_QUOTE
#ifndef VK_BACK_QUOTE
#define VK_BACK_QUOTE 0xC0
#endif // VK_BACK_QUOTE
#ifndef VK_SLASH
#define VK_SLASH 0xBF
#endif // VK_SLASH
#ifndef VK_BACK_SLASH
#define VK_BACK_SLASH 0xDC
#endif // VK_BACK_SLASH
#ifndef VK_EQUALS
#define VK_EQUALS 0xBB
#endif // VK_EQUALS
#ifndef VK_OPEN_BRACKET
#define VK_OPEN_BRACKET 0xDB
#endif // VK_OPEN_BRACKET
#ifndef VK_CLOSE_BRACKET
#define VK_CLOSE_BRACKET 0xDD
#endif // VK_CLOSE_BRACKET
#ifndef VK_GR_LESS
#define VK_GR_LESS 0xE2
#endif // VK_GR_LESS


GHOST_SystemWin32::GHOST_SystemWin32()
: m_hasPerformanceCounter(false), m_freq(0), m_start(0)
{
	m_displayManager = new GHOST_DisplayManagerWin32 ();
	GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::GHOST_SystemWin32(): m_displayManager==0\n");
	m_displayManager->initialize();
	
	// Check if current keyboard layout uses AltGr
	this->keyboardAltGr();
	// Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
	OleInitialize(0);
}

GHOST_SystemWin32::~GHOST_SystemWin32()
{
	// Shutdown COM
	OleUninitialize();
}


GHOST_TUns64 GHOST_SystemWin32::getMilliSeconds() const
{
	// Hardware does not support high resolution timers. We will use GetTickCount instead then.
	if (!m_hasPerformanceCounter) {
		return ::GetTickCount();
	}

	// Retrieve current count
	__int64 count = 0;
	::QueryPerformanceCounter((LARGE_INTEGER*)&count);

	// Calculate the time passed since system initialization.
	__int64 delta = 1000*(count-m_start);

	GHOST_TUns64 t = (GHOST_TUns64)(delta/m_freq);
	return t; 
}


GHOST_TUns8 GHOST_SystemWin32::getNumDisplays() const
{
	GHOST_ASSERT(m_displayManager, "GHOST_SystemWin32::getNumDisplays(): m_displayManager==0\n");
	GHOST_TUns8 numDisplays;
	m_displayManager->getNumDisplays(numDisplays);
	return numDisplays;
}


void GHOST_SystemWin32::getMainDisplayDimensions(GHOST_TUns32& width, GHOST_TUns32& height) const
{
	width = ::GetSystemMetrics(SM_CXSCREEN);
	height= ::GetSystemMetrics(SM_CYSCREEN);
}


GHOST_IWindow* GHOST_SystemWin32::createWindow(
	const STR_String& title, 
	GHOST_TInt32 left, GHOST_TInt32 top, GHOST_TUns32 width, GHOST_TUns32 height,
	GHOST_TWindowState state, GHOST_TDrawingContextType type,
	bool stereoVisual, const GHOST_TUns16 numOfAASamples, const GHOST_TEmbedderWindowID parentWindow )
{
	GHOST_Window* window = 0;
	window = new GHOST_WindowWin32 (this, title, left, top, width, height, state, type, stereoVisual, numOfAASamples);
	if (window) {
		if (window->getValid()) {
			// Store the pointer to the window
//			if (state != GHOST_kWindowStateFullScreen) {
				m_windowManager->addWindow(window);
//			}
		}
		else {
			// An invalid window could be one that was used to test for AA
			window = ((GHOST_WindowWin32*)window)->getNextWindow();
			
			// If another window is found, let the wm know about that one, but not the old one
			if (window->getValid()) {
				m_windowManager->addWindow(window);
			}
			else {
				delete window;
				window = 0;
			}

		}
	}
	return window;
}


bool GHOST_SystemWin32::processEvents(bool waitForEvent)
{
	MSG msg;
	bool anyProcessed = false;

	do {
		GHOST_TimerManager* timerMgr = getTimerManager();

		if (waitForEvent && !::PeekMessage(&msg, 0, 0, 0, PM_NOREMOVE)) {
#if 1
			::Sleep(1);
#else
			GHOST_TUns64 next = timerMgr->nextFireTime();
			GHOST_TInt64 maxSleep = next - getMilliSeconds();
			
			if (next == GHOST_kFireTimeNever) {
				::WaitMessage();
			} else if(maxSleep >= 0.0) {
				::SetTimer(NULL, 0, maxSleep, NULL);
				::WaitMessage();
				::KillTimer(NULL, 0);
			}
#endif
		}

		if (timerMgr->fireTimers(getMilliSeconds())) {
			anyProcessed = true;
		}

		// Process all the events waiting for us
		while (::PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
			anyProcessed = true;
		}
	} while (waitForEvent && !anyProcessed);

	return anyProcessed;
}


GHOST_TSuccess GHOST_SystemWin32::getCursorPosition(GHOST_TInt32& x, GHOST_TInt32& y) const
{
	POINT point;
	if(::GetCursorPos(&point)){
		x = point.x;
		y = point.y;
		return GHOST_kSuccess;
	}
	return GHOST_kFailure;
}


GHOST_TSuccess GHOST_SystemWin32::setCursorPosition(GHOST_TInt32 x, GHOST_TInt32 y)
{
	if (!GetActiveWindow())
		return GHOST_kFailure;
	return ::SetCursorPos(x, y) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
}


GHOST_TSuccess GHOST_SystemWin32::getModifierKeys(GHOST_ModifierKeys& keys) const
{
	bool down = HIBYTE(::GetKeyState(VK_LSHIFT)) != 0;
	keys.set(GHOST_kModifierKeyLeftShift, down);
	down = HIBYTE(::GetKeyState(VK_RSHIFT)) != 0;
	keys.set(GHOST_kModifierKeyRightShift, down);
	
	down = HIBYTE(::GetKeyState(VK_LMENU)) != 0;
	keys.set(GHOST_kModifierKeyLeftAlt, down);
	down = HIBYTE(::GetKeyState(VK_RMENU)) != 0;
	keys.set(GHOST_kModifierKeyRightAlt, down);
	
	down = HIBYTE(::GetKeyState(VK_LCONTROL)) != 0;
	keys.set(GHOST_kModifierKeyLeftControl, down);
	down = HIBYTE(::GetKeyState(VK_RCONTROL)) != 0;
	keys.set(GHOST_kModifierKeyRightControl, down);
	
	bool lwindown = HIBYTE(::GetKeyState(VK_LWIN)) != 0;
	bool rwindown = HIBYTE(::GetKeyState(VK_RWIN)) != 0;
	if(lwindown || rwindown)
		keys.set(GHOST_kModifierKeyOS, true);
	else
		keys.set(GHOST_kModifierKeyOS, false);
	return GHOST_kSuccess;
}


GHOST_TSuccess GHOST_SystemWin32::getButtons(GHOST_Buttons& buttons) const
{
	/* Check for swapped buttons (left-handed mouse buttons)
	 * GetAsyncKeyState() will give back the state of the physical mouse buttons.
	 */
	bool swapped = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE;

	bool down = HIBYTE(::GetKeyState(VK_LBUTTON)) != 0;
	buttons.set(swapped ? GHOST_kButtonMaskRight : GHOST_kButtonMaskLeft, down);

	down = HIBYTE(::GetKeyState(VK_MBUTTON)) != 0;
	buttons.set(GHOST_kButtonMaskMiddle, down);

	down = HIBYTE(::GetKeyState(VK_RBUTTON)) != 0;
	buttons.set(swapped ? GHOST_kButtonMaskLeft : GHOST_kButtonMaskRight, down);
	return GHOST_kSuccess;
}


GHOST_TSuccess GHOST_SystemWin32::init()
{
	GHOST_TSuccess success = GHOST_System::init();
	
	for(int i = 0; i < 255; i++) {
		m_prevKeyStatus[i] = false;
		m_curKeyStatus[i] = false;
	}

	/* Disable scaling on high DPI displays on Vista */
	HMODULE user32 = ::LoadLibraryA("user32.dll");
	typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
	LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
		(LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
	if (SetProcessDPIAware)
		SetProcessDPIAware();
	FreeLibrary(user32);

	// Determine whether this system has a high frequency performance counter. */
	m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
	if (m_hasPerformanceCounter) {
		GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer available\n")
		::QueryPerformanceCounter((LARGE_INTEGER*)&m_start);
	}
	else {
		GHOST_PRINT("GHOST_SystemWin32::init: High Frequency Performance Timer not available\n")
	}

	if (success) {
		WNDCLASS wc;
		wc.style= CS_HREDRAW | CS_VREDRAW;
		wc.lpfnWndProc= s_wndProc;
		wc.cbClsExtra= 0;
		wc.cbWndExtra= 0;
		wc.hInstance= ::GetModuleHandle(0);
		wc.hIcon = ::LoadIcon(wc.hInstance, "APPICON");
		
		if (!wc.hIcon) {
			::LoadIcon(NULL, IDI_APPLICATION);
		}
		wc.hCursor = ::LoadCursor(0, IDC_ARROW);
		wc.hbrBackground= (HBRUSH)::GetStockObject(BLACK_BRUSH);
		wc.lpszMenuName = 0;
		wc.lpszClassName= GHOST_WindowWin32::getWindowClassName();

		// Use RegisterClassEx for setting small icon
		if (::RegisterClass(&wc) == 0) {
			success = GHOST_kFailure;
		}
		
		// Add low-level keyboard hook for our process.
		m_llKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, s_llKeyboardProc, wc.hInstance, 0);
	}
	
	return success;
}


GHOST_TSuccess GHOST_SystemWin32::exit()
{
	// remove our low-level keyboard hook.
	UnhookWindowsHookEx(m_llKeyboardHook);
	
	return GHOST_System::exit();
}

void GHOST_SystemWin32::triggerKey(GHOST_IWindow *window, bool down, GHOST_TKey key)
{
	GHOST_Event *extra = new GHOST_EventKey(getSystem()->getMilliSeconds(), down ? GHOST_kEventKeyDown : GHOST_kEventKeyUp, window, key, '\0');
	((GHOST_SystemWin32*)getSystem())->pushEvent(extra);
}
void GHOST_SystemWin32::handleModifierKeys(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, GHOST_ModifierKeys &oldModifiers, GHOST_ModifierKeys &newModifiers) const
{
	switch(wParam) {
		case VK_SHIFT:
			{
				bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
				if(lchanged) {
					((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftAlt), GHOST_kKeyLeftAlt);
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt);
					if (rchanged) {
						((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightAlt), GHOST_kKeyRightAlt);
					}
				}
				lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
				if(lchanged) {
					((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftControl), GHOST_kKeyLeftControl);
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl);
					if (rchanged) {
						((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightControl), GHOST_kKeyRightControl);
					}
				}
			}
			break;
		case VK_CONTROL:
			{
				bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
				if(lchanged) {
					((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftAlt), GHOST_kKeyLeftAlt);
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt);
					if (rchanged) {
						((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightAlt), GHOST_kKeyRightAlt);
					}
				}
				lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
				if(lchanged) {
					((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftShift), GHOST_kKeyLeftShift);
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift);
					if (rchanged) {
						((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightShift), GHOST_kKeyRightShift);
					}
				}
			}
			break;
		case VK_MENU:
			{
				bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
				if(lchanged) {
					((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftShift), GHOST_kKeyLeftShift);
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift);
					if (rchanged) {
						((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightShift), GHOST_kKeyRightShift);
					}
				}
				lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
				if(lchanged) {
					((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyLeftControl), GHOST_kKeyLeftControl);
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl);
					if (rchanged) {
						((GHOST_SystemWin32*)getSystem())->triggerKey(window, newModifiers.get(GHOST_kModifierKeyRightControl), GHOST_kKeyRightControl);
					}
				}
			}
			break;
		default:
			break;
	}
}

GHOST_TKey GHOST_SystemWin32::convertKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam) const
{
	GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
	bool isExtended = (lParam&(1<<24))?true:false;
	
	GHOST_TKey key;
	GHOST_ModifierKeys oldModifiers, newModifiers;
	system->retrieveModifierKeys(oldModifiers);
	system->getModifierKeys(newModifiers);

	// check if modifier keys different from this event have changed and trigger those
	// This can happen when some action takes a long time (Blender not responding), resulting
	// in dropped events.
	system->handleModifierKeys(window, wParam, lParam, oldModifiers, newModifiers);
	
	//std::cout << wParam << " " << system->m_curKeyStatus[wParam] << " shift pressed: " << system->shiftPressed() << std::endl;

	if ((wParam >= '0') && (wParam <= '9')) {
		// VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 - 0x39)
		key = (GHOST_TKey)(wParam - '0' + GHOST_kKey0);
	}
	else if ((wParam >= 'A') && (wParam <= 'Z')) {
		// VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 - 0x5A)
		key = (GHOST_TKey)(wParam - 'A' + GHOST_kKeyA);
	}
	else if ((wParam >= VK_F1) && (wParam <= VK_F24)) {
		key = (GHOST_TKey)(wParam - VK_F1 + GHOST_kKeyF1);
	}
	else {
		switch (wParam) {
		case VK_RETURN:
			key = isExtended?GHOST_kKeyNumpadEnter:GHOST_kKeyEnter;
			break;

		case VK_BACK:     key = GHOST_kKeyBackSpace;		break;
		case VK_TAB:      key = GHOST_kKeyTab;				break;
		case VK_ESCAPE:   key = GHOST_kKeyEsc;				break;
		case VK_SPACE:    key = GHOST_kKeySpace;			break;
		case VK_PRIOR:    key = GHOST_kKeyUpPage;			break;
		case VK_NEXT:     key = GHOST_kKeyDownPage;			break;
		case VK_END:      key = GHOST_kKeyEnd;				break;
		case VK_HOME:
			{
				if(system->m_curKeyStatus[VK_NUMPAD7] && system->shiftPressed())
					key = GHOST_kKeyNumpad7;
				else
					key = GHOST_kKeyHome;
			}
			break;
		case VK_INSERT:   key = GHOST_kKeyInsert;			break;
		case VK_DELETE:   key = GHOST_kKeyDelete;			break;
		case VK_LEFT:     key = GHOST_kKeyLeftArrow;		break;
		case VK_RIGHT:    key = GHOST_kKeyRightArrow;		break;
		case VK_UP:       key = GHOST_kKeyUpArrow;			break;
		case VK_DOWN:     key = GHOST_kKeyDownArrow;		break;
		case VK_NUMPAD0:  key = GHOST_kKeyNumpad0;			break;
		case VK_NUMPAD1:  key = GHOST_kKeyNumpad1;			break;
		case VK_NUMPAD2:  key = GHOST_kKeyNumpad2;			break;
		case VK_NUMPAD3:  key = GHOST_kKeyNumpad3;			break;
		case VK_NUMPAD4:  key = GHOST_kKeyNumpad4;			break;
		case VK_NUMPAD5:  key = GHOST_kKeyNumpad5;			break;
		case VK_NUMPAD6:  key = GHOST_kKeyNumpad6;			break;
		case VK_NUMPAD7:  key = GHOST_kKeyNumpad7;			break;
		case VK_NUMPAD8:  key = GHOST_kKeyNumpad8;			break;
		case VK_NUMPAD9:  key = GHOST_kKeyNumpad9;			break;
		case VK_SNAPSHOT: key = GHOST_kKeyPrintScreen;		break;
		case VK_PAUSE:    key = GHOST_kKeyPause;			break;
		case VK_MULTIPLY: key = GHOST_kKeyNumpadAsterisk;	 break;
		case VK_SUBTRACT: key = GHOST_kKeyNumpadMinus;		break;
		case VK_DECIMAL:  key = GHOST_kKeyNumpadPeriod;		break;
		case VK_DIVIDE:   key = GHOST_kKeyNumpadSlash;		break;
		case VK_ADD:      key = GHOST_kKeyNumpadPlus;		break;

		case VK_SEMICOLON:		key = GHOST_kKeySemicolon;		break;
		case VK_EQUALS:			key = GHOST_kKeyEqual;			break;
		case VK_COMMA:			key = GHOST_kKeyComma;			break;
		case VK_MINUS:			key = GHOST_kKeyMinus;			break;
		case VK_PERIOD:			key = GHOST_kKeyPeriod;			break;
		case VK_SLASH:			key = GHOST_kKeySlash;			break;
		case VK_BACK_QUOTE:		key = GHOST_kKeyAccentGrave;	break;
		case VK_OPEN_BRACKET:	key = GHOST_kKeyLeftBracket;	break;
		case VK_BACK_SLASH:		key = GHOST_kKeyBackslash;		break;
		case VK_CLOSE_BRACKET:	key = GHOST_kKeyRightBracket;	break;
		case VK_QUOTE:			key = GHOST_kKeyQuote;			break;
		case VK_GR_LESS:		key = GHOST_kKeyGrLess;			break;

		case VK_SHIFT:
			{
				bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftShift) != newModifiers.get(GHOST_kModifierKeyLeftShift);
				if(lchanged) {
					key = GHOST_kKeyLeftShift;
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightShift) != newModifiers.get(GHOST_kModifierKeyRightShift);
					if(rchanged) {
						key = GHOST_kKeyRightShift;
					} else {
						key = GHOST_kKeyUnknown;
					}
				}
			}
			break;
		case VK_CONTROL:
			{
				bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftControl) != newModifiers.get(GHOST_kModifierKeyLeftControl);
				if(lchanged) {
					key = GHOST_kKeyLeftControl;
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightControl) != newModifiers.get(GHOST_kModifierKeyRightControl);
					if(rchanged) {
						key = GHOST_kKeyRightControl;
					} else {
						key = GHOST_kKeyUnknown;
					}
				}
			}
			break;
		case VK_MENU:
			{
				if(m_hasAltGr && isExtended) {
					// We have here an extended RAlt, which is AltGr. The keyboard driver on Windows sends before this a LControl, so
					// to be able to input characters created with AltGr (normal on German, French, Finnish and other keyboards) we
					// push an extra LControl up event. This ensures we don't have a 'hanging' ctrl event in Blender windowmanager
					// when typing in Text editor or Console.
					GHOST_Event *extra = new GHOST_EventKey(getSystem()->getMilliSeconds(), GHOST_kEventKeyUp, window, GHOST_kKeyLeftControl, '\0');
					((GHOST_SystemWin32*)getSystem())->pushEvent(extra);
					newModifiers.set(GHOST_kModifierKeyRightControl, false);
					newModifiers.set(GHOST_kModifierKeyLeftControl, false);
				}
				bool lchanged = oldModifiers.get(GHOST_kModifierKeyLeftAlt) != newModifiers.get(GHOST_kModifierKeyLeftAlt);
				if(lchanged) {
					key = GHOST_kKeyLeftAlt;
				} else {
					bool rchanged = oldModifiers.get(GHOST_kModifierKeyRightAlt) != newModifiers.get(GHOST_kModifierKeyRightAlt);
					if(rchanged) {
						key = GHOST_kKeyRightAlt;
					} else {
						key = GHOST_kKeyUnknown;
					}
				}
			}
			break;
		case VK_LWIN:
		case VK_RWIN:
			key = GHOST_kKeyOS;
			break;
		case VK_NUMLOCK: key = GHOST_kKeyNumLock; break;
		case VK_SCROLL: key = GHOST_kKeyScrollLock; break;
		case VK_CAPITAL: key = GHOST_kKeyCapsLock; break;
		default:
			key = GHOST_kKeyUnknown;
			break;
		}
	}
	((GHOST_SystemWin32*)getSystem())->storeModifierKeys(newModifiers);
	return key;
}

GHOST_EventButton* GHOST_SystemWin32::processButtonEvent(GHOST_TEventType type, GHOST_IWindow *window, GHOST_TButtonMask mask)
{
	return new GHOST_EventButton (getSystem()->getMilliSeconds(), type, window, mask);
}


GHOST_EventCursor* GHOST_SystemWin32::processCursorEvent(GHOST_TEventType type, GHOST_IWindow *Iwindow)
{
	GHOST_TInt32 x_screen, y_screen;
	GHOST_SystemWin32 * system = ((GHOST_SystemWin32 * ) getSystem());
	GHOST_WindowWin32 * window = ( GHOST_WindowWin32 * ) Iwindow;
	
	system->getCursorPosition(x_screen, y_screen);

	if(window->getCursorGrabMode() != GHOST_kGrabDisable && window->getCursorGrabMode() != GHOST_kGrabNormal)
	{
		GHOST_TInt32 x_new= x_screen;
		GHOST_TInt32 y_new= y_screen;
		GHOST_TInt32 x_accum, y_accum;
		GHOST_Rect bounds;

		/* fallback to window bounds */
		if(window->getCursorGrabBounds(bounds)==GHOST_kFailure){
			window->getClientBounds(bounds);
		}

		/* could also clamp to screen bounds
		 * wrap with a window outside the view will fail atm  */

		bounds.wrapPoint(x_new, y_new, 2); /* offset of one incase blender is at screen bounds */

		window->getCursorGrabAccum(x_accum, y_accum);
		if(x_new != x_screen|| y_new != y_screen) {
			/* when wrapping we don't need to add an event because the
			 * setCursorPosition call will cause a new event after */
			system->setCursorPosition(x_new, y_new); /* wrap */
			window->setCursorGrabAccum(x_accum + (x_screen - x_new), y_accum + (y_screen - y_new));
		}else{
			return new GHOST_EventCursor(system->getMilliSeconds(),
										 GHOST_kEventCursorMove,
										 window,
										 x_screen + x_accum,
										 y_screen + y_accum
			);
		}

	}
	else {
		return new GHOST_EventCursor(system->getMilliSeconds(),
									 GHOST_kEventCursorMove,
									 window,
									 x_screen,
									 y_screen
		);
	}
	return NULL;
}


GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
{
	// short fwKeys = LOWORD(wParam);			// key flags
	int zDelta = (short) HIWORD(wParam);	// wheel rotation
	
	// zDelta /= WHEEL_DELTA;
	// temporary fix below: microsoft now has added more precision, making the above division not work
	if (zDelta <= 0 ) zDelta= -1; else zDelta= 1;	
	
	// short xPos = (short) LOWORD(lParam);	// horizontal position of pointer
	// short yPos = (short) HIWORD(lParam);	// vertical position of pointer
	return new GHOST_EventWheel (getSystem()->getMilliSeconds(), window, zDelta);
}


GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, bool keyDown, WPARAM wParam, LPARAM lParam)
{
	GHOST_TKey key = ((GHOST_SystemWin32*)getSystem())->convertKey(window, wParam, lParam);
	GHOST_EventKey* event;
	if (key != GHOST_kKeyUnknown) {
		MSG keyMsg;
		char ascii = '\0';

			/* Eat any character related messages */
		if (::PeekMessage(&keyMsg, NULL, WM_CHAR, WM_SYSDEADCHAR, PM_REMOVE)) {
			ascii = (char) keyMsg.wParam;
			
		}

		event = new GHOST_EventKey(getSystem()->getMilliSeconds(), keyDown ? GHOST_kEventKeyDown: GHOST_kEventKeyUp, window, key, ascii);
	}
	else {
		event = 0;
	}
	return event;
}


GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
{
	return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
}

GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType, 
													GHOST_TDragnDropTypes draggedObjectType,
													GHOST_IWindow* window,
													int mouseX, int mouseY,
													void* data)
{
	GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
	return system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(),
													  eventType,
													  draggedObjectType,
													  window,mouseX,mouseY,data)
			);
}

void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
{
	minmax->ptMinTrackSize.x=320;
	minmax->ptMinTrackSize.y=240;
}

/* Note that this function gets *all* key events from the entire system (all
 * threads running in this desktop session. So when getting event here, don't assume
 * it's for Blender. Thus we only do status bookkeeping, so we can check
 * in s_wndProc and processKeyEvent what the real keyboard status is.
 * This is needed for proper handling of shift+numpad keys for instance.
 */
LRESULT CALLBACK GHOST_SystemWin32::s_llKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
	GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
	
	bool down = false;
	if(wParam==WM_KEYDOWN || wParam==WM_SYSKEYDOWN ){
		down = true;
	}
	
	if(nCode!=HC_ACTION)
		return CallNextHookEx(system->m_llKeyboardHook, nCode, wParam, lParam);
		
	KBDLLHOOKSTRUCT &keyb = *(PKBDLLHOOKSTRUCT)(lParam);
	system->m_prevKeyStatus[keyb.vkCode] = system->m_curKeyStatus[keyb.vkCode];
	//std::cout << "ll: " << keyb.vkCode << " " << down << " ";
	if(keyb.flags) {
		if((keyb.flags & LLKHF_EXTENDED) == LLKHF_EXTENDED) {
			//std::cout << "extended ";
		}
		if((keyb.flags & LLKHF_ALTDOWN) == LLKHF_ALTDOWN) {
		}
		if((keyb.flags & LLKHF_INJECTED)== LLKHF_INJECTED) {
			//std::cout << "injected ";
		}
		if((keyb.flags & LLKHF_UP) == LLKHF_UP) {
			system->m_curKeyStatus[keyb.vkCode] = false;
			//std::cout << "up" << std::endl;
		} else {
			system->m_curKeyStatus[keyb.vkCode] = true;
			//std::cout << "down" << std::endl;
		}
	}
	else {
		system->m_curKeyStatus[keyb.vkCode] = true;
		//std::cout << "down" << std::endl;
	}
	
	return CallNextHookEx(system->m_llKeyboardHook, nCode, wParam, lParam);
}

LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	GHOST_Event* event = 0;
	LRESULT lResult = 0;
	GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
	GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")

	if (hwnd) {
		GHOST_WindowWin32* window = (GHOST_WindowWin32*)::GetWindowLong(hwnd, GWL_USERDATA);
		if (window) {
			switch (msg) {
				// we need to check if new key layout has AltGr
				case WM_INPUTLANGCHANGE:
					system->keyboardAltGr();
					break;
				////////////////////////////////////////////////////////////////////////
				// Keyboard events, processed
				////////////////////////////////////////////////////////////////////////
				case WM_KEYDOWN:
				case WM_SYSKEYDOWN:
					event = processKeyEvent(window, true, wParam, lParam);
					if (!event) {
						GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
						GHOST_PRINT(msg)
						GHOST_PRINT(" key ignored\n")
					}
					break;

				case WM_KEYUP:
				case WM_SYSKEYUP:
					event = processKeyEvent(window, false, wParam, lParam);
					if (!event) {
						GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
						GHOST_PRINT(msg)
						GHOST_PRINT(" key ignored\n")
					}
					break;

				////////////////////////////////////////////////////////////////////////
				// Keyboard events, ignored
				////////////////////////////////////////////////////////////////////////
				case WM_CHAR:
					/* The WM_CHAR message is posted to the window with the keyboard focus when 
					 * a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR 
					 * contains the character code of the key that was pressed. 
					 */
				case WM_DEADCHAR:
					/* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
					 * WM_KEYUP message is translated by the TranslateMessage function. WM_DEADCHAR 
					 * specifies a character code generated by a dead key. A dead key is a key that 
					 * generates a character, such as the umlaut (double-dot), that is combined with 
					 * another character to form a composite character. For example, the umlaut-O 
					 * character (Ù) is generated by typing the dead key for the umlaut character, and 
					 * then typing the O key.
					 */
				case WM_SYSDEADCHAR:
					/* The WM_SYSDEADCHAR message is sent to the window with the keyboard focus when 
					 * a WM_SYSKEYDOWN message is translated by the TranslateMessage function. 
					 * WM_SYSDEADCHAR specifies the character code of a system dead key - that is, 
					 * a dead key that is pressed while holding down the alt key. 
					 */
					break;
				////////////////////////////////////////////////////////////////////////
				// Tablet events, processed
				////////////////////////////////////////////////////////////////////////
				case WT_PACKET:
					((GHOST_WindowWin32*)window)->processWin32TabletEvent(wParam, lParam);
					break;
				case WT_CSRCHANGE:
				case WT_PROXIMITY:
					((GHOST_WindowWin32*)window)->processWin32TabletInitEvent();
					break;
				////////////////////////////////////////////////////////////////////////
				// Mouse events, processed
				////////////////////////////////////////////////////////////////////////
				case WM_LBUTTONDOWN:
					window->registerMouseClickEvent(true);
					event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
					break;
				case WM_MBUTTONDOWN:
					window->registerMouseClickEvent(true);
					event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
					break;
				case WM_RBUTTONDOWN:
					window->registerMouseClickEvent(true);
					event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
					break;
				case WM_XBUTTONDOWN:
					window->registerMouseClickEvent(true);
					if ((short) HIWORD(wParam) == XBUTTON1){
						event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton4);
					}else if((short) HIWORD(wParam) == XBUTTON2){
						event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskButton5);
					}
					break;
				case WM_LBUTTONUP:
					window->registerMouseClickEvent(false);
					event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
					break;
				case WM_MBUTTONUP:
					window->registerMouseClickEvent(false);
					event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
					break;
				case WM_RBUTTONUP:
					window->registerMouseClickEvent(false);
					event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
					break;
				case WM_XBUTTONUP:
					window->registerMouseClickEvent(false);
					if ((short) HIWORD(wParam) == XBUTTON1){
						event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton4);
					}else if((short) HIWORD(wParam) == XBUTTON2){
						event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskButton5);
					}
					break;
				case WM_MOUSEMOVE:
					event = processCursorEvent(GHOST_kEventCursorMove, window);
					break;
				case WM_MOUSEWHEEL:
					/* The WM_MOUSEWHEEL message is sent to the focus window 
					 * when the mouse wheel is rotated. The DefWindowProc 
					 * function propagates the message to the window's parent.
					 * There should be no internal forwarding of the message, 
					 * since DefWindowProc propagates it up the parent chain 
					 * until it finds a window that processes it.
					 */
					event = processWheelEvent(window, wParam, lParam);
					break;
				case WM_SETCURSOR:
					/* The WM_SETCURSOR message is sent to a window if the mouse causes the cursor
					 * to move within a window and mouse input is not captured.
					 * This means we have to set the cursor shape every time the mouse moves!
					 * The DefWindowProc function uses this message to set the cursor to an 
					 * arrow if it is not in the client area.
					 */
					if (LOWORD(lParam) == HTCLIENT) {
						// Load the current cursor
						window->loadCursor(window->getCursorVisibility(), window->getCursorShape());
						// Bypass call to DefWindowProc
						return 0;
					} 
					else {
						// Outside of client area show standard cursor
						window->loadCursor(true, GHOST_kStandardCursorDefault);
					}
					break;

				////////////////////////////////////////////////////////////////////////
				// Mouse events, ignored
				////////////////////////////////////////////////////////////////////////
				case WM_NCMOUSEMOVE:
					/* The WM_NCMOUSEMOVE message is posted to a window when the cursor is moved 
					 * within the nonclient area of the window. This message is posted to the window 
					 * that contains the cursor. If a window has captured the mouse, this message is not posted.
					 */
				case WM_NCHITTEST:
					/* The WM_NCHITTEST message is sent to a window when the cursor moves, or 
					 * when a mouse button is pressed or released. If the mouse is not captured, 
					 * the message is sent to the window beneath the cursor. Otherwise, the message 
					 * is sent to the window that has captured the mouse. 
					 */
					break;

				////////////////////////////////////////////////////////////////////////
				// Window events, processed
				////////////////////////////////////////////////////////////////////////
				case WM_CLOSE:
					/* The WM_CLOSE message is sent as a signal that a window or an application should terminate. */
					event = processWindowEvent(GHOST_kEventWindowClose, window);
					break;
				case WM_ACTIVATE:
					/* The WM_ACTIVATE message is sent to both the window being activated and the window being 
					 * deactivated. If the windows use the same input queue, the message is sent synchronously, 
					 * first to the window procedure of the top-level window being deactivated, then to the window
					 * procedure of the top-level window being activated. If the windows use different input queues,
					 * the message is sent asynchronously, so the window is activated immediately. 
					 */
					event = processWindowEvent(LOWORD(wParam) ? GHOST_kEventWindowActivate : GHOST_kEventWindowDeactivate, window);
					/* WARNING: Let DefWindowProc handle WM_ACTIVATE, otherwise WM_MOUSEWHEEL
					will not be dispatched to OUR active window if we minimize one of OUR windows. */
					lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
					break;
				case WM_PAINT:
					/* An application sends the WM_PAINT message when the system or another application 
					 * makes a request to paint a portion of an application's window. The message is sent
					 * when the UpdateWindow or RedrawWindow function is called, or by the DispatchMessage 
					 * function when the application obtains a WM_PAINT message by using the GetMessage or 
					 * PeekMessage function. 
					 */
					event = processWindowEvent(GHOST_kEventWindowUpdate, window);
					::ValidateRect(hwnd, NULL);
					break;
				case WM_GETMINMAXINFO:
					/* The WM_GETMINMAXINFO message is sent to a window when the size or 
					 * position of the window is about to change. An application can use 
					 * this message to override the window's default maximized size and 
					 * position, or its default minimum or maximum tracking size. 
					 */
					processMinMaxInfo((MINMAXINFO *) lParam);
					/* Let DefWindowProc handle it. */
					break;
				case WM_SIZE:
					/* The WM_SIZE message is sent to a window after its size has changed.
					 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
					 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
					 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
					 * message without calling DefWindowProc.
					 */
					event = processWindowEvent(GHOST_kEventWindowSize, window);
					break;
				case WM_CAPTURECHANGED:
					window->lostMouseCapture();
					break;
				case WM_MOVING:
					/* The WM_MOVING message is sent to a window that the user is moving. By processing 
					 * this message, an application can monitor the size and position of the drag rectangle
					 * and, if needed, change its size or position.
					 */
				case WM_MOVE:
					/* The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
					 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
					 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
					 * message without calling DefWindowProc. 
					 */
					event = processWindowEvent(GHOST_kEventWindowMove, window);
					break;
				////////////////////////////////////////////////////////////////////////
				// Window events, ignored
				////////////////////////////////////////////////////////////////////////
				case WM_WINDOWPOSCHANGED:
					/* The WM_WINDOWPOSCHANGED message is sent to a window whose size, position, or place
					 * in the Z order has changed as a result of a call to the SetWindowPos function or 
					 * another window-management function.
					 * The WM_SIZE and WM_MOVE messages are not sent if an application handles the 
					 * WM_WINDOWPOSCHANGED message without calling DefWindowProc. It is more efficient
					 * to perform any move or size change processing during the WM_WINDOWPOSCHANGED 
					 * message without calling DefWindowProc.
					 */
				case WM_ERASEBKGND:
					/* An application sends the WM_ERASEBKGND message when the window background must be 
					 * erased (for example, when a window is resized). The message is sent to prepare an 
					 * invalidated portion of a window for painting. 
					 */
				case WM_NCPAINT:
					/* An application sends the WM_NCPAINT message to a window when its frame must be painted. */
				case WM_NCACTIVATE:
					/* The WM_NCACTIVATE message is sent to a window when its nonclient area needs to be changed 
					 * to indicate an active or inactive state. 
					 */
				case WM_DESTROY:
					/* The WM_DESTROY message is sent when a window is being destroyed. It is sent to the window 
					 * procedure of the window being destroyed after the window is removed from the screen.	
					 * This message is sent first to the window being destroyed and then to the child windows 
					 * (if any) as they are destroyed. During the processing of the message, it can be assumed 
					 * that all child windows still exist. 
					 */
				case WM_NCDESTROY:
					/* The WM_NCDESTROY message informs a window that its nonclient area is being destroyed. The 
					 * DestroyWindow function sends the WM_NCDESTROY message to the window following the WM_DESTROY
					 * message. WM_DESTROY is used to free the allocated memory object associated with the window. 
					 */
				case WM_KILLFOCUS:
					/* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. */
				case WM_SHOWWINDOW:
					/* The WM_SHOWWINDOW message is sent to a window when the window is about to be hidden or shown. */
				case WM_WINDOWPOSCHANGING:
					/* The WM_WINDOWPOSCHANGING message is sent to a window whose size, position, or place in 
					 * the Z order is about to change as a result of a call to the SetWindowPos function or 
					 * another window-management function. 
					 */
				case WM_SETFOCUS:
					/* The WM_SETFOCUS message is sent to a window after it has gained the keyboard focus. */
				case WM_ENTERSIZEMOVE:
					/* The WM_ENTERSIZEMOVE message is sent one time to a window after it enters the moving 
					 * or sizing modal loop. The window enters the moving or sizing modal loop when the user 
					 * clicks the window's title bar or sizing border, or when the window passes the 
					 * WM_SYSCOMMAND message to the DefWindowProc function and the wParam parameter of the 
					 * message specifies the SC_MOVE or SC_SIZE value. The operation is complete when 
					 * DefWindowProc returns. 
					 */
					break;
					
				////////////////////////////////////////////////////////////////////////
				// Other events
				////////////////////////////////////////////////////////////////////////
				case WM_GETTEXT:
					/* An application sends a WM_GETTEXT message to copy the text that 
					 * corresponds to a window into a buffer provided by the caller. 
					 */
				case WM_ACTIVATEAPP:
					/* The WM_ACTIVATEAPP message is sent when a window belonging to a 
					 * different application than the active window is about to be activated.
					 * The message is sent to the application whose window is being activated
					 * and to the application whose window is being deactivated. 
					 */
				case WM_TIMER:
					/* The WIN32 docs say:
					 * The WM_TIMER message is posted to the installing thread's message queue
					 * when a timer expires. You can process the message by providing a WM_TIMER
					 * case in the window procedure. Otherwise, the default window procedure will
					 * call the TimerProc callback function specified in the call to the SetTimer
					 * function used to install the timer. 
					 *
					 * In GHOST, we let DefWindowProc call the timer callback.
					 */
					break;
				case WM_BLND_NDOF_AXIS:
					{
						GHOST_TEventNDOFData ndofdata;
						system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
						system->m_eventManager->
							pushEvent(new GHOST_EventNDOF(
								system->getMilliSeconds(), 
								GHOST_kEventNDOFMotion, 
								window, ndofdata));
					}
					break;
				case WM_BLND_NDOF_BTN:
					{
						GHOST_TEventNDOFData ndofdata;
						system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
						system->m_eventManager->
							pushEvent(new GHOST_EventNDOF(
								system->getMilliSeconds(), 
								GHOST_kEventNDOFButton, 
								window, ndofdata));
					}
					break;
			}
		}
		else {
			// Event found for a window before the pointer to the class has been set.
			GHOST_PRINT("GHOST_SystemWin32::wndProc: GHOST window event before creation\n")
			/* These are events we typically miss at this point:
			   WM_GETMINMAXINFO	0x24
			   WM_NCCREATE			0x81
			   WM_NCCALCSIZE		0x83
			   WM_CREATE			0x01
			   We let DefWindowProc do the work.
			*/
		}
	}
	else {
		// Events without valid hwnd
		GHOST_PRINT("GHOST_SystemWin32::wndProc: event without window\n")
	}

	if (event) {
		system->pushEvent(event);
	}
	else {
		lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
	}
	return lResult;
}

GHOST_TUns8* GHOST_SystemWin32::getClipboard(bool selection) const 
{
	char *buffer;
	char *temp_buff;
	
	if ( IsClipboardFormatAvailable(CF_TEXT) && OpenClipboard(NULL) ) {
		HANDLE hData = GetClipboardData( CF_TEXT );
		if (hData == NULL) {
			CloseClipboard();
			return NULL;
		}
		buffer = (char*)GlobalLock( hData );
		
		temp_buff = (char*) malloc(strlen(buffer)+1);
		strcpy(temp_buff, buffer);
		
		GlobalUnlock( hData );
		CloseClipboard();
		
		temp_buff[strlen(buffer)] = '\0';
		if (buffer) {
			return (GHOST_TUns8*)temp_buff;
		} else {
			return NULL;
		}
	} else {
		return NULL;
	}
}

void GHOST_SystemWin32::putClipboard(GHOST_TInt8 *buffer, bool selection) const
{
	if(selection) {return;} // for copying the selection, used on X11

	if (OpenClipboard(NULL)) {
		HLOCAL clipbuffer;
		char *data;
		
		if (buffer) {
			EmptyClipboard();
			
			clipbuffer = LocalAlloc(LMEM_FIXED,((strlen(buffer)+1)));
			data = (char*)GlobalLock(clipbuffer);

			strcpy(data, (char*)buffer);
			data[strlen(buffer)] = '\0';
			LocalUnlock(clipbuffer);
			SetClipboardData(CF_TEXT,clipbuffer);
		}
		CloseClipboard();
	} else {
		return;
	}
}

const GHOST_TUns8* GHOST_SystemWin32::getSystemDir() const
{
	static char knownpath[MAX_PATH];
	HRESULT hResult = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);

	if (hResult == S_OK)
	{
		return (GHOST_TUns8*)knownpath;
	}

	return NULL;
}

const GHOST_TUns8* GHOST_SystemWin32::getUserDir() const
{
	static char knownpath[MAX_PATH];
	HRESULT hResult = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, knownpath);

	if (hResult == S_OK)
	{
		return (GHOST_TUns8*)knownpath;
	}

	return NULL;
}

const GHOST_TUns8* GHOST_SystemWin32::getBinaryDir() const
{
	static char fullname[MAX_PATH];
	if(GetModuleFileName(0, fullname, MAX_PATH)) {
		return (GHOST_TUns8*)fullname;
	}

	return NULL;
}
