2009-02-22 10:43:35 +00:00
|
|
|
|
#region --- License ---
|
|
|
|
|
/* Copyright (c) 2006, 2007 Stefanos Apostolopoulos
|
|
|
|
|
* See license.txt for license info
|
|
|
|
|
*/
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Diagnostics;
|
2010-10-22 13:41:42 +00:00
|
|
|
|
using System.Drawing;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
using Microsoft.Win32;
|
2010-10-22 13:41:42 +00:00
|
|
|
|
using OpenTK.Input;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
|
|
|
|
namespace OpenTK.Platform.Windows
|
|
|
|
|
{
|
2010-03-11 22:53:11 +00:00
|
|
|
|
/// \internal
|
2009-02-22 10:43:35 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Contains methods to register for and process mouse WM_INPUT messages.
|
|
|
|
|
/// </summary>
|
2010-10-22 13:41:42 +00:00
|
|
|
|
internal class WinRawMouse : IMouseDriver
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
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;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
|
|
|
|
internal WinRawMouse()
|
|
|
|
|
{
|
|
|
|
|
Debug.WriteLine("Initializing mouse driver (WinRawMouse).");
|
|
|
|
|
Debug.Indent();
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
// 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();
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
RegisterDevices(window, out mice, out rawids);
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
|
|
|
|
Debug.Unindent();
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
#region IMouseDriver Members
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
public IList<MouseDevice> Mouse { get { throw new NotImplementedException(); } }
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
2010-10-20 14:58:38 +00:00
|
|
|
|
public MouseState GetState()
|
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
native.ProcessEvents();
|
|
|
|
|
if (mice.Count > 0)
|
|
|
|
|
return mice[0];
|
|
|
|
|
else
|
|
|
|
|
return new MouseState();
|
2010-10-20 14:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public MouseState GetState(int index)
|
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
native.ProcessEvents();
|
|
|
|
|
if (index < mice.Count)
|
|
|
|
|
return mice[index];
|
|
|
|
|
else
|
|
|
|
|
return new MouseState();
|
2010-10-20 14:58:38 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
#endregion
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
static int RegisterDevices(IntPtr window, out List<MouseState> mice, out Dictionary<ContextHandle, int> rawids)
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
|
|
|
|
int count = WinRawInput.DeviceCount;
|
2010-10-22 13:41:42 +00:00
|
|
|
|
mice = new List<MouseState>();
|
|
|
|
|
rawids = new Dictionary<ContextHandle, int>();
|
|
|
|
|
|
2009-02-22 10:43:35 +00:00
|
|
|
|
RawInputDeviceList[] ridl = new RawInputDeviceList[count];
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
ridl[i] = new RawInputDeviceList();
|
|
|
|
|
Functions.GetRawInputDeviceList(ridl, ref count, API.RawInputDeviceListSize);
|
|
|
|
|
|
|
|
|
|
// Discover mouse devices:
|
|
|
|
|
for (int i = 0; i < count; i++)
|
|
|
|
|
{
|
|
|
|
|
uint size = 0;
|
|
|
|
|
Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
|
|
|
|
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size);
|
|
|
|
|
Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size);
|
|
|
|
|
string name = Marshal.PtrToStringAnsi(name_ptr);
|
|
|
|
|
Marshal.FreeHGlobal(name_ptr);
|
|
|
|
|
|
|
|
|
|
if (name.ToLower().Contains("root"))
|
|
|
|
|
{
|
|
|
|
|
// This is a terminal services device, skip it.
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else if (ridl[i].Type == RawInputDeviceType.MOUSE || ridl[i].Type == RawInputDeviceType.HID)
|
|
|
|
|
{
|
|
|
|
|
// This is a mouse or a USB mouse device. In the latter case, discover if it really is a
|
|
|
|
|
// mouse device by qeurying the registry.
|
|
|
|
|
|
|
|
|
|
// remove the \??\
|
|
|
|
|
name = name.Substring(4);
|
|
|
|
|
|
|
|
|
|
string[] split = name.Split('#');
|
|
|
|
|
|
|
|
|
|
string id_01 = split[0]; // ACPI (Class code)
|
|
|
|
|
string id_02 = split[1]; // PNP0303 (SubClass code)
|
|
|
|
|
string id_03 = split[2]; // 3&13c0b0c5&0 (Protocol code)
|
|
|
|
|
// The final part is the class GUID and is not needed here
|
|
|
|
|
|
|
|
|
|
string findme = string.Format(
|
|
|
|
|
@"System\CurrentControlSet\Enum\{0}\{1}\{2}",
|
|
|
|
|
id_01, id_02, id_03);
|
|
|
|
|
|
|
|
|
|
RegistryKey regkey = Registry.LocalMachine.OpenSubKey(findme);
|
|
|
|
|
|
|
|
|
|
string deviceDesc = (string)regkey.GetValue("DeviceDesc");
|
|
|
|
|
deviceDesc = deviceDesc.Substring(deviceDesc.LastIndexOf(';') + 1);
|
|
|
|
|
string deviceClass = (string)regkey.GetValue("Class");
|
|
|
|
|
|
|
|
|
|
if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("mouse"))
|
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
// Register the device:
|
2009-02-22 10:43:35 +00:00
|
|
|
|
RawInputDeviceInfo info = new RawInputDeviceInfo();
|
|
|
|
|
int devInfoSize = API.RawInputDeviceInfoSize;
|
|
|
|
|
Functions.GetRawInputDeviceInfo(ridl[i].Device, RawInputDeviceInfoEnum.DEVICEINFO,
|
|
|
|
|
info, ref devInfoSize);
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
mice.Add(RegisterRawDevice(deviceDesc, window));
|
|
|
|
|
rawids.Add(new ContextHandle(ridl[i].Device), mice.Count - 1);
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return count;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
static MouseState RegisterRawDevice(string device, IntPtr window)
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
MouseState state = new MouseState();
|
2009-02-22 10:43:35 +00:00
|
|
|
|
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();
|
|
|
|
|
rid[0].UsagePage = 1;
|
|
|
|
|
rid[0].Usage = 2;
|
|
|
|
|
rid[0].Flags = RawInputDeviceFlags.INPUTSINK;
|
|
|
|
|
rid[0].Target = window;
|
|
|
|
|
|
|
|
|
|
if (!Functions.RegisterRawInputDevices(rid, 1, API.RawInputDeviceSize))
|
|
|
|
|
{
|
|
|
|
|
throw new ApplicationException(
|
|
|
|
|
String.Format(
|
|
|
|
|
"Raw input registration failed with error: {0}. Device: {1}",
|
|
|
|
|
Marshal.GetLastWin32Error(),
|
|
|
|
|
rid[0].ToString())
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
Debug.Print("Registered mouse {0}", device);
|
2009-11-01 12:44:38 +00:00
|
|
|
|
Point p = new Point();
|
2009-02-22 10:43:35 +00:00
|
|
|
|
if (Functions.GetCursorPos(ref p))
|
2010-10-22 13:41:42 +00:00
|
|
|
|
{
|
|
|
|
|
state.X = p.X;
|
|
|
|
|
state.Y = p.Y;
|
|
|
|
|
}
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
return state;
|
|
|
|
|
}
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
IntPtr WindowProcedure(IntPtr handle, WindowMessage message, IntPtr wParam, IntPtr lParam)
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
switch (message)
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
case WindowMessage.INPUT:
|
|
|
|
|
int expected_size = 0, real_size = 0;
|
|
|
|
|
RawInput data = new RawInput();
|
|
|
|
|
|
|
|
|
|
// Get the size of the input buffer
|
|
|
|
|
Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
|
|
|
|
|
IntPtr.Zero, ref expected_size, API.RawInputHeaderSize);
|
|
|
|
|
|
|
|
|
|
// Read the actual data
|
|
|
|
|
unsafe
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
real_size = Functions.GetRawInputData(lParam, GetRawInputDataEnum.INPUT,
|
|
|
|
|
&data, ref expected_size, API.RawInputHeaderSize);
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
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;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
|
|
|
|
default:
|
2010-10-22 13:41:42 +00:00
|
|
|
|
return Functions.CallWindowProc(OldWndProc, handle, message, wParam, lParam);
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
bool ProcessEvent(IntPtr device, RawMouse raw)
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
if (mice.Count == 0)
|
|
|
|
|
return false;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
ContextHandle handle = new ContextHandle(device);
|
|
|
|
|
MouseState mouse;
|
|
|
|
|
if (rawids.ContainsKey(handle))
|
|
|
|
|
mouse = mice[rawids[handle]];
|
|
|
|
|
else
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0)
|
|
|
|
|
mouse.WheelPrecise += (short)raw.ButtonData / 120.0f;
|
|
|
|
|
|
|
|
|
|
if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0)
|
2009-02-22 10:43:35 +00:00
|
|
|
|
{
|
2010-10-22 13:41:42 +00:00
|
|
|
|
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;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
mice[rawids[handle]] = mouse;
|
|
|
|
|
return true;
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-22 13:41:42 +00:00
|
|
|
|
|
2009-02-22 10:43:35 +00:00
|
|
|
|
}
|
|
|
|
|
}
|