From 8386c20b6a875f12e1effab5f1b25d10b348a297 Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Mon, 10 Mar 2008 08:50:19 +0000 Subject: [PATCH] Can now distinguish between left and right control, shift, alt and enter keys. --- Source/OpenTK/Platform/Windows/API.cs | 39 +++++++++- Source/OpenTK/Platform/Windows/WMInput.cs | 72 ++++++++++++------- .../OpenTK/Platform/Windows/WinGLContext.cs | 2 +- 3 files changed, 87 insertions(+), 26 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/API.cs b/Source/OpenTK/Platform/Windows/API.cs index 059d2969..69365215 100644 --- a/Source/OpenTK/Platform/Windows/API.cs +++ b/Source/OpenTK/Platform/Windows/API.cs @@ -521,7 +521,7 @@ namespace OpenTK.Platform.Windows #endregion - #region GetASsyncKeyState + #region GetAsyncKeyState [System.Security.SuppressUnmanagedCodeSecurity] [DllImport("user32.dll", SetLastError = true)] @@ -529,6 +529,26 @@ namespace OpenTK.Platform.Windows #endregion + #region GetKeyState + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("user32.dll", SetLastError = true)] + internal static extern SHORT GetKeyState(VirtualKeys vKey); + + #endregion + + #region MapVirtualKey + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("user32.dll", SetLastError = true)] + internal static extern UINT MapVirtualKey(UINT uCode, MapVirtualKeyType uMapType); + + [System.Security.SuppressUnmanagedCodeSecurity] + [DllImport("user32.dll", SetLastError = true)] + internal static extern UINT MapVirtualKey(VirtualKeys vkey, MapVirtualKeyType uMapType); + + #endregion + #region ShowWindow /// @@ -3506,6 +3526,23 @@ namespace OpenTK.Platform.Windows #endregion + #region MapVirtualKeyType + + internal enum MapVirtualKeyType + { + /// uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the function returns 0. + VirtualKeyToScanCode = 0, + /// uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and right-hand keys. If there is no translation, the function returns 0. + ScanCodeToVirtualKey = 1, + /// uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, the function returns 0. + VirtualKeyToCharacter = 2, + /// Windows NT/2000/XP: uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand keys. If there is no translation, the function returns 0. + ScanCodeToVirtualKeyExtended = 3, + VirtualKeyToScanCodeExtended = 4, + } + + #endregion + #endregion #region --- Callbacks --- diff --git a/Source/OpenTK/Platform/Windows/WMInput.cs b/Source/OpenTK/Platform/Windows/WMInput.cs index 44dfd3ec..2a0dabd3 100644 --- a/Source/OpenTK/Platform/Windows/WMInput.cs +++ b/Source/OpenTK/Platform/Windows/WMInput.cs @@ -25,6 +25,10 @@ namespace OpenTK.Platform.Windows IList keyboards = new List(1); IList mice = new List(1); internal static readonly WinKeyMap KeyMap = new WinKeyMap(); + // Used to distinguish left and right control, alt and enter keys. + const int ExtendedBit = 1 << 24; + // Used to distinguish left and right shift keys. + static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0); #region --- Constructor --- @@ -110,42 +114,58 @@ namespace OpenTK.Platform.Windows case WindowMessage.SYSKEYUP: bool pressed = (WindowMessage)msg.Msg == WindowMessage.KEYDOWN || (WindowMessage)msg.Msg == WindowMessage.SYSKEYDOWN; - //bool left = (((int)msg.LParam) & 0x100000) == 0; // valid for Shift, Control and Menu presses. + + // Shift/Control/Alt behave strangely when e.g. ShiftRight is held down and ShiftLeft is pressed + // and released. It looks like neither key is released in this case, or that the wrong key is + // released in the case of Control and Alt. + // To combat this, we are going to release both keys when either is released. Hacky, but should work. + // Win95 does not distinguish left/right key constants (GetAsyncKeyState returns 0). + // In this case, both keys will be reported as pressed. + + bool extended = (((int)msg.LParam) & ExtendedBit) != 0; switch ((VirtualKeys)msg.WParam) { case VirtualKeys.SHIFT: - // Win95 does not distinguish left/right constants (GetAsyncKeyState returns 0). - // In this case, report both keys as down. - bool left = Functions.GetAsyncKeyState(VirtualKeys.LSHIFT) != 0; - bool right = Functions.GetAsyncKeyState(VirtualKeys.RSHIFT) != 0; - if (left) + // The behavior of this key is very strange. Unlike Control and Alt, there is no extended bit + // to distinguish between left and right keys. Moreover, pressing both keys and releasing one + // may result in both keys being held down (but not always). + // The only reliably way to solve this was reported by BlueMonkMN at the forums: we should + // check the scancodes. It looks like GLFW does the same thing, so it should be reliable. + + // TODO: Not 100% reliable, when both keys are pressed at once. + if (ShiftRightScanCode != 0) + { + if ((((int)msg.LParam >> 16) & 0xFF) == ShiftRightScanCode) + keyboard[Input.Key.ShiftRight] = pressed; + else + keyboard[Input.Key.ShiftLeft] = pressed; + } + else + { + // Should only fall here on Windows 9x and NT4.0- keyboard[Input.Key.ShiftLeft] = pressed; - if (right) - keyboard[Input.Key.ShiftRight] = pressed; - if (!left && !right) - keyboard[Input.Key.ShiftLeft] = keyboard[Input.Key.ShiftRight] = pressed; + } return; case VirtualKeys.CONTROL: - left = Functions.GetAsyncKeyState(VirtualKeys.LCONTROL) != 0; - right = Functions.GetAsyncKeyState(VirtualKeys.RCONTROL) != 0; - if (left) - keyboard[Input.Key.ControlLeft] = pressed; - if (right) + if (extended) keyboard[Input.Key.ControlRight] = pressed; - if (!left && !right) - keyboard[Input.Key.ControlLeft] = keyboard[Input.Key.ControlRight] = pressed; + else + keyboard[Input.Key.ControlLeft] = pressed; return; case VirtualKeys.MENU: - left = Functions.GetAsyncKeyState(VirtualKeys.LMENU) != 0; - right = Functions.GetAsyncKeyState(VirtualKeys.RMENU) != 0; - if (left) - keyboard[Input.Key.AltLeft] = pressed; - if (right) + if (extended) keyboard[Input.Key.AltRight] = pressed; - if (!left && !right) - keyboard[Input.Key.AltLeft] = keyboard[Input.Key.AltRight] = pressed; + else + keyboard[Input.Key.AltLeft] = pressed; + return; + + case VirtualKeys.RETURN: + if (extended) + keyboard[Key.KeypadEnter] = pressed; + else + keyboard[Key.Enter] = pressed; return; default: @@ -162,6 +182,10 @@ namespace OpenTK.Platform.Windows } break; + case WindowMessage.KILLFOCUS: + keyboard.ClearKeys(); + break; + case WindowMessage.DESTROY: Debug.Print("Input window detached from parent {0}.", Handle); ReleaseHandle(); diff --git a/Source/OpenTK/Platform/Windows/WinGLContext.cs b/Source/OpenTK/Platform/Windows/WinGLContext.cs index 64fc9f13..2136ae2c 100644 --- a/Source/OpenTK/Platform/Windows/WinGLContext.cs +++ b/Source/OpenTK/Platform/Windows/WinGLContext.cs @@ -392,7 +392,7 @@ namespace OpenTK.Platform.Windows if (!Wgl.Imports.DeleteContext(renderContext)) { //throw new ApplicationException("Could not destroy the OpenGL render context. Error: " + Marshal.GetLastWin32Error()); - //Debug.Print("Could not destroy the OpenGL render context. Error: {0}", Marshal.GetLastWin32Error()); + Debug.Print("Could not destroy the OpenGL render context. Error: {0}", Marshal.GetLastWin32Error()); } renderContext = null; }