Initial implementation of raw mouse input on Windows.

This commit is contained in:
the_fiddler 2010-10-22 13:41:42 +00:00
parent 687594db4c
commit ef6c910d30
4 changed files with 166 additions and 204 deletions

View file

@ -851,6 +851,9 @@ namespace OpenTK.Platform.Windows
[DllImport("user32.dll", SetLastError = true)]
public static extern BOOL BringWindowToTop(HWND hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern BOOL SetParent(HWND child, HWND newParent);
#endregion
#region Display settings
@ -1353,17 +1356,6 @@ namespace OpenTK.Platform.Windows
/// <remarks>
/// GetRawInputData gets the raw input one RawInput structure at a time. In contrast, GetRawInputBuffer gets an array of RawInput structures.
/// </remarks>
[CLSCompliant(false)]
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern UINT GetRawInputData(
HRAWINPUT RawInput,
GetRawInputDataEnum Command,
[Out] LPVOID Data,
[In, Out] ref UINT Size,
UINT SizeHeader
);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern INT GetRawInputData(
@ -1395,17 +1387,6 @@ namespace OpenTK.Platform.Windows
/// <remarks>
/// GetRawInputData gets the raw input one RawInput structure at a time. In contrast, GetRawInputBuffer gets an array of RawInput structures.
/// </remarks>
[CLSCompliant(false)]
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern UINT GetRawInputData(
HRAWINPUT RawInput,
GetRawInputDataEnum Command,
/*[MarshalAs(UnmanagedType.LPStruct)]*/ [Out] out RawInput Data,
[In, Out] ref UINT Size,
UINT SizeHeader
);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
internal static extern INT GetRawInputData(
@ -1416,6 +1397,16 @@ namespace OpenTK.Platform.Windows
INT SizeHeader
);
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport("user32.dll", SetLastError = true)]
unsafe internal static extern INT GetRawInputData(
HRAWINPUT RawInput,
GetRawInputDataEnum Command,
RawInput* Data,
[In, Out] ref INT Size,
INT SizeHeader
);
#endregion
#region IntPtr NextRawInputStructure(IntPtr data)
@ -1487,7 +1478,7 @@ namespace OpenTK.Platform.Windows
#region --- Constants ---
internal struct Constants
static class Constants
{
// Found in winuser.h
internal const int KEYBOARD_OVERRUN_MAKE_CODE = 0xFF;
@ -1574,6 +1565,8 @@ namespace OpenTK.Platform.Windows
// (found in winuser.h)
internal const int ENUM_REGISTRY_SETTINGS = -2;
internal const int ENUM_CURRENT_SETTINGS = -1;
internal static readonly IntPtr MESSAGE_ONLY = new IntPtr(-3);
}
#endregion
@ -2231,25 +2224,11 @@ namespace OpenTK.Platform.Windows
/// <para>To get device specific information, call GetRawInputDeviceInfo with the hDevice from RAWINPUTHEADER.</para>
/// <para>Raw input is available only when the application calls RegisterRawInputDevices with valid device specifications.</para>
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
internal struct RawInput
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct RawInput
{
internal RawInputHeader Header;
internal RawInputData Data;
internal byte[] ToByteArray()
{
unsafe
{
byte[] dump = new byte[API.RawInputSize];
fixed (RawInputDeviceType* ptr = &Header.Type)
{
for (int i = 0; i < API.RawInputSize; i++)
dump[i] = *((byte*)ptr + i);
return dump;
}
}
}
public RawInputHeader Header;
public RawInputData Data;
}
[StructLayout(LayoutKind.Explicit)]
@ -2350,38 +2329,9 @@ namespace OpenTK.Platform.Windows
/// <summary>
/// Contains information about the state of the mouse.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack=1)]
[StructLayout(LayoutKind.Explicit)]
internal struct RawMouse
{
//internal RawMouseFlags Flags; // USHORT in winuser.h, but only INT works -- USHORT returns 0.
USHORT flags;
byte for_alignment; // Not used -- used for alignment
/// <summary>
/// Reserved.
/// </summary>
//ULONG Buttons;
internal USHORT buttonFlags;
/// <summary>
/// If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta.
/// </summary>
internal USHORT ButtonData;// { get { return (USHORT)((Buttons & 0xFFFF0000) >> 16); } }
/// <summary>
/// Raw state of the mouse buttons.
/// </summary>
internal ULONG RawButtons;
/// <summary>
/// Motion in the X direction. This is signed relative motion or absolute motion, depending on the value of usFlags.
/// </summary>
internal LONG LastX;
/// <summary>
/// Motion in the Y direction. This is signed relative motion or absolute motion, depending on the value of usFlags.
/// </summary>
internal LONG LastY;
/// <summary>
/// Device-specific additional information for the event.
/// </summary>
internal ULONG ExtraInformation;
/// <summary>
/// Mouse state. This member can be any reasonable combination of the following.
/// MOUSE_ATTRIBUTES_CHANGED
@ -2393,12 +2343,34 @@ namespace OpenTK.Platform.Windows
/// MOUSE_VIRTUAL_DESKTOP
/// Mouse coordinates are mapped to the virtual desktop (for a multiple monitor system).
/// </summary>
internal RawMouseFlags Flags { get { return (RawMouseFlags)(flags); } }
[FieldOffset(0)] public RawMouseFlags Flags; // USHORT in winuser.h, but only INT works -- USHORT returns 0.
[FieldOffset(4)] public RawInputMouseState ButtonFlags;
/// <summary>
/// Transition state of the mouse buttons.
/// If usButtonFlags is RI_MOUSE_WHEEL, this member is a signed value that specifies the wheel delta.
/// </summary>
internal RawInputMouseState ButtonFlags { get { return (RawInputMouseState)(buttonFlags); } }
[FieldOffset(6)] public USHORT ButtonData;
/// <summary>
/// Raw state of the mouse buttons.
/// </summary>
[FieldOffset(8)] public ULONG RawButtons;
/// <summary>
/// Motion in the X direction. This is signed relative motion or absolute motion, depending on the value of usFlags.
/// </summary>
[FieldOffset(12)] public LONG LastX;
/// <summary>
/// Motion in the Y direction. This is signed relative motion or absolute motion, depending on the value of usFlags.
/// </summary>
[FieldOffset(16)] public LONG LastY;
/// <summary>
/// Device-specific additional information for the event.
/// </summary>
[FieldOffset(20)] public ULONG ExtraInformation;
}
#endregion
@ -2483,10 +2455,11 @@ namespace OpenTK.Platform.Windows
/// Number of HID inputs in bRawData.
/// </summary>
internal DWORD Count;
/// <summary>
/// Raw input data as an array of bytes.
/// </summary>
internal BYTE RawData;
// The RawData field must be marshalled manually.
///// <summary>
///// Raw input data as an array of bytes.
///// </summary>
//internal IntPtr RawData;
}
#endregion
@ -3239,6 +3212,7 @@ namespace OpenTK.Platform.Windows
/// <summary>
/// Mouse indicator flags (found in winuser.h).
/// </summary>
[Flags]
internal enum RawMouseFlags : ushort
{
/// <summary>

View file

@ -83,7 +83,10 @@ namespace OpenTK.Platform.Windows
public virtual OpenTK.Input.IMouseDriver CreateMouseDriver()
{
throw new NotImplementedException();
if (System.Environment.OSVersion.Version.Major >= 5)
return new WinRawMouse();
else
return new WMInput(null);
}
#endregion

View file

@ -38,9 +38,10 @@ namespace OpenTK.Platform.Windows
Debug.Indent();
AssignHandle(parent.WindowHandle);
WinWindowInfo win = new WinWindowInfo(this.Handle, parent);
Debug.Print("Input window attached to parent {0}", parent);
keyboardDriver = new WinRawKeyboard(this.Handle);
mouseDriver = new WinRawMouse(this.Handle);
mouseDriver = new WinRawMouse();
Debug.Unindent();
@ -93,9 +94,7 @@ namespace OpenTK.Platform.Windows
return;
case RawInputDeviceType.MOUSE:
if (!mouseDriver.ProcessEvent(data))
Functions.DefRawInputProc(ref data, 1, (uint)API.RawInputHeaderSize);
return;
throw new NotSupportedException();
case RawInputDeviceType.HID:
Functions.DefRawInputProc(ref data, 1, (uint)API.RawInputHeaderSize);

View file

@ -6,12 +6,11 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenTK.Input;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Win32;
using System.Drawing;
using OpenTK.Input;
namespace OpenTK.Platform.Windows
{
@ -19,54 +18,64 @@ namespace OpenTK.Platform.Windows
/// <summary>
/// Contains methods to register for and process mouse WM_INPUT messages.
/// </summary>
internal class WinRawMouse : IMouseDriver, IDisposable
internal class WinRawMouse : IMouseDriver
{
private List<MouseDevice> mice = new List<MouseDevice>();
private IntPtr window;
#region --- Constructors ---
List<MouseState> mice;
Dictionary<ContextHandle, int> rawids; // ContextHandle instead of IntPtr for fast dictionary access
readonly INativeWindow native;
readonly IntPtr window;
readonly WindowProcedure WndProc;
readonly IntPtr OldWndProc;
internal WinRawMouse()
: this(IntPtr.Zero)
{
}
internal WinRawMouse(IntPtr windowHandle)
{
Debug.WriteLine("Initializing mouse driver (WinRawMouse).");
Debug.Indent();
this.window = windowHandle;
// Create a new message-only window to retrieve WM_INPUT messages.
native = new NativeWindow();
window = (native.WindowInfo as WinWindowInfo).WindowHandle;
//Functions.SetParent(window, Constants.MESSAGE_ONLY);
// Subclass the window to retrieve the events we are interested in.
WndProc = WindowProcedure;
OldWndProc = Functions.SetWindowLong(window, WndProc);
native.ProcessEvents();
RegisterDevices();
RegisterDevices(window, out mice, out rawids);
Debug.Unindent();
}
#endregion
#region IMouseDriver Members
#region --- IMouseDriver Members ---
public IList<MouseDevice> Mouse
{
get { return mice; }
}
public IList<MouseDevice> Mouse { get { throw new NotImplementedException(); } }
public MouseState GetState()
{
throw new NotImplementedException();
native.ProcessEvents();
if (mice.Count > 0)
return mice[0];
else
return new MouseState();
}
public MouseState GetState(int index)
{
throw new NotImplementedException();
native.ProcessEvents();
if (index < mice.Count)
return mice[index];
else
return new MouseState();
}
#region public int RegisterDevices()
#endregion
public int RegisterDevices()
static int RegisterDevices(IntPtr window, out List<MouseState> mice, out Dictionary<ContextHandle, int> rawids)
{
int count = WinRawInput.DeviceCount;
mice = new List<MouseState>();
rawids = new Dictionary<ContextHandle, int>();
RawInputDeviceList[] ridl = new RawInputDeviceList[count];
for (int i = 0; i < count; i++)
ridl[i] = new RawInputDeviceList();
@ -114,22 +123,14 @@ namespace OpenTK.Platform.Windows
if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("mouse"))
{
OpenTK.Input.MouseDevice mouse = new OpenTK.Input.MouseDevice();
mouse.Description = deviceDesc;
// Register the keyboard:
// Register the device:
RawInputDeviceInfo info = new RawInputDeviceInfo();
int devInfoSize = API.RawInputDeviceInfoSize;
Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICEINFO,
info, ref devInfoSize);
mouse.NumberOfButtons = info.Device.Mouse.NumberOfButtons;
mouse.NumberOfWheels = info.Device.Mouse.HasHorizontalWheel ? 1 : 0;
mouse.DeviceID = ridl[i].Device;//(IntPtr)info.Device.Mouse.Id;
this.RegisterRawDevice(mouse);
mice.Add(mouse);
mice.Add(RegisterRawDevice(deviceDesc, window));
rawids.Add(new ContextHandle(ridl[i].Device), mice.Count - 1);
}
}
}
@ -137,14 +138,9 @@ namespace OpenTK.Platform.Windows
return count;
}
#endregion
#endregion
#region internal void RegisterRawDevice(OpenTK.Input.Mouse mouse)
internal void RegisterRawDevice(OpenTK.Input.MouseDevice mouse)
static MouseState RegisterRawDevice(string device, IntPtr window)
{
MouseState state = new MouseState();
RawInputDevice[] rid = new RawInputDevice[1];
// Mouse is 1/2 (page/id). See http://www.microsoft.com/whdc/device/input/HID_HWID.mspx
rid[0] = new RawInputDevice();
@ -164,106 +160,96 @@ namespace OpenTK.Platform.Windows
}
else
{
Debug.Print("Registered mouse {0}", mouse.ToString());
Debug.Print("Registered mouse {0}", device);
Point p = new Point();
if (Functions.GetCursorPos(ref p))
mouse.Position = p;
{
state.X = p.X;
state.Y = p.Y;
}
}
return state;
}
#endregion
#region internal bool ProcessEvent(API.RawInput rin)
/// <summary>
/// Processes raw input events.
/// </summary>
/// <param name="rin"></param>
/// <returns></returns>
internal bool ProcessEvent(RawInput rin)
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
{
//MouseDevice mouse = mice.Find(delegate(MouseDevice m)
//{
// return m.DeviceID == rin.Header.Device;
//});
MouseDevice mouse;
if (mice.Count > 0) mouse = mice[0];
else return false;
switch (rin.Header.Type)
switch (message)
{
case RawInputDeviceType.MOUSE:
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0) mouse[MouseButton.Left] = true;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0) mouse[MouseButton.Left] = false;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0) mouse[MouseButton.Right] = true;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0) mouse[MouseButton.Right] = false;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0) mouse[MouseButton.Middle] = true;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0) mouse[MouseButton.Middle] = false;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0) mouse[MouseButton.Button1] = true;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0) mouse[MouseButton.Button1] = false;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0) mouse[MouseButton.Button2] = true;
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0) mouse[MouseButton.Button2] = false;
case WindowMessage.INPUT:
int expected_size = 0, real_size = 0;
RawInput data = new RawInput();
if ((rin.Data.Mouse.ButtonFlags & RawInputMouseState.WHEEL) != 0)
mouse.Wheel += (short)rin.Data.Mouse.ButtonData / 120;
if ((rin.Data.Mouse.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0)
{
mouse.Position = new Point(rin.Data.Mouse.LastX, rin.Data.Mouse.LastY);
}
else
{ // Seems like MOUSE_MOVE_RELATIVE is the default, unless otherwise noted.
mouse.Position = new Point(mouse.X + rin.Data.Mouse.LastX,
mouse.Y + rin.Data.Mouse.LastY);
}
if ((rin.Data.Mouse.Flags & RawMouseFlags.MOUSE_VIRTUAL_DESKTOP) != 0)
Debug.WriteLine(String.Format("Mouse {0} defines MOUSE_VIRTUAL_DESKTOP flag, please report at http://www.opentk.com", mouse.ToString()));
// Get the size of the input buffer
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
IntPtr.Zero, ref expected_size, API.RawInputHeaderSize);
return true;
// Read the actual data
unsafe
{
real_size = Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
&data, ref expected_size, API.RawInputHeaderSize);
}
if (real_size == expected_size)
{
if (data.Header.Type == RawInputDeviceType.MOUSE)
{
if (ProcessEvent(data.Header.Device, data.Data.Mouse))
{
return IntPtr.Zero;
}
}
}
// We didn't handle this message after all, give it back to the old WndProc.
goto default;
default:
throw new ApplicationException("WinRawMouse driver received invalid data.");
return Functions.CallWindowProc(OldWndProc, handle, message, wParam, lParam);
}
}
#endregion
#region public void Poll()
public void Poll()
bool ProcessEvent(IntPtr device, RawMouse raw)
{
}
if (mice.Count == 0)
return false;
#endregion
ContextHandle handle = new ContextHandle(device);
MouseState mouse;
if (rawids.ContainsKey(handle))
mouse = mice[rawids[handle]];
else
return false;
#region --- IDisposable Members ---
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0) mouse.EnableBit((int)MouseButton.Left);
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0) mouse.DisableBit((int)MouseButton.Left);
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0) mouse.EnableBit((int)MouseButton.Right);
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0) mouse.DisableBit((int)MouseButton.Right);
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0) mouse.EnableBit((int)MouseButton.Middle);
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0) mouse.DisableBit((int)MouseButton.Middle);
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0) mouse.EnableBit((int)MouseButton.Button1);
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0) mouse.DisableBit((int)MouseButton.Button1);
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0) mouse.EnableBit((int)MouseButton.Button2);
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0) mouse.DisableBit((int)MouseButton.Button2);
private bool disposed;
if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0)
mouse.WheelPrecise += (short)raw.ButtonData / 120.0f;
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool manual)
{
if (!disposed)
if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0)
{
if (manual)
{
mice.Clear();
}
disposed = true;
mouse.X = raw.LastX;
mouse.Y = raw.LastY;
}
else
{ // Seems like MOUSE_MOVE_RELATIVE is the default, unless otherwise noted.
mouse.X += raw.LastX;
mouse.Y += raw.LastY;
}
mice[rawids[handle]] = mouse;
return true;
}
~WinRawMouse()
{
Dispose(false);
}
#endregion
}
}