Make sure DirectInput objects are disposed properly. Also includes a better fix for thread safety issues (#722).

This commit is contained in:
J.D. Purcell 2017-04-08 11:49:04 -04:00
parent 5446a636ba
commit a2aba7e3c2
5 changed files with 165 additions and 96 deletions

View File

@ -1,5 +1,4 @@
using System;
using System.Linq;
using System.Collections.Generic;
using SlimDX;
using SlimDX.DirectInput;
@ -10,50 +9,77 @@ namespace BizHawk.Client.EmuHawk
{
// ********************************** Static interface **********************************
static DirectInput dinput;
public static List<GamePad> Devices;
private static readonly object _syncObj = new object();
private static readonly List<GamePad> _devices = new List<GamePad>();
private static DirectInput _dinput;
public static void Initialize()
{
if (dinput == null)
dinput = new DirectInput();
Devices = new List<GamePad>();
foreach (DeviceInstance device in dinput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly))
lock (_syncObj)
{
Console.WriteLine("joydevice: {0} `{1}`", device.InstanceGuid, device.ProductName);
Cleanup();
if (device.ProductName.Contains("XBOX 360"))
continue; // Don't input XBOX 360 controllers into here; we'll process them via XInput (there are limitations in some trigger axes when xbox pads go over xinput)
_dinput = new DirectInput();
var joystick = new Joystick(dinput, device.InstanceGuid);
joystick.SetCooperativeLevel(GlobalWin.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
foreach (DeviceObjectInstance deviceObject in joystick.GetObjects())
foreach (DeviceInstance device in _dinput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly))
{
if ((deviceObject.ObjectType & ObjectDeviceType.Axis) != 0)
joystick.GetObjectPropertiesById((int)deviceObject.ObjectType).SetRange(-1000, 1000);
}
joystick.Acquire();
Console.WriteLine("joydevice: {0} `{1}`", device.InstanceGuid, device.ProductName);
GamePad p = new GamePad(device.InstanceName, device.InstanceGuid, joystick);
Devices.Add(p);
if (device.ProductName.Contains("XBOX 360"))
continue; // Don't input XBOX 360 controllers into here; we'll process them via XInput (there are limitations in some trigger axes when xbox pads go over xinput)
var joystick = new Joystick(_dinput, device.InstanceGuid);
joystick.SetCooperativeLevel(GlobalWin.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
foreach (DeviceObjectInstance deviceObject in joystick.GetObjects())
{
if ((deviceObject.ObjectType & ObjectDeviceType.Axis) != 0)
joystick.GetObjectPropertiesById((int)deviceObject.ObjectType).SetRange(-1000, 1000);
}
joystick.Acquire();
GamePad p = new GamePad(device.InstanceName, device.InstanceGuid, joystick);
_devices.Add(p);
}
}
}
public static IEnumerable<GamePad> EnumerateDevices()
{
lock (_syncObj)
{
foreach (var device in _devices)
{
yield return device;
}
}
}
public static void UpdateAll()
{
foreach (var device in Devices.ToList())
device.Update();
lock (_syncObj)
{
foreach (var device in _devices)
{
device.Update();
}
}
}
public static void CloseAll()
public static void Cleanup()
{
if (Devices != null)
lock (_syncObj)
{
foreach (var device in Devices)
foreach (var device in _devices)
{
device.joystick.Dispose();
Devices.Clear();
}
_devices.Clear();
if (_dinput != null)
{
_dinput.Dispose();
_dinput = null;
}
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using SlimDX.XInput;
#pragma warning disable 169
@ -13,9 +12,9 @@ namespace BizHawk.Client.EmuHawk
{
// ********************************** Static interface **********************************
public static List<GamePad360> Devices = new List<GamePad360>();
static bool IsAvailable;
private static readonly object _syncObj = new object();
private static readonly List<GamePad360> _devices = new List<GamePad360>();
private static readonly bool _isAvailable = IsAvailable();
[DllImport("kernel32", SetLastError = true, EntryPoint = "GetProcAddress")]
static extern IntPtr GetProcAddressOrdinal(IntPtr hModule, IntPtr procName);
@ -43,9 +42,8 @@ namespace BizHawk.Client.EmuHawk
public XINPUT_GAMEPAD Gamepad;
}
public static void Initialize()
private static bool IsAvailable()
{
IsAvailable = false;
try
{
//some users wont even have xinput installed. in order to avoid spurious exceptions and possible instability, check for the library first
@ -70,35 +68,60 @@ namespace BizHawk.Client.EmuHawk
//don't remove this code. it's important to catch errors on systems with broken xinput installs.
//(probably, checking for the library was adequate, but lets not get rid of this anyway)
var test = new SlimDX.XInput.Controller(UserIndex.One).IsConnected;
IsAvailable = true;
return true;
}
}
catch { }
if (!IsAvailable) return;
return false;
}
//now, at this point, slimdx may be using one xinput, and we may be using another
//i'm not sure how slimdx picks its dll to bind to.
//i'm not sure how troublesome this will be
//maybe we should get rid of slimdx for this altogether
public static void Initialize()
{
lock (_syncObj)
{
_devices.Clear();
var c1 = new Controller(UserIndex.One);
var c2 = new Controller(UserIndex.Two);
var c3 = new Controller(UserIndex.Three);
var c4 = new Controller(UserIndex.Four);
if (!_isAvailable)
return;
if (c1.IsConnected) Devices.Add(new GamePad360(0,c1));
if (c2.IsConnected) Devices.Add(new GamePad360(1,c2));
if (c3.IsConnected) Devices.Add(new GamePad360(2,c3));
if (c4.IsConnected) Devices.Add(new GamePad360(3,c4));
//now, at this point, slimdx may be using one xinput, and we may be using another
//i'm not sure how slimdx picks its dll to bind to.
//i'm not sure how troublesome this will be
//maybe we should get rid of slimdx for this altogether
var c1 = new Controller(UserIndex.One);
var c2 = new Controller(UserIndex.Two);
var c3 = new Controller(UserIndex.Three);
var c4 = new Controller(UserIndex.Four);
if (c1.IsConnected) _devices.Add(new GamePad360(0, c1));
if (c2.IsConnected) _devices.Add(new GamePad360(1, c2));
if (c3.IsConnected) _devices.Add(new GamePad360(2, c3));
if (c4.IsConnected) _devices.Add(new GamePad360(3, c4));
}
}
public static IEnumerable<GamePad360> EnumerateDevices()
{
lock (_syncObj)
{
foreach (var device in _devices)
{
yield return device;
}
}
}
public static void UpdateAll()
{
if(IsAvailable)
foreach (var device in Devices.ToList())
lock (_syncObj)
{
foreach (var device in _devices)
{
device.Update();
}
}
}
// ********************************** Instance Members **********************************

View File

@ -135,6 +135,14 @@ namespace BizHawk.Client.EmuHawk
Instance = new Input();
}
public static void Cleanup()
{
#if WINDOWS
KeyInput.Cleanup();
GamePad.Cleanup();
#endif
}
public enum InputEventType
{
Press, Release
@ -338,9 +346,8 @@ namespace BizHawk.Client.EmuHawk
//FloatValues.Clear();
//analyze xinput
for (int i = 0; i < GamePad360.Devices.Count; i++)
foreach (var pad in GamePad360.EnumerateDevices())
{
var pad = GamePad360.Devices[i];
string xname = "X" + (pad.PlayerNumber) + " ";
for (int b = 0; b < pad.NumButtons; b++)
HandleButton(xname + pad.ButtonName(b), pad.Pressed(b));
@ -355,10 +362,10 @@ namespace BizHawk.Client.EmuHawk
}
//analyze joysticks
for (int i = 0; i < GamePad.Devices.Count; i++)
foreach (var item in GamePad.EnumerateDevices().Select((n, i) => new { Device = n, Index = i }))
{
var pad = GamePad.Devices[i];
string jname = "J" + (i + 1) + " ";
var pad = item.Device;
string jname = "J" + (item.Index + 1) + " ";
for (int b = 0; b < pad.NumButtons; b++)
HandleButton(jname + pad.ButtonName(b), pad.Pressed(b));

View File

@ -6,50 +6,68 @@ namespace BizHawk.Client.EmuHawk
{
public static class KeyInput
{
private static DirectInput dinput;
private static Keyboard keyboard;
private static KeyboardState state = new KeyboardState();
private static readonly object _syncObj = new object();
private static readonly List<KeyEvent> _eventList = new List<KeyEvent>();
private static DirectInput _dinput;
private static Keyboard _keyboard;
public static void Initialize()
{
if (dinput == null)
dinput = new DirectInput();
lock (_syncObj)
{
Cleanup();
if (keyboard == null || keyboard.Disposed)
keyboard = new Keyboard(dinput);
keyboard.SetCooperativeLevel(GlobalWin.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
keyboard.Properties.BufferSize = 8;
_dinput = new DirectInput();
_keyboard = new Keyboard(_dinput);
_keyboard.SetCooperativeLevel(GlobalWin.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
_keyboard.Properties.BufferSize = 8;
}
}
static List<KeyEvent> EmptyList = new List<KeyEvent>();
static List<KeyEvent> EventList = new List<KeyEvent>();
public static void Cleanup()
{
lock (_syncObj)
{
if (_keyboard != null)
{
_keyboard.Dispose();
_keyboard = null;
}
if (_dinput != null)
{
_dinput.Dispose();
_dinput = null;
}
}
}
public static IEnumerable<KeyEvent> Update()
{
EventList.Clear();
if (keyboard.Acquire().IsFailure)
return EmptyList;
if (keyboard.Poll().IsFailure)
return EmptyList;
for (; ; )
lock (_syncObj)
{
var events = keyboard.GetBufferedData();
if (Result.Last.IsFailure)
return EventList;
if (events.Count == 0)
break;
foreach (var e in events)
{
foreach (var k in e.PressedKeys)
EventList.Add(new KeyEvent { Key = k, Pressed = true });
foreach (var k in e.ReleasedKeys)
EventList.Add(new KeyEvent { Key = k, Pressed = false });
}
}
_eventList.Clear();
return EventList;
if (_keyboard == null || _keyboard.Acquire().IsFailure || _keyboard.Poll().IsFailure)
return _eventList;
for (; ; )
{
var events = _keyboard.GetBufferedData();
if (Result.Last.IsFailure || events.Count == 0)
break;
foreach (var e in events)
{
foreach (var k in e.PressedKeys)
_eventList.Add(new KeyEvent { Key = k, Pressed = true });
foreach (var k in e.ReleasedKeys)
_eventList.Add(new KeyEvent { Key = k, Pressed = false });
}
}
return _eventList;
}
}
public struct KeyEvent
@ -57,6 +75,5 @@ namespace BizHawk.Client.EmuHawk
public Key Key;
public bool Pressed;
}
}
}

View File

@ -231,9 +231,7 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.Sound = null;
}
GlobalWin.GL.Dispose();
#if WINDOWS
GamePad.CloseAll();
#endif
Input.Cleanup();
}
else
{ // Display error message windows
@ -305,9 +303,7 @@ namespace BizHawk.Client.EmuHawk
GlobalWin.Sound = null;
}
GlobalWin.GL.Dispose();
#if WINDOWS
GamePad.CloseAll();
#endif
Input.Cleanup();
}
}