using System; using System.Collections.Generic; using System.Text; using Eto; using Eto.Forms; using Eto.Drawing; using BizHawk.Client.EtoHawk; using BizHawk.Emulation.Common; using BizHawk.Common; using BizHawk.Client.Common; using System.Linq; using System.Collections; using System.Reflection; using System.IO; using System.Threading; namespace EtoHawk.Config { public partial class ControllerConfig : Dialog { private const int MAXPLAYERS = 8; private static readonly Dictionary ControllerImages = new Dictionary(); private readonly ControllerDefinition _theDefinition; private Thread _timer; private string _wasPressed = string.Empty; private InputWidget _focusedWidget; public InputWidget FocusedWidget { get{ return _focusedWidget; } set { if (_focusedWidget != value) { _focusedWidget = value; _wasPressed = Input.Instance.GetNextBindEvent (); SetupTimer (); } } } static ControllerConfig() { ControllerImages.Add("NES Controller", GetResourceImage("NES_Controller")); ControllerImages.Add("SNES Controller", GetResourceImage("SNES_Controller")); ControllerImages.Add("Nintento 64 Controller", GetResourceImage("N64")); ControllerImages.Add("Gameboy Controller", GetResourceImage("GBController")); ControllerImages.Add("GBA Controller", GetResourceImage("GBA_Controller")); ControllerImages.Add("Dual Gameboy Controller", GetResourceImage("GBController")); ControllerImages.Add("SMS Controller", GetResourceImage("SMSController")); ControllerImages.Add("Genesis 3-Button Controller", GetResourceImage("GENController")); ControllerImages.Add("GPGX Genesis Controller", GetResourceImage("GENController")); ControllerImages.Add("Saturn Controller", GetResourceImage("SaturnController")); ControllerImages.Add("Intellivision Controller", GetResourceImage("IntVController")); ControllerImages.Add("ColecoVision Basic Controller", GetResourceImage("colecovisioncontroller")); ControllerImages.Add("Atari 2600 Basic Controller", GetResourceImage("atari_controller")); ControllerImages.Add("Atari 7800 ProLine Joystick Controller", GetResourceImage("A78Joystick")); ControllerImages.Add("PC Engine Controller", GetResourceImage("PCEngineController")); ControllerImages.Add("Commodore 64 Controller", GetResourceImage("C64Joystick")); ControllerImages.Add("TI83 Controller", GetResourceImage("TI83_Controller")); ControllerImages.Add("WonderSwan Controller", GetResourceImage("WonderSwanColor")); ControllerImages.Add("Lynx Controller", GetResourceImage("Lynx")); ControllerImages.Add("PSX Gamepad Controller", GetResourceImage("PSX_Original_Controller")); ControllerImages.Add("PSX DualShock Controller", GetResourceImage("psx_dualshock")); } private static Bitmap GetResourceImage(string name) { Assembly assm = Assembly.GetExecutingAssembly(); string resName = assm.ManifestModule.Name.Replace (".exe", "." + name + ".png"); Bitmap img = null; try{ using(Stream strm = assm.GetManifestResourceStream (resName)){ img = new Bitmap(strm); img = new Bitmap(img, 250, null, ImageInterpolation.Medium); //Force the images to always be 250x250, it's the easiest way to enforce the auto-sized picture box to always be that size. } } catch(Exception ex){ } if (img == null) { img = new Bitmap (new Size (250, 250), PixelFormat.Format32bppRgba); Graphics g = new Graphics (img); g.DrawText (new Font (SystemFont.Label, 12), Eto.Drawing.Colors.Black, 0, 0, name + " image not found."); } return img; } /*protected override void OnActivated(EventArgs e) { base.OnActivated(e); Input.Instance.ControlInputFocus(this, Input.InputFocus.Mouse, true); } protected override void OnDeactivate(EventArgs e) { base.OnDeactivate(e); Input.Instance.ControlInputFocus(this, Input.InputFocus.Mouse, false); }*/ private ControllerConfig() { InitializeComponent(); /*Closing += (o, e) => { buttonOK.Focus(); // A very dirty hack to avoid https://code.google.com/p/bizhawk/issues/detail?id=161 };*/ } private void SetupTimer() { if (_timer != null) { _timer.Interrupt(); //Shouldn't happen, we kill it when it loses focus. if (!_timer.Join(50)) { _timer.Abort(); //Should have ended right away and didn't, so now it must die. } _timer = null; } _timer = new Thread(Timer_Tick); _timer.Start(); } private void Timer_Tick() { while (true) { try { ReadKeys(); Thread.Sleep(33); } catch (ThreadInterruptedException) { break; //Time to leave this place } } _timer = null; } /// /// Poll input events and apply processing related to accepting that as a binding /// private void ReadKeys() { Input.Instance.Update(); var bindingStr = Input.Instance.GetNextBindEvent(); if (!string.IsNullOrEmpty(_wasPressed) && bindingStr == _wasPressed) { return; } if (bindingStr != null) { if (_focusedWidget != null) { Application.Instance.Invoke (new Action (() => { _focusedWidget.HandleMappingInput(bindingStr); })); } _wasPressed = bindingStr; } } private delegate Panel PanelCreator(Dictionary settings, List buttons, Size size); private Panel CreateNormalPanel(Dictionary settings, List buttons, Size size) { var cp = new ControllerConfigPanel { /*Dock = DockStyle.Fill, AutoScroll = true*/ }; cp.ParentConfig = this; //cp.Tooltip = toolTip1; cp.LoadSettings(settings, checkBoxAutoTab.Checked==true, buttons, size.Width, size.Height); return cp; } /*private static Control CreateAnalogPanel(Dictionary settings, List buttons, Size size) { return new AnalogBindPanel(settings, buttons) { Dock = DockStyle.Fill, AutoScroll = true }; }*/ private static void LoadToPanel(Panel dest, string controllerName, IList controllerButtons, IDictionary> settingsblock, T defaultvalue, PanelCreator createpanel) { Dictionary settings; if (!settingsblock.TryGetValue(controllerName, out settings)) { settings = new Dictionary(); settingsblock[controllerName] = settings; } // check to make sure that the settings object has all of the appropriate boolbuttons foreach (var button in controllerButtons) { if (!settings.Keys.Contains(button)) { settings[button] = defaultvalue; } } if (controllerButtons.Count == 0) { return; } // split the list of all settings into buckets by player number var buckets = new List[MAXPLAYERS + 1]; for (var i = 0; i < buckets.Length; i++) { buckets[i] = new List(); } // by iterating through only the controller's active buttons, we're silently // discarding anything that's not on the controller right now. due to the way // saving works, those entries will still be preserved in the config file, tho foreach (var button in controllerButtons) { int i; for (i = 1; i <= MAXPLAYERS; i++) { if (button.StartsWith("P" + i)) { break; } } if (i > MAXPLAYERS) // couldn't find { i = 0; } buckets[i].Add(button); } if (buckets[0].Count == controllerButtons.Count) { // everything went into bucket 0, so make no tabs at all dest.Content = createpanel(settings, controllerButtons.ToList(), dest.Size); } else { // create multiple player tabs var tt = new TabControl { /*Dock = DockStyle.Fill*/ }; int pageidx = 0; for (int i = 1; i <= MAXPLAYERS; i++) { if (buckets[i].Count > 0) { string tabname = Global.Emulator.SystemId == "WSWAN" ? i == 1 ? "Normal" : "Rotated" : "Player " + i; // hack TabPage pg = new TabPage(createpanel(settings, buckets[i], tt.Size)); pg.Text = tabname; tt.Pages.Add(pg); pageidx++; } } if (buckets[0].Count > 0) { string tabname = Global.Emulator.SystemId == "C64" ? "Keyboard" : "Console"; // hack TabPage pg = new TabPage(createpanel(settings, buckets[0], tt.Size)); pg.Text = tabname; tt.Pages.Add(pg); } dest.Content = tt; } } public ControllerConfig(ControllerDefinition def) : this() { _theDefinition = def; SuspendLayout(); checkBoxUDLR.Checked = Global.Config.AllowUD_LR; checkBoxAutoTab.Checked = Global.Config.InputConfigAutoTab; LoadPanels(Global.Config); SetControllerPicture(def.Name); var analog = tabControl1.Pages[0]; ResumeLayout(); } private void LoadPanels( IDictionary> normal, IDictionary> autofire, IDictionary> analog) { LoadToPanel(NormalControlsTab, _theDefinition.Name, _theDefinition.BoolButtons, normal, string.Empty, CreateNormalPanel); LoadToPanel(AutofireControlsTab, _theDefinition.Name, _theDefinition.BoolButtons, autofire, string.Empty, CreateNormalPanel); /*LoadToPanel(AnalogControlsTab, _theDefinition.Name, _theDefinition.FloatControls, analog, new BizHawk.Client.Common.Config.AnalogBind(string.Empty, 1.0f, 0.1f), CreateAnalogPanel); if (AnalogControlsTab.Controls.Count == 0) { tabControl1.TabPages.Remove(AnalogControlsTab); }*/ } private void LoadPanels(ControlDefaults cd) { LoadPanels(cd.AllTrollers, cd.AllTrollersAutoFire, cd.AllTrollersAnalog); } private void LoadPanels(BizHawk.Client.Common.Config c) { LoadPanels(c.AllTrollers, c.AllTrollersAutoFire, c.AllTrollersAnalog); } private void SetControllerPicture(string controlName) { Bitmap bmp; if (!ControllerImages.TryGetValue(controlName, out bmp)) { bmp = ControllerConfig.GetResourceImage("Help"); //Properties.Resources.Help; } pictureBox1.Image = bmp; pictureBox1.Size = bmp.Size; //tableLayoutPanel1.ColumnStyles[1].Width = bmp.Width; // Uberhack /*if (controlName == "Commodore 64 Controller") { var pictureBox2 = new PictureBox { Image = Properties.Resources.C64Keyboard, Size = Properties.Resources.C64Keyboard.Size }; tableLayoutPanel1.ColumnStyles[1].Width = Properties.Resources.C64Keyboard.Width; pictureBox1.Height /= 2; pictureBox1.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right; pictureBox1.Dock = DockStyle.Top; pictureBox2.Location = new Point(pictureBox1.Location.X, pictureBox1.Location.Y + pictureBox1.Size.Height + 10); tableLayoutPanel1.Controls.Add(pictureBox2, 1, 0); pictureBox2.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom; }*/ } // lazy methods, but they're not called often and actually // tracking all of the ControllerConfigPanels wouldn't be simpler private static void SetAutoTab(Control c, bool value) { if (c is ControllerConfigPanel) { (c as ControllerConfigPanel).SetAutoTab(value); } /*else if (c is AnalogBindPanel) { // TODO }*/ else if (c is Panel && ((Panel)c).Controls.Count() > 0) { foreach (Control cc in ((Panel)c).Controls) { SetAutoTab(cc, value); } } } private void Save() { ActOnControlCollection(NormalControlsTab, c => c.Save(Global.Config.AllTrollers[_theDefinition.Name])); ActOnControlCollection(AutofireControlsTab, c => c.Save(Global.Config.AllTrollersAutoFire[_theDefinition.Name])); //ActOnControlCollection(AnalogControlsTab, c => c.Save(Global.Config.AllTrollersAnalog[_theDefinition.Name])); } private void SaveToDefaults(ControlDefaults cd) { ActOnControlCollection(NormalControlsTab, c => c.Save(cd.AllTrollers[_theDefinition.Name])); ActOnControlCollection(AutofireControlsTab, c => c.Save(cd.AllTrollersAutoFire[_theDefinition.Name])); //ActOnControlCollection(AnalogControlsTab, c => c.Save(cd.AllTrollersAnalog[_theDefinition.Name])); } private static void ActOnControlCollection(Control c, Action proc) where T : Control { if (c is T) { proc(c as T); } else if (c is Panel && ((Panel)c).Controls.Count() > 0) { foreach (Control cc in ((Panel)c).Controls) { ActOnControlCollection(cc, proc); } } else if (c is TabControl) { foreach (TabPage pg in ((TabControl)c).Pages) { ActOnControlCollection(pg, proc); } } } private void CheckBoxAutoTab_CheckedChanged(object sender, EventArgs e) { SetAutoTab(this, checkBoxAutoTab.Checked==true); } private void ButtonOk_Click(object sender, EventArgs e) { Global.Config.AllowUD_LR = checkBoxUDLR.Checked==true; Global.Config.InputConfigAutoTab = checkBoxAutoTab.Checked==true; Save(); //GlobalWin.OSD.AddMessage("Controller settings saved"); Result = true; Close(); } private void ButtonCancel_Click(object sender, EventArgs e) { //GlobalWin.OSD.AddMessage("Controller config aborted"); Result = false; Close(); } private void NewControllerConfig_Load(object sender, EventArgs e) { Title = _theDefinition.Name + " Configuration"; } private static TabControl GetTabControl(IEnumerable controls) { if (controls != null) { return controls .OfType() .Select(c => c) .FirstOrDefault(); } return null; } private void ButtonLoadDefaults_Click(object sender, EventArgs e) { tabControl1.SuspendLayout(); TabPage wasTabbedMain = tabControl1.SelectedPage; var tb1 = GetTabControl(NormalControlsTab.Controls); var tb2 = GetTabControl(AutofireControlsTab.Controls); var tb3 = GetTabControl(AnalogControlsTab.Controls); int? wasTabbedPage1 = null; int? wasTabbedPage2 = null; int? wasTabbedPage3 = null; if (tb1 != null && tb1.SelectedPage != null) { wasTabbedPage1 = tb1.SelectedIndex; } if (tb2 != null && tb2.SelectedPage != null) { wasTabbedPage2 = tb2.SelectedIndex; } if (tb3 != null && tb3.SelectedPage != null) { wasTabbedPage3 = tb3.SelectedIndex; } NormalControlsTab.Content = null; //NormalControlsTab.Controls.Clear(); AutofireControlsTab.Content = null; //AutofireControlsTab.Controls.Clear(); AnalogControlsTab.Content = null; //AnalogControlsTab.Controls.Clear(); // load panels directly from the default config. // this means that the changes are NOT committed. so "Cancel" works right and you // still have to hit OK at the end. var cd = ConfigService.Load(BizHawk.Client.Common.Config.ControlDefaultPath); LoadPanels(cd); tabControl1.SelectedPage = wasTabbedMain; if (wasTabbedPage1.HasValue) { var newTb1 = GetTabControl(NormalControlsTab.Controls); if (newTb1 != null) { newTb1.SelectedIndex = wasTabbedPage1.Value; } } if (wasTabbedPage2.HasValue) { var newTb2 = GetTabControl(AutofireControlsTab.Controls); if (newTb2 != null) { newTb2.SelectedIndex = wasTabbedPage2.Value; } } if (wasTabbedPage3.HasValue) { var newTb3 = GetTabControl(AnalogControlsTab.Controls); if (newTb3 != null) { newTb3.SelectedIndex = wasTabbedPage3.Value; } } tabControl1.ResumeLayout(); } private void ButtonSaveDefaults_Click(object sender, EventArgs e) { // this doesn't work anymore, as it stomps out any defaults for buttons that aren't currently active on the console // there are various ways to fix it, each with its own semantic problems var result = MessageBox.Show(this, "OK to overwrite defaults for current control scheme?", "Save Defaults", MessageBoxButtons.YesNo); if (result == DialogResult.Yes) { var cd = ConfigService.Load(BizHawk.Client.Common.Config.ControlDefaultPath); cd.AllTrollers[_theDefinition.Name] = new Dictionary(); cd.AllTrollersAutoFire[_theDefinition.Name] = new Dictionary(); cd.AllTrollersAnalog[_theDefinition.Name] = new Dictionary(); SaveToDefaults(cd); ConfigService.Save(BizHawk.Client.Common.Config.ControlDefaultPath, cd); } } private void ClearWidgetAndChildren(Control c) { if (c is InputCompositeWidget) { (c as InputCompositeWidget).Clear(); } if (c is InputWidget) { (c as InputWidget).ClearAll(); } /*if (c is AnalogBindControl) { (c as AnalogBindControl).Unbind_Click(null, null); }*/ if (c is Panel && ((Panel)c).Controls.Any()) { foreach (Control child in ((Panel)c).Controls) { ClearWidgetAndChildren(child); } } } private void ClearBtn_Click(object sender, EventArgs e) { foreach (var c in this.Controls) { ClearWidgetAndChildren(c); } } } }