From 5ef675c11625599268e30d6c787953ed13f5c33d Mon Sep 17 00:00:00 2001 From: zeromus Date: Sun, 25 Aug 2013 17:11:19 +0000 Subject: [PATCH] solve an apparent threading datastructure conflict bug in input binding vs input poll thread. print list of discovered gamepad devices while booting input system. --- BizHawk.MultiClient/Input/GamePad.cs | 10 +- BizHawk.MultiClient/Input/Input.cs | 177 ++++++++++++++------------- 2 files changed, 99 insertions(+), 88 deletions(-) diff --git a/BizHawk.MultiClient/Input/GamePad.cs b/BizHawk.MultiClient/Input/GamePad.cs index c5065a140b..5179db9e56 100644 --- a/BizHawk.MultiClient/Input/GamePad.cs +++ b/BizHawk.MultiClient/Input/GamePad.cs @@ -21,10 +21,12 @@ namespace BizHawk.MultiClient foreach (DeviceInstance device in dinput.GetDevices(DeviceClass.GameController, DeviceEnumerationFlags.AttachedOnly)) { - if (device.ProductName.Contains("XBOX 360")) - continue; // Don't input XBOX 360 controllers into here; we'll process them via XInput. - - var joystick = new Joystick(dinput, device.InstanceGuid); + Console.WriteLine("joydevice: {0} `{1}`", device.InstanceGuid, device.ProductName); + + 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(Global.MainForm.Handle, CooperativeLevel.Background | CooperativeLevel.Nonexclusive); foreach (DeviceObjectInstance deviceObject in joystick.GetObjects()) { diff --git a/BizHawk.MultiClient/Input/Input.cs b/BizHawk.MultiClient/Input/Input.cs index 012fcaf939..986da8097f 100644 --- a/BizHawk.MultiClient/Input/Input.cs +++ b/BizHawk.MultiClient/Input/Input.cs @@ -278,71 +278,76 @@ namespace BizHawk.MultiClient GamePad360.UpdateAll(); _Modifiers = KeyInput.GetModifierKeysAsKeys(); - _NewEvents.Clear(); - //analyze keys - var bleh = new HashSet(); - foreach (var k in KeyInput.State.PressedKeys) - bleh.Add(k); - foreach (var k in KeyInput.State.AllKeys) - if (bleh.Contains(k)) - HandleButton(k.ToString(), true); - else - HandleButton(k.ToString(), false); - - lock (FloatValues) + //this block is going to massively modify data structures that the binding method uses, so we have to lock it all + lock (this) { - //FloatValues.Clear(); + _NewEvents.Clear(); - //analyze xinput - for (int i = 0; i < GamePad360.Devices.Count; i++) + //analyze keys + var bleh = new HashSet(); + foreach (var k in KeyInput.State.PressedKeys) + bleh.Add(k); + foreach (var k in KeyInput.State.AllKeys) + if (bleh.Contains(k)) + HandleButton(k.ToString(), true); + else + HandleButton(k.ToString(), false); + + lock (FloatValues) { - var pad = GamePad360.Devices[i]; - string xname = "X" + (i + 1) + " "; - for (int b = 0; b < pad.NumButtons; b++) - HandleButton(xname + pad.ButtonName(b), pad.Pressed(b)); - foreach (var sv in pad.GetFloats()) + //FloatValues.Clear(); + + //analyze xinput + for (int i = 0; i < GamePad360.Devices.Count; i++) { - string n = xname = sv.Item1; - float f = sv.Item2; - if (trackdeltas) - FloatDeltas[n] += Math.Abs(f - FloatValues[n]); - FloatValues[n] = f; + var pad = GamePad360.Devices[i]; + string xname = "X" + (i + 1) + " "; + for (int b = 0; b < pad.NumButtons; b++) + HandleButton(xname + pad.ButtonName(b), pad.Pressed(b)); + foreach (var sv in pad.GetFloats()) + { + string n = xname = sv.Item1; + float f = sv.Item2; + if (trackdeltas) + FloatDeltas[n] += Math.Abs(f - FloatValues[n]); + FloatValues[n] = f; + } } + + //analyze joysticks + for (int i = 0; i < GamePad.Devices.Count; i++) + { + var pad = GamePad.Devices[i]; + string jname = "J" + (i + 1) + " "; + + for (int b = 0; b < pad.NumButtons; b++) + HandleButton(jname + pad.ButtonName(b), pad.Pressed(b)); + foreach (var sv in pad.GetFloats()) + { + string n = jname + sv.Item1; + float f = sv.Item2; + //if (n == "J5 RotationZ") + // System.Diagnostics.Debugger.Break(); + if (trackdeltas) + FloatDeltas[n] += Math.Abs(f - FloatValues[n]); + FloatValues[n] = f; + } + } + } - //analyze joysticks - for (int i = 0; i < GamePad.Devices.Count; i++) + bool swallow = !Global.MainForm.AllowInput; + + foreach (var ie in _NewEvents) { - var pad = GamePad.Devices[i]; - string jname = "J" + (i + 1) + " "; - - for (int b = 0; b < pad.NumButtons; b++) - HandleButton(jname + pad.ButtonName(b), pad.Pressed(b)); - foreach (var sv in pad.GetFloats()) - { - string n = jname + sv.Item1; - float f = sv.Item2; - //if (n == "J5 RotationZ") - // System.Diagnostics.Debugger.Break(); - if (trackdeltas) - FloatDeltas[n] += Math.Abs(f - FloatValues[n]); - FloatValues[n] = f; - } + //events are swallowed in some cases: + if (ie.EventType == InputEventType.Press && swallow) + { } + else + EnqueueEvent(ie); } - - } - - bool swallow = !Global.MainForm.AllowInput; - - foreach (var ie in _NewEvents) - { - //events are swallowed in some cases: - if (ie.EventType == InputEventType.Press && swallow) - { } - else - EnqueueEvent(ie); - } + } //lock(this) //arbitrary selection of polling frequency: Thread.Sleep(10); @@ -390,40 +395,44 @@ namespace BizHawk.MultiClient //returns the next Press event, if available. should be useful public string GetNextBindEvent() { - if (InputEvents.Count == 0) return null; - if (!Global.MainForm.AllowInput) return null; - - //we only listen to releases for input binding, because we need to distinguish releases of pure modifierkeys from modified keys - //if you just pressed ctrl, wanting to bind ctrl, we'd see: pressed:ctrl, unpressed:ctrl - //if you just pressed ctrl+c, wanting to bind ctrl+c, we'd see: pressed:ctrl, pressed:ctrl+c, unpressed:ctrl+c, unpressed:ctrl - //so its the first unpress we need to listen for - - while (InputEvents.Count != 0) + //this whole process is intimately involved with the data structures, which can conflict with the input thread. + lock (this) { - var ie = DequeueEvent(); - - //as a special perk, we'll accept escape immediately - if (ie.EventType == InputEventType.Press && ie.LogicalButton.Button == "Escape") - goto ACCEPT; - - if (ie.EventType == InputEventType.Press) continue; + if (InputEvents.Count == 0) return null; + if (!Global.MainForm.AllowInput) return null; - ACCEPT: - Console.WriteLine("Bind Event: {0} ", ie); + //we only listen to releases for input binding, because we need to distinguish releases of pure modifierkeys from modified keys + //if you just pressed ctrl, wanting to bind ctrl, we'd see: pressed:ctrl, unpressed:ctrl + //if you just pressed ctrl+c, wanting to bind ctrl+c, we'd see: pressed:ctrl, pressed:ctrl+c, unpressed:ctrl+c, unpressed:ctrl + //so its the first unpress we need to listen for - foreach (var kvp in LastState) - if (kvp.Value) - { - Console.WriteLine("Unpressing " + kvp.Key); - UnpressState[kvp.Key] = true; - } - - InputEvents.Clear(); + while (InputEvents.Count != 0) + { + var ie = DequeueEvent(); - return ie.LogicalButton.ToString(); + //as a special perk, we'll accept escape immediately + if (ie.EventType == InputEventType.Press && ie.LogicalButton.Button == "Escape") + goto ACCEPT; + + if (ie.EventType == InputEventType.Press) continue; + + ACCEPT: + Console.WriteLine("Bind Event: {0} ", ie); + + foreach (var kvp in LastState) + if (kvp.Value) + { + Console.WriteLine("Unpressing " + kvp.Key); + UnpressState[kvp.Key] = true; + } + + InputEvents.Clear(); + + return ie.LogicalButton.ToString(); + } + + return null; } - - return null; } //controls whether modifier keys will be ignored as key press events