GHOST_TSuccess GHOST_SystemCarbon::getModifierKeys(GHOST_ModifierKeys& keys) const
{
	UInt32 modifiers = ::GetCurrentKeyModifiers();

	keys.set(GHOST_kModifierKeyOS, (modifiers & cmdKey) ? true : false);
	keys.set(GHOST_kModifierKeyLeftAlt, (modifiers & optionKey) ? true : false);
	keys.set(GHOST_kModifierKeyLeftShift, (modifiers & shiftKey) ? true : false);
	keys.set(GHOST_kModifierKeyLeftControl, (modifiers & controlKey) ? true : false);
	
	return GHOST_kSuccess;
}
예제 #2
0
GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool& isDown) const
{
	GHOST_ModifierKeys keys;
	// Get the state of all modifier keys 
	GHOST_TSuccess success = getModifierKeys(keys);
	if (success) {
		// Isolate the state of the key requested
		isDown = keys.get(mask);
	}
	return success;
}
예제 #3
0
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;
}
예제 #4
0
GHOST_TSuccess
GHOST_SystemSDL::getModifierKeys(GHOST_ModifierKeys& keys) const
{
	SDL_Keymod mod= SDL_GetModState();

	keys.set(GHOST_kModifierKeyLeftShift,    (mod & KMOD_LSHIFT) != 0);
	keys.set(GHOST_kModifierKeyRightShift,   (mod & KMOD_RSHIFT) != 0);
	keys.set(GHOST_kModifierKeyLeftControl,  (mod & KMOD_LCTRL) != 0);
	keys.set(GHOST_kModifierKeyRightControl, (mod & KMOD_RCTRL) != 0);
	keys.set(GHOST_kModifierKeyLeftAlt,      (mod & KMOD_LALT) != 0);
	keys.set(GHOST_kModifierKeyRightAlt,     (mod & KMOD_RALT) != 0);
	keys.set(GHOST_kModifierKeyOS,           (mod & (KMOD_LGUI|KMOD_RGUI)) != 0);

	return GHOST_kSuccess;
}
예제 #5
0
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	GHOST_Event *event = 0;
	bool eventHandled = false;

	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 *)::GetWindowLongPtr(hwnd, GWL_USERDATA);
		if (window) {
			switch (msg) {
				// we need to check if new key layout has AltGr
				case WM_INPUTLANGCHANGE:
					system->handleKeyboardChange();
					break;
				////////////////////////////////////////////////////////////////////////
				// Keyboard events, processed
				////////////////////////////////////////////////////////////////////////
				case WM_INPUT:
				{
					// check WM_INPUT from input sink when ghost window is not in the foreground
					if (wParam == RIM_INPUTSINK) {
						if (GetFocus() != hwnd) // WM_INPUT message not for this window
							return 0;
					} //else wParam == RIM_INPUT

					RAWINPUT raw;
					RAWINPUT *raw_ptr = &raw;
					UINT rawSize = sizeof(RAWINPUT);

					GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));

					switch (raw.header.dwType)
					{
						case RIM_TYPEKEYBOARD:
							event = processKeyEvent(window, raw);
							if (!event) {
								GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ");
								GHOST_PRINT(msg);
								GHOST_PRINT(" key ignored\n");
							}
							break;
#ifdef WITH_INPUT_NDOF
						case RIM_TYPEHID:
							if (system->processNDOF(raw))
								eventHandled = true;
							break;
#endif
					}
					break;
				}
				////////////////////////////////////////////////////////////////////////
				// Keyboard events, ignored
				////////////////////////////////////////////////////////////////////////
				case WM_KEYDOWN:
				case WM_SYSKEYDOWN:
				case WM_KEYUP:
				case WM_SYSKEYUP:
				/* These functions were replaced by WM_INPUT*/
				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.
					 */
					break;
				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.
				 */
				case WM_SYSCHAR:
					/* The WM_SYSCHAR message is sent to the window with the keyboard focus when 
					 * a WM_SYSCHAR message is translated by the TranslateMessage function. 
					 * WM_SYSCHAR specifies the character code of a dead key - that is, 
					 * a dead key that is pressed while holding down the alt key.
					 * To prevent the sound, DefWindowProc must be avoided by return
					 */
					break;
				case WM_SYSCOMMAND:
					/* The WM_SYSCHAR message is sent to the window when system commands such as 
					 * maximize, minimize  or close the window are triggered. Also it is sent when ALT 
					 * button is press for menu. To prevent this we must return preventing DefWindowProc.
					 */
					if (wParam == SC_KEYMENU) 
					{
						eventHandled = true;
					}// else
						/* XXX Disable for now due to area resizing issue. bug# 34990 */
					/*if((wParam&0xfff0)==SC_SIZE)
					{
						window->registerMouseClickEvent(0);
						window->m_wsh.startSizing(wParam);
						eventHandled = true;
					}*/
					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(0);
					event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskLeft);
					break;
				case WM_MBUTTONDOWN:
					window->registerMouseClickEvent(0);
					event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskMiddle);
					break;
				case WM_RBUTTONDOWN:
					window->registerMouseClickEvent(0);
					event = processButtonEvent(GHOST_kEventButtonDown, window, GHOST_kButtonMaskRight);
					break;
				case WM_XBUTTONDOWN:
					window->registerMouseClickEvent(0);
					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(1);
					if(window->m_wsh.isWinChanges())
						window->m_wsh.accept();
					else
						event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskLeft);
					break;
				case WM_MBUTTONUP:
					window->registerMouseClickEvent(1);
					event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskMiddle);
					break;
				case WM_RBUTTONUP:
					window->registerMouseClickEvent(1);
					event = processButtonEvent(GHOST_kEventButtonUp, window, GHOST_kButtonMaskRight);
					break;
				case WM_XBUTTONUP:
					window->registerMouseClickEvent(1);
					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:
					if(window->m_wsh.isWinChanges())
						window->m_wsh.updateWindowSize();
					else
						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. 
					 */
				{
					GHOST_ModifierKeys modifiers;
					modifiers.clear();
					system->storeModifierKeys(modifiers);
					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. */
					if(LOWORD(wParam)==WA_INACTIVE)
						window->lostMouseCapture();

					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. 
					 */
					break;
				case WM_KILLFOCUS:
					/* The WM_KILLFOCUS message is sent to a window immediately before it loses the keyboard focus. 
					 * We want to prevent this if a window is still active and it loses focus to nowhere*/
					if (!wParam && hwnd == GetActiveWindow())
						SetFocus(hwnd);
				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_CANCELMODE:
					if(window->m_wsh.isWinChanges())
						window->m_wsh.cancel();

			}
		}
		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);
		eventHandled = true;
	}

	if (!eventHandled)
		lResult = ::DefWindowProcW(hwnd, msg, wParam, lParam);

	return lResult;
}
예제 #6
0
GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int *keyDown, char *vk)
{
	GHOST_TKey key = GHOST_kKeyUnknown;


	if (!keyDown)
		return GHOST_kKeyUnknown;


	GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();

	GHOST_ModifierKeys modifiers;
	system->retrieveModifierKeys(modifiers);

	// RI_KEY_BREAK doesn't work for sticky keys release, so we also
	// check for the up message
	unsigned int msg = raw.data.keyboard.Message;
	*keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK) && msg != WM_KEYUP && msg != WM_SYSKEYUP;

	key = this->convertKey(window, raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags & (RI_KEY_E1 | RI_KEY_E0)));
	
	// extra handling of modifier keys: don't send repeats out from GHOST
	if (key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt)
	{
		bool changed = false;
		GHOST_TModifierKeyMask modifier;
		switch (key) {
			case GHOST_kKeyLeftShift:
			{
				changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
				modifier = GHOST_kModifierKeyLeftShift;
			}
			break;
			case GHOST_kKeyRightShift:
			{
				changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
				modifier = GHOST_kModifierKeyRightShift;
			}
			break;
			case GHOST_kKeyLeftControl:
			{
				changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
				modifier = GHOST_kModifierKeyLeftControl;
			}
			break;
			case GHOST_kKeyRightControl:
			{
				changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
				modifier = GHOST_kModifierKeyRightControl;
			}
			break;
			case GHOST_kKeyLeftAlt:
			{
				changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
				modifier = GHOST_kModifierKeyLeftAlt;
			}
			break;
			case GHOST_kKeyRightAlt:
			{
				changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
				modifier = GHOST_kModifierKeyRightAlt;
			}
			break;
			default: break;
		}
		
		if (changed)
		{
			modifiers.set(modifier, (bool)*keyDown);
			system->storeModifierKeys(modifiers);
		}
		else {
			key = GHOST_kKeyUnknown;
		}
	}
	

	if (vk) *vk = raw.data.keyboard.VKey;

	return key;
}
예제 #7
0
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;
	}
}