From c2d6f0c091e9388ce2708fd9c3f083571e204000 Mon Sep 17 00:00:00 2001 From: the_fiddler Date: Mon, 8 Nov 2010 21:43:29 +0000 Subject: [PATCH] Refactored input drivers in terms of WinInputBase to reduce code duplication. --- Source/OpenTK/Platform/Windows/WMInput.cs | 349 ++++++------------ Source/OpenTK/Platform/Windows/WinRawInput.cs | 289 ++++++--------- .../OpenTK/Platform/Windows/WinRawKeyboard.cs | 7 +- Source/OpenTK/Platform/Windows/WinRawMouse.cs | 2 +- 4 files changed, 238 insertions(+), 409 deletions(-) diff --git a/Source/OpenTK/Platform/Windows/WMInput.cs b/Source/OpenTK/Platform/Windows/WMInput.cs index 1dd3e2dd..5f14efa8 100644 --- a/Source/OpenTK/Platform/Windows/WMInput.cs +++ b/Source/OpenTK/Platform/Windows/WMInput.cs @@ -27,269 +27,160 @@ using System; using System.Collections.Generic; -using System.Text; -using System.Windows.Forms; - -using OpenTK.Input; using System.Diagnostics; using System.Drawing; +using System.Threading; +using System.Text; + +using OpenTK.Input; + namespace OpenTK.Platform.Windows { // Input driver for legacy (pre XP) Windows platforms. - sealed class WMInput : System.Windows.Forms.NativeWindow, IInputDriver2 + // Supports a single mouse and keyboard through WM_MOUSE* and WM_KEY* events. + // Supports multiple joysticks through WinMM. + sealed class WMInput : +#if !ASYNC_INPUT + WinInputBase, +#else + IInputDriver2, +#endif + IMouseDriver2, IKeyboardDriver2, IGamePadDriver { - #region --- Fields --- + #region Fields - WinMMJoystick gamepad_driver = new WinMMJoystick(); - // Driver supports only one keyboard and mouse; - KeyboardDevice keyboard = new KeyboardDevice(); - MouseDevice mouse = new MouseDevice(); - 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 long ExtendedBit = 1 << 24; - // Used to distinguish left and right shift keys. - static readonly uint ShiftRightScanCode = Functions.MapVirtualKey(VirtualKeys.RSHIFT, 0); + readonly WinMMJoystick gamepad_driver = new WinMMJoystick(); + KeyboardState keyboard = new KeyboardState(); + MouseState mouse = new MouseState(); + + readonly WinKeyMap KeyMap = new WinKeyMap(); #endregion - #region --- Constructor --- + #region Constructor - public WMInput(WinWindowInfo parent) + public WMInput() + : base() { - Debug.WriteLine("Initalizing WMInput driver."); - Debug.Indent(); - - AssignHandle(parent.WindowHandle); - Debug.Print("Input window attached to parent {0}", parent); - - Debug.Unindent(); - - keyboard.Description = "Standard Windows keyboard"; - keyboard.NumberOfFunctionKeys = 12; - keyboard.NumberOfKeys = 101; - keyboard.NumberOfLeds = 3; - - mouse.Description = "Standard Windows mouse"; - mouse.NumberOfButtons = 3; - mouse.NumberOfWheels = 1; - - keyboards.Add(keyboard); - mice.Add(mouse); + Debug.WriteLine("Using WMInput."); } #endregion - #region protected override void WndProc(ref Message msg) - - bool mouse_about_to_enter = false; - protected override void WndProc(ref Message msg) + #region Private Members +#if ASYNC_INPUT + void UpdateKeyboard() { - UIntPtr lparam, wparam; - unsafe + for (int i = 0; i < 256; i++) { - lparam = (UIntPtr)(void*)msg.LParam; - wparam = (UIntPtr)(void*)msg.WParam; - } - - switch ((WindowMessage)msg.Msg) - { - // Mouse events: - case WindowMessage.NCMOUSEMOVE: - mouse_about_to_enter = true; // Used to simulate a mouse enter event. - break; - - case WindowMessage.MOUSEMOVE: - mouse.Position = new Point( - (int)(lparam.ToUInt32() & 0x0000FFFF), - (int)(lparam.ToUInt32() & 0xFFFF0000) >> 16); - if (mouse_about_to_enter) - { - Cursor.Current = Cursors.Default; - mouse_about_to_enter = false; - } - return; - - case WindowMessage.MOUSEWHEEL: - // This is due to inconsistent behavior of the WParam value on 64bit arch, whese - // wparam = 0xffffffffff880000 or wparam = 0x00000000ff100000 - mouse.Wheel += (int)((long)msg.WParam << 32 >> 48) / 120; - return; - - case WindowMessage.LBUTTONDOWN: - mouse[MouseButton.Left] = true; - return; - - case WindowMessage.MBUTTONDOWN: - mouse[MouseButton.Middle] = true; - return; - - case WindowMessage.RBUTTONDOWN: - mouse[MouseButton.Right] = true; - return; - - case WindowMessage.XBUTTONDOWN: - mouse[((wparam.ToUInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = true; - return; - - case WindowMessage.LBUTTONUP: - mouse[MouseButton.Left] = false; - return; - - case WindowMessage.MBUTTONUP: - mouse[MouseButton.Middle] = false; - return; - - case WindowMessage.RBUTTONUP: - mouse[MouseButton.Right] = false; - return; - - case WindowMessage.XBUTTONUP: - // TODO: Is this correct? - mouse[((wparam.ToUInt32() & 0xFFFF0000) >> 16) != (int)MouseKeys.XButton1 ? MouseButton.Button1 : MouseButton.Button2] = false; - return; - - // Keyboard events: - case WindowMessage.KEYDOWN: - case WindowMessage.KEYUP: - case WindowMessage.SYSKEYDOWN: - case WindowMessage.SYSKEYUP: - bool pressed = (WindowMessage)msg.Msg == WindowMessage.KEYDOWN || - (WindowMessage)msg.Msg == WindowMessage.SYSKEYDOWN; - - // 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 = (msg.LParam.ToInt64() & ExtendedBit) != 0; - switch ((VirtualKeys)wparam) - { - case VirtualKeys.SHIFT: - // 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) - { - unchecked - { - if (((lparam.ToUInt32() >> 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; - } - return; - - case VirtualKeys.CONTROL: - if (extended) - keyboard[Input.Key.ControlRight] = pressed; - else - keyboard[Input.Key.ControlLeft] = pressed; - return; - - case VirtualKeys.MENU: - if (extended) - 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: - if (!WMInput.KeyMap.ContainsKey((VirtualKeys)msg.WParam)) - { - Debug.Print("Virtual key {0} ({1}) not mapped.", (VirtualKeys)msg.WParam, (int)msg.WParam); - break; - } - else - { - keyboard[WMInput.KeyMap[(VirtualKeys)msg.WParam]] = pressed; - return; - } - } - break; - - case WindowMessage.KILLFOCUS: - keyboard.ClearKeys(); - break; - - case WindowMessage.DESTROY: - Debug.Print("Input window detached from parent {0}.", Handle); - ReleaseHandle(); - break; - - case WindowMessage.QUIT: - Debug.WriteLine("Input window quit."); - this.Dispose(); - break; - } - - base.WndProc(ref msg); - } - - #endregion - - #region --- IDisposable Members --- - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - this.ReleaseHandle(); - - disposed = true; + VirtualKeys key = (VirtualKeys)i; + bool pressed = (Functions.GetAsyncKeyState(key) >> 8) != 0; + if (KeyMap.ContainsKey(key)) + { + keyboard[KeyMap[key]] = pressed; + } } } - ~WMInput() + void UpdateMouse() { - Dispose(false); + POINT p = new POINT(); + Functions.GetCursorPos(ref p); + // Note: we cannot poll the mouse wheel + mouse[MouseButton.Left] = (Functions.GetAsyncKeyState(VirtualKeys.LBUTTON) >> 8) != 0; + mouse[MouseButton.Middle] = (Functions.GetAsyncKeyState(VirtualKeys.RBUTTON) >> 8) != 0; + mouse[MouseButton.Right] = (Functions.GetAsyncKeyState(VirtualKeys.MBUTTON) >> 8) != 0; + mouse[MouseButton.Button1] = (Functions.GetAsyncKeyState(VirtualKeys.XBUTTON1) >> 8) != 0; + mouse[MouseButton.Button2] = (Functions.GetAsyncKeyState(VirtualKeys.XBUTTON2) >> 8) != 0; + } +#endif + #endregion + + #region Protected Members + + protected override IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + return base.WindowProcedure(handle, message, wParam, lParam); + } + + protected override void CreateDrivers() + { + //keyboard.IsConnected = true; + //Native.KeyDown += delegate(object sender, KeyboardKeyEventArgs e) + //{ + // keyboard.EnableBit((int)e.Key); + //}; + //Native.KeyUp += delegate(object sender, KeyboardKeyEventArgs e) + //{ + // keyboard.DisableBit((int)e.Key); + //}; + + //mouse.IsConnected = false; + // Todo: implement and hook INativeWindow.Mouse* events. + //Native.MouseMove += delegate(object sender, MouseMoveEventArgs e) + //{ + //}; } #endregion - public IMouseDriver2 MouseDriver + #region IInputDriver2 Members + + public override IKeyboardDriver2 KeyboardDriver { - get { throw new NotImplementedException(); } + get { return this; } } - public IKeyboardDriver2 KeyboardDriver + public override IMouseDriver2 MouseDriver { - get { throw new NotImplementedException(); } + get { return this; } } - public IGamePadDriver GamePadDriver + public override IGamePadDriver GamePadDriver { - get { throw new NotImplementedException(); } + get { return this; } } + + #endregion + + #region IMouseDriver2 Members + + public MouseState GetState() + { + return mouse; + } + + public MouseState GetState(int index) + { + if (index == 0) + return mouse; + else + return new MouseState(); + } + + #endregion + + #region IKeyboardDriver2 Members + + KeyboardState IKeyboardDriver2.GetState() + { + return keyboard; + } + + KeyboardState IKeyboardDriver2.GetState(int index) + { + if (index == 0) + return keyboard; + else + return new KeyboardState(); + } + + string IKeyboardDriver2.GetDeviceName(int index) + { + return "Default Windows Keyboard"; + } + + #endregion } } diff --git a/Source/OpenTK/Platform/Windows/WinRawInput.cs b/Source/OpenTK/Platform/Windows/WinRawInput.cs index 03e2f67b..54425ded 100644 --- a/Source/OpenTK/Platform/Windows/WinRawInput.cs +++ b/Source/OpenTK/Platform/Windows/WinRawInput.cs @@ -25,50 +25,132 @@ // #endregion -#region --- Using directives --- - using System; -using System.Collections.Generic; -using System.Text; -using System.Runtime.InteropServices; using System.Diagnostics; -using System.Windows.Forms; -using OpenTK.Input; +using System.Runtime.InteropServices; using System.Threading; - -#endregion +using OpenTK.Input; namespace OpenTK.Platform.Windows { - // Not complete. - sealed class WinRawInput : IInputDriver2 + sealed class WinRawInput : WinInputBase { + #region Fields + // Input event data. static RawInput data = new RawInput(); static readonly int rawInputStructSize = API.RawInputSize; - static readonly Thread InputThread = new Thread(ProcessEvents); - static WinRawKeyboard keyboardDriver; - static WinRawMouse mouseDriver; - static readonly WinMMJoystick joystickDriver = new WinMMJoystick(); + WinRawKeyboard keyboard_driver; + WinRawMouse mouse_driver; + WinMMJoystick joystick_driver; - static INativeWindow Native; - static WinWindowInfo Parent { get { return Native.WindowInfo as WinWindowInfo; } } - static readonly WindowProcedure WndProc = WindowProcedureImplementation; - static IntPtr OldWndProc; - - static IntPtr DevNotifyHandle; + IntPtr DevNotifyHandle; static readonly Guid DeviceInterfaceHid = new Guid("4D1E55B2-F16F-11CF-88CB-001111000030"); + #endregion + #region Constructors public WinRawInput() + : base() { - InputThread.IsBackground = true; - InputThread.Start(); + Debug.WriteLine("Using WinRawInput."); + } - while (mouseDriver == null || keyboardDriver == null) - Thread.Sleep(0); + #endregion + + #region Private Members + + static IntPtr RegisterForDeviceNotifications(WinWindowInfo parent) + { + IntPtr dev_notify_handle; + BroadcastDeviceInterface bdi = new BroadcastDeviceInterface(); + bdi.Size = BlittableValueType.StrideOf(bdi); + bdi.DeviceType = DeviceBroadcastType.INTERFACE; + bdi.ClassGuid = DeviceInterfaceHid; + unsafe + { + dev_notify_handle = Functions.RegisterDeviceNotification(parent.WindowHandle, + new IntPtr((void*)&bdi), DeviceNotification.WINDOW_HANDLE); + } + if (dev_notify_handle == IntPtr.Zero) + Debug.Print("[Warning] Failed to register for device notifications. Error: {0}", Marshal.GetLastWin32Error()); + + return dev_notify_handle; + } + + + #endregion + + #region Protected Members + + #region WindowProcedure + + // Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID. + protected override IntPtr WindowProcedure( + IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) + { + switch (message) + { + case WindowMessage.INPUT: + int size = 0; + // Get the size of the input buffer + Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, + IntPtr.Zero, ref size, API.RawInputHeaderSize); + + // Read the actual raw input structure + if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, + out data, ref size, API.RawInputHeaderSize)) + { + switch (data.Header.Type) + { + case RawInputDeviceType.KEYBOARD: + if (((WinRawKeyboard)KeyboardDriver).ProcessKeyboardEvent(data)) + return IntPtr.Zero; + break; + + case RawInputDeviceType.MOUSE: + if (((WinRawMouse)MouseDriver).ProcessMouseEvent(data)) + return IntPtr.Zero; + break; + + case RawInputDeviceType.HID: + break; + } + } + break; + + case WindowMessage.DEVICECHANGE: + ((WinRawKeyboard)KeyboardDriver).RefreshDevices(); + ((WinRawMouse)MouseDriver).RefreshDevices(); + break; + } + return base.WindowProcedure(handle, message, wParam, lParam); + } + + #endregion + + #region CreateDrivers + + protected override void CreateDrivers() + { + keyboard_driver = new WinRawKeyboard(Parent.WindowHandle); + mouse_driver = new WinRawMouse(Parent.WindowHandle); + joystick_driver = new WinMMJoystick(); + + DevNotifyHandle = RegisterForDeviceNotifications(Parent); + } + + #endregion + + protected override void Dispose(bool manual) + { + if (!Disposed) + { + Functions.UnregisterDeviceNotification(DevNotifyHandle); + base.Dispose(manual); + } } #endregion @@ -91,166 +173,21 @@ namespace OpenTK.Platform.Windows #endregion - #region Private Members - - #region ConstructMessageWindow - - static INativeWindow ConstructMessageWindow() - { - Debug.WriteLine("Initializing windows raw input driver."); - Debug.Indent(); - - // Create a new message-only window to retrieve WM_INPUT messages. - Native = new NativeWindow(); - Native.ProcessEvents(); - Functions.SetParent(Parent.WindowHandle, Constants.MESSAGE_ONLY); - Native.ProcessEvents(); - RegisterForDeviceNotifications(); - - // Subclass the window to retrieve the events we are interested in. - OldWndProc = Functions.SetWindowLong(Parent.WindowHandle, WndProc); - - Debug.Print("Input window attached to parent {0}", Parent); - keyboardDriver = new WinRawKeyboard(Parent.WindowHandle); - mouseDriver = new WinRawMouse(Parent.WindowHandle); - - Debug.Unindent(); - return Native; - } - - static void RegisterForDeviceNotifications() - { - BroadcastDeviceInterface bdi = new BroadcastDeviceInterface(); - bdi.Size = BlittableValueType.StrideOf(bdi); - bdi.DeviceType = DeviceBroadcastType.INTERFACE; - bdi.ClassGuid = DeviceInterfaceHid; - unsafe - { - DevNotifyHandle = Functions.RegisterDeviceNotification(Parent.WindowHandle, - new IntPtr((void*)&bdi), DeviceNotification.WINDOW_HANDLE); - } - if (DevNotifyHandle == IntPtr.Zero) - Debug.Print("[Warning] Failed to register for device notifications. Error: {0}", Marshal.GetLastWin32Error()); - } - - #endregion - - #region WindowProcedureImplementation - - // Processes the input Windows Message, routing the buffer to the correct Keyboard, Mouse or HID. - static IntPtr WindowProcedureImplementation(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam) - { - switch (message) - { - case WindowMessage.INPUT: - int size = 0; - // Get the size of the input buffer - Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, - IntPtr.Zero, ref size, API.RawInputHeaderSize); - - // Read the actual raw input structure - if (size == Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT, - out data, ref size, API.RawInputHeaderSize)) - { - switch (data.Header.Type) - { - case RawInputDeviceType.KEYBOARD: - if (keyboardDriver.ProcessKeyboardEvent(data)) - return IntPtr.Zero; - break; - - case RawInputDeviceType.MOUSE: - if (mouseDriver.ProcessMouseEvent(data)) - return IntPtr.Zero; - break; - - case RawInputDeviceType.HID: - break; - } - } - break; - - case WindowMessage.DEVICECHANGE: - mouseDriver.RefreshDevices(); - break; - } - return Functions.CallWindowProc(OldWndProc, handle, message, wParam, lParam); - } - - #endregion - - #region ProcessEvents - - static void ProcessEvents() - { - INativeWindow native = ConstructMessageWindow(); - - MSG msg = new MSG(); - while (native.Exists) - { - int ret = Functions.GetMessage(ref msg, Parent.WindowHandle, 0, 0); - if (ret == -1) - { - throw new PlatformException(String.Format( - "An error happened while processing the message queue. Windows error: {0}", - Marshal.GetLastWin32Error())); - } - - Functions.TranslateMessage(ref msg); - Functions.DispatchMessage(ref msg); - } - } - - #endregion - - #endregion - #region IInputDriver2 Members - public IMouseDriver2 MouseDriver + public override IKeyboardDriver2 KeyboardDriver { - get { return mouseDriver; } + get { return keyboard_driver; } } - public IKeyboardDriver2 KeyboardDriver + public override IMouseDriver2 MouseDriver { - get { return keyboardDriver; } + get { return mouse_driver; } } - public IGamePadDriver GamePadDriver + public override IGamePadDriver GamePadDriver { - get { return joystickDriver; } - } - - #endregion - - #region IDisposable Members - - private bool disposed; - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - private void Dispose(bool manual) - { - if (!disposed) - { - if (manual) - { - } - - Functions.UnregisterDeviceNotification(DevNotifyHandle); - disposed = true; - } - } - - ~WinRawInput() - { - Debug.Print("[Warning] Resource leaked: {0}.", this); - Dispose(false); + get { return joystick_driver; } } #endregion diff --git a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs index e2d39286..a1388e11 100644 --- a/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs +++ b/Source/OpenTK/Platform/Windows/WinRawKeyboard.cs @@ -36,6 +36,7 @@ namespace OpenTK.Platform.Windows { sealed class WinRawKeyboard : IKeyboardDriver2 { + static readonly WinKeyMap KeyMap = new WinKeyMap(); readonly List keyboards = new List(); readonly List names = new List(); readonly Dictionary rawids = new Dictionary(); @@ -46,7 +47,7 @@ namespace OpenTK.Platform.Windows public WinRawKeyboard(IntPtr windowHandle) { - Debug.WriteLine("Initializing keyboard driver (WinRawKeyboard)."); + Debug.WriteLine("Using WinRawKeyboard."); Debug.Indent(); this.window = windowHandle; @@ -157,14 +158,14 @@ namespace OpenTK.Platform.Windows break; default: - if (!WMInput.KeyMap.ContainsKey(rin.Data.Keyboard.VKey)) + if (!KeyMap.ContainsKey(rin.Data.Keyboard.VKey)) { Debug.Print("Virtual key {0} ({1}) not mapped.", rin.Data.Keyboard.VKey, (int)rin.Data.Keyboard.VKey); } else { - keyboard[WMInput.KeyMap[rin.Data.Keyboard.VKey]] = pressed; + keyboard[KeyMap[rin.Data.Keyboard.VKey]] = pressed; processed = true; } break; diff --git a/Source/OpenTK/Platform/Windows/WinRawMouse.cs b/Source/OpenTK/Platform/Windows/WinRawMouse.cs index ce3bc374..637b1cba 100644 --- a/Source/OpenTK/Platform/Windows/WinRawMouse.cs +++ b/Source/OpenTK/Platform/Windows/WinRawMouse.cs @@ -50,7 +50,7 @@ namespace OpenTK.Platform.Windows public WinRawMouse(IntPtr window) { - Debug.WriteLine("Initializing mouse driver (WinRawMouse)."); + Debug.WriteLine("Using WinRawMouse."); Debug.Indent(); if (window == IntPtr.Zero)