using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Reflection; using System.Windows.Forms; using BizHawk.Emulation.Common; using BizHawk.Client.Common; namespace BizHawk.Client.EmuHawk { public partial class VirtualpadTool : Form, IToolFormAutoConfig { [RequiredService] private IEmulator Emulator { get; set; } [ConfigPersist] public bool StickyPads { get; set; } [ConfigPersist] public bool ClearAlsoClearsAnalog { get; set; } private bool _readOnly; private List Pads { get { return ControllerPanel.Controls .OfType() .ToList(); } } public bool Readonly { get { return _readOnly; } set { _readOnly = value; Pads.ForEach(p => p.ReadOnly = value); } } public VirtualpadTool() { StickyPads = true; InitializeComponent(); } private void VirtualpadTool_Load(object sender, EventArgs e) { CreatePads(); } public void ClearVirtualPadHolds() { if (ClearAlsoClearsAnalog) { Pads.ForEach(pad => pad.Clear()); } else { Pads.ForEach(pad => pad.ClearBoolean()); } } public void BumpAnalogValue(int? x, int? y) // TODO: multi-player { Pads.ForEach(pad => pad.BumpAnalog(x, y)); } private void CreatePads() { ControllerPanel.Controls.Clear(); var schemaType = Assembly .GetExecutingAssembly() .GetTypes() .Where(t => typeof(IVirtualPadSchema) .IsAssignableFrom(t) && t.GetCustomAttributes(false) .OfType() .Any()) .FirstOrDefault(t => t.GetCustomAttributes(false) .OfType() .First().SystemId == Emulator.SystemId); if (schemaType != null) { var padschemas = (Activator.CreateInstance(schemaType) as IVirtualPadSchema).GetPadSchemas(); if (VersionInfo.DeveloperBuild) { CheckPads(padschemas, Emulator.ControllerDefinition); } var pads = padschemas.Select(s => new VirtualPad(s)); if (pads.Any()) { ControllerPanel.Controls.AddRange(pads.Reverse().ToArray()); } } } public void ScrollToPadSchema(string padSchemaName) { foreach (var control in ControllerPanel.Controls) { VirtualPad vp = control as VirtualPad; if (vp == null) continue; if (vp.PadSchemaDisplayName == padSchemaName) ControllerPanel.ScrollControlIntoView(vp); } } private void CheckPads(IEnumerable schemas, BizHawk.Emulation.Common.ControllerDefinition def) { HashSet analogs = new HashSet(def.FloatControls); HashSet bools = new HashSet(def.BoolButtons); foreach (var schema in schemas) { foreach (var button in schema.Buttons) { HashSet searchset = null; switch (button.Type) { case PadSchema.PadInputType.AnalogStick: case PadSchema.PadInputType.FloatSingle: case PadSchema.PadInputType.TargetedPair: // analog searchset = analogs; break; case PadSchema.PadInputType.Boolean: searchset = bools; break; case PadSchema.PadInputType.DiscManager: searchset = bools; searchset.UnionWith(analogs); break; } if (!searchset.Contains(button.Name)) { MessageBox.Show(this, string.Format("Schema warning: Schema entry '{0}':'{1}' will not correspond to any control in definition '{2}'", schema.DisplayName, button.Name, def.Name), "Dev Warning"); } } } } #region IToolForm Implementation public bool AskSaveChanges() { return true; } public bool UpdateBefore { get { return false; } } public void Restart() { if (!IsHandleCreated || IsDisposed) { return; } CreatePads(); } public void UpdateValues() { if (!IsHandleCreated || IsDisposed) { return; } Pads.ForEach(p => p.SetPrevious(null)); // Not the cleanest way to clear this every frame if (Global.MovieSession.Movie.IsPlaying && !Global.MovieSession.Movie.IsFinished) { Readonly = true; Pads.ForEach(p => p.Set(Global.MovieSession.CurrentInput)); } else { if (Global.MovieSession.Movie.IsRecording) { Pads.ForEach(p => p.SetPrevious(Global.MovieSession.PreviousFrame)); } Readonly = false; } if (!Readonly && !StickyPads && !Control.MouseButtons.HasFlag(MouseButtons.Left)) { Pads.ForEach(pad => pad.Clear()); } Pads.ForEach(pad => pad.UpdateValues()); } public void FastUpdate() { // TODO: SetPrevious logic should go here too or that will get out of whack if (!Readonly && !StickyPads) { Pads.ForEach(pad => pad.Clear()); } } #endregion #region Menu private void PadsSubMenu_DropDownOpened(object sender, EventArgs e) { StickyMenuItem.Checked = StickyPads; } private void ClearAllMenuItem_Click(object sender, EventArgs e) { ClearVirtualPadHolds(); } private void StickyMenuItem_Click(object sender, EventArgs e) { StickyPads ^= true; } private void PadBoxContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e) { StickyContextMenuItem.Checked = StickyPads; } private void ExitMenuItem_Click(object sender, EventArgs e) { Close(); } private void OptionsSubMenu_DropDownOpened(object sender, EventArgs e) { ClearClearsAnalogInputMenuItem.Checked = ClearAlsoClearsAnalog; } private void ClearClearsAnalogInputMenuItem_Click(object sender, EventArgs e) { ClearAlsoClearsAnalog ^= true; } #endregion } }