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;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
using SlimDX; using SlimDX;
using SlimDX.DirectInput; using SlimDX.DirectInput;
@ -10,24 +9,26 @@ namespace BizHawk.Client.EmuHawk
{ {
// ********************************** Static interface ********************************** // ********************************** Static interface **********************************
static DirectInput dinput; private static readonly object _syncObj = new object();
public static List<GamePad> Devices; private static readonly List<GamePad> _devices = new List<GamePad>();
private static DirectInput _dinput;
public static void Initialize() public static void Initialize()
{ {
if (dinput == null) lock (_syncObj)
dinput = new DirectInput(); {
Cleanup();
Devices = new List<GamePad>(); _dinput = new DirectInput();
foreach (DeviceInstance device in dinput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly)) foreach (DeviceInstance device in _dinput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly))
{ {
Console.WriteLine("joydevice: {0} `{1}`", device.InstanceGuid, device.ProductName); Console.WriteLine("joydevice: {0} `{1}`", device.InstanceGuid, device.ProductName);
if (device.ProductName.Contains("XBOX 360")) 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) 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); var joystick = new Joystick(_dinput, device.InstanceGuid);
joystick.SetCooperativeLevel(GlobalWin.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); joystick.SetCooperativeLevel(GlobalWin.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive);
foreach (DeviceObjectInstance deviceObject in joystick.GetObjects()) foreach (DeviceObjectInstance deviceObject in joystick.GetObjects())
{ {
@ -37,23 +38,48 @@ namespace BizHawk.Client.EmuHawk
joystick.Acquire(); joystick.Acquire();
GamePad p = new GamePad(device.InstanceName, device.InstanceGuid, joystick); GamePad p = new GamePad(device.InstanceName, device.InstanceGuid, joystick);
Devices.Add(p); _devices.Add(p);
}
}
}
public static IEnumerable<GamePad> EnumerateDevices()
{
lock (_syncObj)
{
foreach (var device in _devices)
{
yield return device;
}
} }
} }
public static void UpdateAll() public static void UpdateAll()
{ {
foreach (var device in Devices.ToList()) lock (_syncObj)
{
foreach (var device in _devices)
{
device.Update(); 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(); device.joystick.Dispose();
Devices.Clear(); }
_devices.Clear();
if (_dinput != null)
{
_dinput.Dispose();
_dinput = null;
}
} }
} }

View File

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

View File

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

View File

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

View File

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