1b3b510376
Fixes crashes when using OpenTK over the Remote Desktop Client for Mac (version 2010).
350 lines
13 KiB
C#
350 lines
13 KiB
C#
#region License
|
|
//
|
|
// The Open Toolkit Library License
|
|
//
|
|
// Copyright (c) 2006 - 2010 the Open Toolkit library.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
// in the Software without restriction, including without limitation the rights to
|
|
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
// the Software, and to permit persons to whom the Software is furnished to do
|
|
// so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in all
|
|
// copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
// OTHER DEALINGS IN THE SOFTWARE.
|
|
//
|
|
#endregion
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using Microsoft.Win32;
|
|
using OpenTK.Input;
|
|
|
|
namespace OpenTK.Platform.Windows
|
|
{
|
|
/// \internal
|
|
/// <summary>
|
|
/// Contains methods to register for and process mouse WM_INPUT messages.
|
|
/// </summary>
|
|
sealed class WinRawMouse : IMouseDriver2
|
|
{
|
|
readonly List<MouseState> mice = new List<MouseState>();
|
|
readonly List<string> names = new List<string>();
|
|
readonly Dictionary<ContextHandle, int> rawids = new Dictionary<ContextHandle, int>();
|
|
readonly IntPtr Window;
|
|
readonly object UpdateLock = new object();
|
|
|
|
#region Constructors
|
|
|
|
public WinRawMouse(IntPtr window)
|
|
{
|
|
Debug.WriteLine("Using WinRawMouse.");
|
|
Debug.Indent();
|
|
|
|
if (window == IntPtr.Zero)
|
|
throw new ArgumentNullException("window");
|
|
|
|
Window = window;
|
|
RefreshDevices();
|
|
|
|
Debug.Unindent();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Members
|
|
|
|
public void RefreshDevices()
|
|
{
|
|
lock (UpdateLock)
|
|
{
|
|
// Mark all devices as disconnected. We will check which of those
|
|
// are connected later on.
|
|
for (int i = 0; i < mice.Count; i++)
|
|
{
|
|
MouseState state = mice[i];
|
|
state.IsConnected = false;
|
|
mice[i] = state;
|
|
}
|
|
|
|
int count = WinRawInput.DeviceCount;
|
|
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
|
|
foreach (RawInputDeviceList dev in ridl)
|
|
{
|
|
ContextHandle id = new ContextHandle(dev.Device);
|
|
if (rawids.ContainsKey(id))
|
|
{
|
|
// Device already registered, mark as connected
|
|
MouseState state = mice[rawids[id]];
|
|
state.IsConnected = true;
|
|
mice[rawids[id]] = state;
|
|
continue;
|
|
}
|
|
|
|
// Unregistered device, find what it is
|
|
string name = GetDeviceName(dev);
|
|
if (name.ToLower().Contains("root"))
|
|
{
|
|
// This is a terminal services device, skip it.
|
|
continue;
|
|
}
|
|
else if (dev.Type == RawInputDeviceType.MOUSE || dev.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.
|
|
RegistryKey regkey = FindRegistryKey(name);
|
|
if (regkey == null)
|
|
continue;
|
|
|
|
string deviceDesc = (string)regkey.GetValue("DeviceDesc");
|
|
string deviceClass = (string)regkey.GetValue("Class") as string;
|
|
if(deviceClass == null)
|
|
{
|
|
// Added to address OpenTK issue 3198 with mouse on Windows 8
|
|
string deviceClassGUID = (string)regkey.GetValue("ClassGUID");
|
|
RegistryKey classGUIDKey = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Class\" + deviceClassGUID);
|
|
deviceClass = classGUIDKey != null ? (string) classGUIDKey.GetValue("Class") : string.Empty;
|
|
}
|
|
|
|
// deviceDesc remained null on a new Win7 system - not sure why.
|
|
// Since the description is not vital information, use a dummy description
|
|
// when that happens.
|
|
if (String.IsNullOrEmpty(deviceDesc))
|
|
deviceDesc = "Windows Mouse " + mice.Count;
|
|
else
|
|
deviceDesc = deviceDesc.Substring(deviceDesc.LastIndexOf(';') + 1);
|
|
|
|
if (!String.IsNullOrEmpty(deviceClass) && deviceClass.ToLower().Equals("mouse"))
|
|
{
|
|
if (!rawids.ContainsKey(new ContextHandle(dev.Device)))
|
|
{
|
|
// Register the device:
|
|
RawInputDeviceInfo info = new RawInputDeviceInfo();
|
|
int devInfoSize = API.RawInputDeviceInfoSize;
|
|
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICEINFO,
|
|
info, ref devInfoSize);
|
|
|
|
RegisterRawDevice(Window, deviceDesc);
|
|
MouseState state = new MouseState();
|
|
state.IsConnected = true;
|
|
mice.Add(state);
|
|
names.Add(deviceDesc);
|
|
rawids.Add(new ContextHandle(dev.Device), mice.Count - 1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public bool ProcessMouseEvent(RawInput rin)
|
|
{
|
|
RawMouse raw = rin.Data.Mouse;
|
|
ContextHandle handle = new ContextHandle(rin.Header.Device);
|
|
|
|
MouseState mouse;
|
|
if (!rawids.ContainsKey(handle))
|
|
{
|
|
RefreshDevices();
|
|
}
|
|
|
|
if (mice.Count == 0)
|
|
return false;
|
|
|
|
// Note:For some reason, my Microsoft Digital 3000 keyboard reports 0
|
|
// as rin.Header.Device for the "zoom-in/zoom-out" buttons.
|
|
// That's problematic, because no device has a "0" id.
|
|
// As a workaround, we'll add those buttons to the first device (if any).
|
|
int mouse_handle = rawids.ContainsKey(handle) ? rawids[handle] : 0;
|
|
mouse = mice[mouse_handle];
|
|
|
|
// Set and release capture of the mouse to fix http://www.opentk.com/node/2133, Patch by Artfunkel
|
|
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_DOWN) != 0){
|
|
mouse.EnableBit((int)MouseButton.Left);
|
|
Functions.SetCapture(Window);
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.LEFT_BUTTON_UP) != 0)
|
|
{
|
|
mouse.DisableBit((int)MouseButton.Left);
|
|
Functions.ReleaseCapture();
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_DOWN) != 0)
|
|
{
|
|
mouse.EnableBit((int)MouseButton.Right);
|
|
Functions.SetCapture(Window);
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.RIGHT_BUTTON_UP) != 0)
|
|
{
|
|
mouse.DisableBit((int)MouseButton.Right);
|
|
Functions.ReleaseCapture();
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_DOWN) != 0)
|
|
{
|
|
mouse.EnableBit((int)MouseButton.Middle);
|
|
Functions.SetCapture(Window);
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.MIDDLE_BUTTON_UP) != 0)
|
|
{
|
|
mouse.DisableBit((int)MouseButton.Middle);
|
|
Functions.ReleaseCapture();
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_DOWN) != 0)
|
|
{
|
|
mouse.EnableBit((int)MouseButton.Button1);
|
|
Functions.SetCapture(Window);
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_4_UP) != 0)
|
|
{
|
|
mouse.DisableBit((int)MouseButton.Button1);
|
|
Functions.ReleaseCapture();
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_DOWN) != 0)
|
|
{
|
|
mouse.EnableBit((int)MouseButton.Button2);
|
|
Functions.SetCapture(Window);
|
|
}
|
|
if ((raw.ButtonFlags & RawInputMouseState.BUTTON_5_UP) != 0)
|
|
{
|
|
mouse.DisableBit((int)MouseButton.Button2);
|
|
Functions.ReleaseCapture();
|
|
}
|
|
|
|
if ((raw.ButtonFlags & RawInputMouseState.WHEEL) != 0)
|
|
mouse.WheelPrecise += (short)raw.ButtonData / 120.0f;
|
|
|
|
if ((raw.Flags & RawMouseFlags.MOUSE_MOVE_ABSOLUTE) != 0)
|
|
{
|
|
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;
|
|
}
|
|
|
|
lock (UpdateLock)
|
|
{
|
|
mice[mouse_handle] = mouse;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Members
|
|
|
|
static string GetDeviceName(RawInputDeviceList dev)
|
|
{
|
|
// get name size
|
|
uint size = 0;
|
|
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, IntPtr.Zero, ref size);
|
|
|
|
// get actual name
|
|
IntPtr name_ptr = Marshal.AllocHGlobal((IntPtr)size);
|
|
Functions.GetRawInputDeviceInfo(dev.Device, RawInputDeviceInfoEnum.DEVICENAME, name_ptr, ref size);
|
|
string name = Marshal.PtrToStringAnsi(name_ptr);
|
|
Marshal.FreeHGlobal(name_ptr);
|
|
|
|
return name;
|
|
}
|
|
|
|
static RegistryKey FindRegistryKey(string name)
|
|
{
|
|
if (name.Length < 4)
|
|
return null;
|
|
|
|
// remove the \??\
|
|
name = name.Substring(4);
|
|
|
|
string[] split = name.Split('#');
|
|
if (split.Length < 3)
|
|
return null;
|
|
|
|
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);
|
|
return regkey;
|
|
}
|
|
|
|
static void RegisterRawDevice(IntPtr window, string device)
|
|
{
|
|
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))
|
|
{
|
|
Debug.Print("[Warning] Raw input registration failed with error: {0}. Device: {1}",
|
|
Marshal.GetLastWin32Error(), rid[0].ToString());
|
|
}
|
|
else
|
|
{
|
|
Debug.Print("Registered mouse {0}", device);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region IMouseDriver2 Members
|
|
|
|
public MouseState GetState()
|
|
{
|
|
lock (UpdateLock)
|
|
{
|
|
MouseState master = new MouseState();
|
|
foreach (MouseState ms in mice)
|
|
{
|
|
master.MergeBits(ms);
|
|
}
|
|
return master;
|
|
}
|
|
}
|
|
|
|
public MouseState GetState(int index)
|
|
{
|
|
lock (UpdateLock)
|
|
{
|
|
if (mice.Count > index)
|
|
return mice[index];
|
|
else
|
|
return new MouseState();
|
|
}
|
|
}
|
|
|
|
public void SetPosition(double x, double y)
|
|
{
|
|
Functions.SetCursorPos((int)x, (int)y);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|