BizHawk/BizHawk.Client.EtoHawk/MainForm.cs

885 lines
33 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Eto.Forms;
using Eto;
using Eto.Drawing;
using BizHawk.Client.Common;
using BizHawk.Emulation.Common;
using System.Threading;
using System.Runtime.InteropServices;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using BizHawk.Emulation.Cores.Nintendo.NES;
using BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES;
using BizHawk.Emulation.Common.IEmulatorExtensions;
namespace BizHawk.Client.EtoHawk
{
public partial class MainForm : Form
{
private Thread _worker;
private bool _running;
private readonly Throttle _throttle;
private bool _unthrottled;
private bool _runloopFrameProgress;
private long _frameAdvanceTimestamp;
private long _frameRewindTimestamp;
private int _runloopFps;
private int _runloopLastFps;
private bool _runloopFrameadvance;
private long _runloopSecond;
private bool _runloopLastFf;
private bool _inResizeLoop;
private bool _suspended; //True if a modal dialog appears
private readonly InputCoalescer HotkeyCoalescer = new InputCoalescer();
public MainForm()
{
InitBizHawk();
_throttle = new Throttle();
InitializeComponent();
_running = true;
_worker = new Thread(ProgramRunLoop);
_worker.Start();
}
private void InitBizHawk()
{
GlobalWin.MainForm = this;
Global.Rewinder = new Rewinder
{
//MessageCallback = GlobalWin.OSD.AddMessage
};
Global.ControllerInputCoalescer = new ControllerInputCoalescer();
Global.FirmwareManager = new FirmwareManager();
Global.MovieSession = new MovieSession
{
Movie = MovieService.DefaultInstance,
MovieControllerAdapter = MovieService.DefaultInstance.LogGeneratorInstance().MovieControllerAdapter,
//MessageCallback = GlobalWin.OSD.AddMessage,
//AskYesNoCallback = StateErrorAskUser,
PauseCallback = PauseEmulator,
//ModeChangedCallback = SetMainformMovieInfo
};
//Icon = Properties.Resources.logo;
Global.Game = GameInfo.NullInstance;
string iniPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "config.ini");
Global.Config = ConfigService.Load<Config>(iniPath);
Global.Config.ResolveDefaults();
Input.Initialize();
InitControls();
var comm = CreateCoreComm();
CoreFileProvider.SyncCoreCommInputSignals(comm);
Global.Emulator = new NullEmulator(comm, Global.Config.GetCoreSettings<NullEmulator>());
Global.ActiveController = new Controller(NullController.Instance.Definition);
Global.AutoFireController = new AutofireController(NullController.Instance.Definition, Global.Emulator);
Global.AutofireStickyXORAdapter.SetOnOffPatternFromConfig();
Global.Config.SoundOutputMethod = Config.ESoundOutputMethod.OpenAL; //Temporary, remove later
try { GlobalWin.Sound = new Sound(IntPtr.Zero); }
catch
{
string message = "Couldn't initialize sound device! Try changing the output method in Sound config.";
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound)
message = "Couldn't initialize DirectSound! Things may go poorly for you. Try changing your sound driver to 44.1khz instead of 48khz in mmsys.cpl.";
MessageBox.Show(message, "Initialization Error", MessageBoxButtons.OK, MessageBoxType.Error);
Global.Config.SoundOutputMethod = Config.ESoundOutputMethod.Dummy;
GlobalWin.Sound = new Sound(IntPtr.Zero);
}
GlobalWin.Sound.StartSound();
InputManager.RewireInputChain();
/*GlobalWin.Tools = new ToolManager(this);*/
RewireSound();
GlobalWin.DisplayManager = new DisplayManager();
}
private static void InitControls()
{
var controls = new Controller(
new ControllerDefinition
{
Name = "Emulator Frontend Controls",
BoolButtons = Global.Config.HotkeyBindings.Select(x => x.DisplayName).ToList()
});
foreach (var b in Global.Config.HotkeyBindings)
{
controls.BindMulti(b.DisplayName, b.Bindings);
}
Global.ClientControls = controls;
}
private void RewireSound()
{
/*if (_dumpProxy != null)
{
// we're video dumping, so async mode only and use the DumpProxy.
// note that the avi dumper has already rewired the emulator itself in this case.
GlobalWin.Sound.SetAsyncInputPin(_dumpProxy);
}
else*/
{
//Global.Emulator.EndAsyncSound();
//GlobalWin.Sound.SetSyncInputPin(Global.Emulator.SyncSoundProvider);
}
}
public void ProgramRunLoop()
{
//CheckMessages();
//LogConsole.PositionConsole();
while (_running)
{
if (_suspended)
{
Thread.Sleep(33);
continue;
}
Input.Instance.Update();
// handle events and dispatch as a hotkey action, or a hotkey button, or an input button
ProcessInput();
Global.ClientControls.LatchFromPhysical(HotkeyCoalescer);
Global.ActiveController.LatchFromPhysical(Global.ControllerInputCoalescer);
/*Global.ActiveController.ApplyAxisConstraints(
(Global.Emulator is N64 && Global.Config.N64UseCircularAnalogConstraint) ? "Natural Circle" : null);*/
Global.ActiveController.OR_FromLogical(Global.ClickyVirtualPadController);
Global.AutoFireController.LatchFromPhysical(Global.ControllerInputCoalescer);
if (Global.ClientControls["Autohold"])
{
Global.StickyXORAdapter.MassToggleStickyState(Global.ActiveController.PressedButtons);
Global.AutofireStickyXORAdapter.MassToggleStickyState(Global.AutoFireController.PressedButtons);
}
else if (Global.ClientControls["Autofire"])
{
Global.AutofireStickyXORAdapter.MassToggleStickyState(Global.ActiveController.PressedButtons);
}
// autohold/autofire must not be affected by the following inputs
Global.ActiveController.Overrides(Global.LuaAndAdaptor);
if (Global.Config.DisplayInput) // Input display wants to update even while paused
{
GlobalWin.DisplayManager.NeedsToPaint = true;
}
StepRunLoop_Core();
StepRunLoop_Throttle();
if (GlobalWin.DisplayManager.NeedsToPaint)
{
Render();
}
//CheckMessages();
Thread.Sleep(0);
}
}
private void Render()
{
if (_running)
{
//Invalidate doesn't force the update, which may skip frames, but Update blocks and slows things down.
//So invalidate wins for the moment, until we switch to OpenGL.
Application.Instance.Invoke(new Action(() => _viewport.Invalidate()));
GlobalWin.DisplayManager.NeedsToPaint = false;
}
}
private void StepRunLoop_Throttle()
{
SyncThrottle();
_throttle.signal_frameAdvance = _runloopFrameadvance;
_throttle.signal_continuousframeAdvancing = _runloopFrameProgress;
_throttle.Step(true, -1);
}
public void ProcessInput()
{
ControllerInputCoalescer conInput = Global.ControllerInputCoalescer as ControllerInputCoalescer;
for (; ; )
{
// loop through all available events
var ie = Input.Instance.DequeueEvent();
if (ie == null) { break; }
// useful debugging:
// Console.WriteLine(ie);
// TODO - wonder what happens if we pop up something interactive as a response to one of these hotkeys? may need to purge further processing
// look for hotkey bindings for this key
var triggers = Global.ClientControls.SearchBindings(ie.LogicalButton.ToString());
/*if (triggers.Count == 0)
{
// Maybe it is a system alt-key which hasnt been overridden
if (ie.EventType == Input.InputEventType.Press)
{
if (ie.LogicalButton.Alt && ie.LogicalButton.Button.Length == 1)
{
var c = ie.LogicalButton.Button.ToLower()[0];
if ((c >= 'a' && c <= 'z') || c == ' ')
{
SendAltKeyChar(c);
}
}
if (ie.LogicalButton.Alt && ie.LogicalButton.Button == "Space")
{
SendPlainAltKey(32);
}
}
// ordinarily, an alt release with nothing else would move focus to the menubar. but that is sort of useless, and hard to implement exactly right.
}*/
// zero 09-sep-2012 - all input is eligible for controller input. not sure why the above was done.
// maybe because it doesnt make sense to me to bind hotkeys and controller inputs to the same keystrokes
// adelikat 02-dec-2012 - implemented options for how to handle controller vs hotkey conflicts. This is primarily motivated by computer emulation and thus controller being nearly the entire keyboard
bool handled;
switch (Global.Config.Input_Hotkey_OverrideOptions)
{
default:
case 0: // Both allowed
conInput.Receive(ie);
handled = false;
if (ie.EventType == Input.InputEventType.Press)
{
handled = triggers.Aggregate(handled, (current, trigger) => current | CheckHotkey(trigger));
}
// hotkeys which arent handled as actions get coalesced as pollable virtual client buttons
if (!handled)
{
//HotkeyCoalescer.Receive(ie);
}
break;
case 1: // Input overrides Hokeys
conInput.Receive(ie);
if (!Global.ActiveController.HasBinding(ie.LogicalButton.ToString()))
{
handled = false;
if (ie.EventType == Input.InputEventType.Press)
{
handled = triggers.Aggregate(handled, (current, trigger) => current | CheckHotkey(trigger));
}
// hotkeys which arent handled as actions get coalesced as pollable virtual client buttons
if (!handled)
{
//HotkeyCoalescer.Receive(ie);
}
}
break;
case 2: // Hotkeys override Input
handled = false;
if (ie.EventType == Input.InputEventType.Press)
{
handled = triggers.Aggregate(handled, (current, trigger) => current | CheckHotkey(trigger));
}
// hotkeys which arent handled as actions get coalesced as pollable virtual client buttons
if (!handled)
{
//HotkeyCoalescer.Receive(ie);
conInput.Receive(ie);
}
break;
}
} // foreach event
// also handle floats
/*conInput.AcceptNewFloats(Input.Instance.GetFloats().Select(o =>
{
var video = Global.Emulator.VideoProvider();
// hackish
if (o.Item1 == "WMouse X")
{
var P = GlobalWin.DisplayManager.UntransformPoint(new Point((int)o.Item2, 0));
float x = P.X / (float)video.BufferWidth;
return new Tuple<string, float>("WMouse X", x * 20000 - 10000);
}
if (o.Item1 == "WMouse Y")
{
var P = GlobalWin.DisplayManager.UntransformPoint(new Point(0, (int)o.Item2));
float y = P.Y / (float)video.BufferHeight;
return new Tuple<string, float>("WMouse Y", y * 20000 - 10000);
}
return o;
}));*/
}
public bool IsLagFrame
{
get
{
/*if (Global.Emulator.CanPollInput())
{
return Global.Emulator.AsInputPollable().IsLagFrame;
}*/
return false;
}
}
private void StepRunLoop_Core(bool force = false)
{
var runFrame = false;
_runloopFrameadvance = false;
var currentTimestamp = Stopwatch.GetTimestamp();
var suppressCaptureRewind = false;
double frameAdvanceTimestampDeltaMs = (double)(currentTimestamp - _frameAdvanceTimestamp) / Stopwatch.Frequency * 1000.0;
bool frameProgressTimeElapsed = frameAdvanceTimestampDeltaMs >= Global.Config.FrameProgressDelayMs;
if (Global.Config.SkipLagFrame && IsLagFrame && frameProgressTimeElapsed && Global.Emulator.Frame > 0)
{
runFrame = true;
}
if (Global.ClientControls["Frame Advance"] || PressFrameAdvance)
{
// handle the initial trigger of a frame advance
if (_frameAdvanceTimestamp == 0)
{
PauseEmulator();
runFrame = true;
_runloopFrameadvance = true;
_frameAdvanceTimestamp = currentTimestamp;
}
else
{
// handle the timed transition from countdown to FrameProgress
if (frameProgressTimeElapsed)
{
runFrame = true;
_runloopFrameProgress = true;
UnpauseEmulator();
}
}
}
else
{
// handle release of frame advance: do we need to deactivate FrameProgress?
if (_runloopFrameProgress)
{
_runloopFrameProgress = false;
PauseEmulator();
}
_frameAdvanceTimestamp = 0;
}
if (!EmulatorPaused)
{
runFrame = true;
}
bool isRewinding = suppressCaptureRewind = false;// Rewind(ref runFrame, currentTimestamp);
if (UpdateFrame)
{
runFrame = true;
}
var genSound = false;
var coreskipaudio = false;
if (runFrame || force)
{
var isFastForwarding = Global.ClientControls["Fast Forward"] || IsTurboing;
var updateFpsString = _runloopLastFf != isFastForwarding;
_runloopLastFf = isFastForwarding;
// client input-related duties
//GlobalWin.OSD.ClearGUIText();
//Global.CheatList.Pulse();
//zero 03-may-2014 - moved this before call to UpdateToolsBefore(), since it seems to clear the state which a lua event.framestart is going to want to alter
Global.ClickyVirtualPadController.FrameTick();
Global.LuaAndAdaptor.FrameTick();
/*if (GlobalWin.Tools.Has<LuaConsole>())
{
GlobalWin.Tools.LuaConsole.LuaImp.CallFrameBeforeEvent();
}*/
/*if (!IsTurboing)
{
GlobalWin.Tools.UpdateToolsBefore();
}
else
{
GlobalWin.Tools.FastUpdateBefore();
}*/
_runloopFps++;
if ((double)(currentTimestamp - _runloopSecond) / Stopwatch.Frequency >= 1.0)
{
_runloopLastFps = _runloopFps;
_runloopSecond = currentTimestamp;
_runloopFps = 0;
updateFpsString = true;
}
if (updateFpsString)
{
var fps_string = _runloopLastFps + " fps";
if (isRewinding)
{
if (IsTurboing || isFastForwarding)
{
fps_string += " <<<<";
}
else
{
fps_string += " <<";
}
}
else if (IsTurboing)
{
fps_string += " >>>>";
}
else if (isFastForwarding)
{
fps_string += " >>";
}
//GlobalWin.OSD.FPS = fps_string;
}
//CaptureRewind(suppressCaptureRewind);
if (!_runloopFrameadvance)
{
genSound = true;
}
else if (!Global.Config.MuteFrameAdvance)
{
genSound = true;
}
Global.MovieSession.HandleMovieOnFrameLoop();
coreskipaudio = IsTurboing;// && _currAviWriter == null;
{
bool render = !_throttle.skipnextframe;// || _currAviWriter != null;
bool renderSound = !coreskipaudio;
Global.Emulator.FrameAdvance(Global.ActiveController, render, renderSound);
}
Global.MovieSession.HandleMovieAfterFrameLoop();
GlobalWin.DisplayManager.NeedsToPaint = true;
//Global.CheatList.Pulse();
/*if (!PauseAVI)
{
AvFrameAdvance();
}*/
if (IsLagFrame && Global.Config.AutofireLagFrames)
{
Global.AutoFireController.IncrementStarts();
}
Global.AutofireStickyXORAdapter.IncrementLoops(IsLagFrame);
PressFrameAdvance = false;
/*if (GlobalWin.Tools.Has<LuaConsole>())
{
GlobalWin.Tools.LuaConsole.LuaImp.CallFrameAfterEvent();
}*/
/*if (!IsTurboing)
{
UpdateToolsAfter();
}
else
{
GlobalWin.Tools.FastUpdateAfter();
}
if (IsSeeking && Global.Emulator.Frame == PauseOnFrame.Value)
{
PauseEmulator();
PauseOnFrame = null;
}*/
}
if (Global.ClientControls["Rewind"] || PressRewind)
{
//UpdateToolsAfter();
PressRewind = false;
}
if (UpdateFrame)
{
UpdateFrame = false;
}
bool outputSilence = !genSound || coreskipaudio;
GlobalWin.Sound.UpdateSound(0.5f);
}
private void SyncThrottle()
{
// "unthrottled" = throttle was turned off with "Toggle Throttle" hotkey
// "turbo" = throttle is off due to the "Turbo" hotkey being held
// They are basically the same thing but one is a toggle and the other requires a
// hotkey to be held. There is however slightly different behavior in that turbo
// skips outputting the audio. There's also a third way which is when no throttle
// method is selected, but the clock throttle determines that by itself and
// everything appears normal here.
var rewind = Global.Rewinder.RewindActive && (Global.ClientControls["Rewind"] || PressRewind);
var fastForward = Global.ClientControls["Fast Forward"] || FastForward;
var turbo = IsTurboing;
int speedPercent = fastForward ? Global.Config.SpeedPercentAlternate : Global.Config.SpeedPercent;
if (rewind)
{
speedPercent = Math.Max(speedPercent * Global.Config.RewindSpeedMultiplier / Global.Rewinder.RewindFrequency, 5);
}
Global.DisableSecondaryThrottling = _unthrottled || turbo || fastForward || rewind;
// realtime throttle is never going to be so exact that using a double here is wrong
_throttle.SetCoreFps(60.0);
_throttle.signal_paused = EmulatorPaused;
_throttle.signal_unthrottle = _unthrottled || turbo;
_throttle.signal_overrideSecondaryThrottle = (fastForward || rewind) && (Global.Config.SoundThrottle || Global.Config.VSyncThrottle || Global.Config.VSync);
_throttle.SetSpeedPercent(speedPercent);
}
public string CurrentlyOpenRom;
public bool PauseAVI = false;
public bool PressFrameAdvance = false;
public bool PressRewind = false;
public bool FastForward = false;
public bool TurboFastForward = false;
public bool RestoreReadWriteOnStop = false;
public bool UpdateFrame = false;
public bool AllowInput = true;
private int? _pauseOnFrame;
public int? PauseOnFrame // If set, upon completion of this frame, the client wil pause
{
get { return _pauseOnFrame; }
set
{
_pauseOnFrame = value;
//SetPauseStatusbarIcon();
if (value == null) // TODO: make an Event handler instead, but the logic here is that after turbo seeking, tools will want to do a real update when the emulator finally pauses
{
//GlobalWin.Tools.UpdateToolsBefore();
//GlobalWin.Tools.UpdateToolsAfter();
}
}
}
public bool IsSeeking
{
get { return PauseOnFrame.HasValue; }
}
public bool IsTurboSeeking
{
get
{
return PauseOnFrame.HasValue && Global.Config.TurboSeek;
}
}
public bool IsTurboing
{
get
{
return Global.ClientControls["Turbo"] || IsTurboSeeking;
}
}
#region Pause
private bool _emulatorPaused;
public bool EmulatorPaused
{
get
{
return _emulatorPaused;
}
private set
{
_emulatorPaused = value;
if (OnPauseChanged != null)
{
OnPauseChanged(this, new PauseChangedEventArgs(_emulatorPaused));
}
}
}
public delegate void PauseChangedEventHandler(object sender, PauseChangedEventArgs e);
public event PauseChangedEventHandler OnPauseChanged;
public class PauseChangedEventArgs : EventArgs
{
public PauseChangedEventArgs(bool paused)
{
Paused = paused;
}
public bool Paused { get; private set; }
}
public void PauseEmulator()
{
EmulatorPaused = true;
//SetPauseStatusbarIcon();
}
public void UnpauseEmulator()
{
EmulatorPaused = false;
//SetPauseStatusbarIcon();
}
public void TogglePause()
{
EmulatorPaused ^= true;
//SetPauseStatusbarIcon();
// TODO: have tastudio set a pause status change callback, or take control over pause
/*if (GlobalWin.Tools.Has<TAStudio>())
{
GlobalWin.Tools.UpdateValues<TAStudio>();
}*/
}
#endregion
private void MainForm_Closed(object sender, EventArgs e)
{
if (_running)
{
Shutdown();
}
}
private void Shutdown()
{
_running = false;
_worker.Join(5000);
Application.Instance.Quit();
}
private void viewport_Paint(object sender, PaintEventArgs e)
{
if (Global.Emulator != null)
{
var video = Global.Emulator.AsVideoProviderOrDefault();
Bitmap img = new Bitmap(video.BufferWidth, video.BufferHeight, PixelFormat.Format32bppRgb);
BitmapData data = img.Lock();
int[] buffer = (int[])(video.GetVideoBuffer().Clone());
//Buffer is cloned to prevent tearing. The emulation thread is running independent of drawing,
//does not block, can (and will) update the framebuffer while we draw it.
if (img.Platform.IsMac)
{
//Colors are reversed on OSX, even though it's Little Endian just like on Windows.
//I think this is a bug in Eto framework. This hack won't be needed when I bring back OpenGL, or if Eto gets fixed.
for (int i = 0; i < buffer.Length; i++)
{
int x = buffer [i];
x = (x >> 16 & 0xFF) + (x & 0xFF00) + ((x << 16) & 0xFF0000);
buffer[i] = x;
}
}
Marshal.Copy(buffer, 0, data.Data, buffer.Length);
data.Dispose();
e.Graphics.DrawImage(img, 0, 0, _viewport.Width, _viewport.Height);
}
else
{
e.Graphics.FillRectangle(Brushes.Black, new RectangleF(0, 0, _viewport.Width, _viewport.Height));
}
}
private void OpenRom()
{
OpenFileDialog ofd = new OpenFileDialog();
_suspended = true;
if (ofd.ShowDialog(this) == DialogResult.Ok)
{
_suspended = false;
RomLoader loader = new RomLoader();
var nextComm = CreateCoreComm();
CoreFileProvider.SyncCoreCommInputSignals(nextComm);
bool result = loader.LoadRom(ofd.FileName, nextComm);
if (result)
{
Global.Emulator = loader.LoadedEmulator;
Global.Game = loader.Game;
CoreFileProvider.SyncCoreCommInputSignals(nextComm);
InputManager.SyncControls();
/*if (Global.Emulator is TI83 && Global.Config.TI83autoloadKeyPad)
{
GlobalWin.Tools.Load<TI83KeyPad>();
}*/
if (loader.LoadedEmulator is NES)
{
var nes = loader.LoadedEmulator as NES;
if (!string.IsNullOrWhiteSpace(nes.GameName))
{
Global.Game.Name = nes.GameName;
}
Global.Game.Status = nes.RomStatus;
}
else if (loader.LoadedEmulator is QuickNES)
{
var qns = loader.LoadedEmulator as QuickNES;
if (!string.IsNullOrWhiteSpace(qns.BootGodName))
{
Global.Game.Name = qns.BootGodName;
}
if (qns.BootGodStatus.HasValue)
{
Global.Game.Status = qns.BootGodStatus.Value;
}
}
//Global.Rewinder.ResetRewindBuffer();
/*if (Global.Emulator.CoreComm.RomStatusDetails == null && loader.Rom != null)
{
Global.Emulator.CoreComm.RomStatusDetails = string.Format(
"{0}\r\nSHA1:{1}\r\nMD5:{2}\r\n",
loader.Game.Name,
loader.Rom.RomData.HashSHA1(),
loader.Rom.RomData.HashMD5());
}*/
/*if (Global.Emulator.BoardName != null)
{
Console.WriteLine("Core reported BoardID: \"{0}\"", Global.Emulator.BoardName);
}*/
// restarts the lua console if a different rom is loaded.
// im not really a fan of how this is done..
/*if (Global.Config.RecentRoms.Empty || Global.Config.RecentRoms.MostRecent != loader.CanonicalFullPath)
{
GlobalWin.Tools.Restart<LuaConsole>();
}*/
Global.Config.RecentRoms.Add(loader.CanonicalFullPath);
//JumpLists.AddRecentItem(loader.CanonicalFullPath);
// Don't load Save Ram if a movie is being loaded
/*if (!Global.MovieSession.MovieIsQueued && File.Exists(PathManager.SaveRamPath(loader.Game)))
{
LoadSaveRam();
}
GlobalWin.Tools.Restart();
if (Global.Config.LoadCheatFileByGame)
{
if (Global.CheatList.AttemptToLoadCheatFile())
{
GlobalWin.OSD.AddMessage("Cheats file loaded");
}
}
SetWindowText();
CurrentlyOpenRom = loader.CanonicalFullPath;
HandlePlatformMenus();
_stateSlots.Clear();
UpdateCoreStatusBarButton();
UpdateDumpIcon();
SetMainformMovieInfo();
*/
//Global.Rewinder.CaptureRewindState();
Global.StickyXORAdapter.ClearStickies();
Global.StickyXORAdapter.ClearStickyFloats();
Global.AutofireStickyXORAdapter.ClearStickies();
RewireSound();
/*ToolHelpers.UpdateCheatRelatedTools(null, null);
if (Global.Config.AutoLoadLastSaveSlot && _stateSlots.HasSlot(Global.Config.SaveSlot))
{
LoadQuickSave("QuickSave" + Global.Config.SaveSlot);
}
if (Global.FirmwareManager.RecentlyServed.Count > 0)
{
Console.WriteLine("Active Firmwares:");
foreach (var f in Global.FirmwareManager.RecentlyServed)
{
Console.WriteLine(" {0} : {1}", f.FirmwareId, f.Hash);
}
}*/
EnableControls();
//return true;
}
}
_suspended = false;
}
CoreComm CreateCoreComm()
{
CoreComm ret = new CoreComm(ShowMessageCoreComm, NotifyCoreComm);
//ret.RequestGLContext = () => GlobalWin.GLManager.CreateGLContext();
//ret.ActivateGLContext = (gl) => GlobalWin.GLManager.Activate((GLManager.ContextRef)gl);
//ret.DeactivateGLContext = () => GlobalWin.GLManager.Deactivate();
return ret;
}
private void ShowMessageCoreComm(string message)
{
MessageBox.Show(Application.Instance.MainForm, message, "Warning", MessageBoxButtons.OK);
}
private void NotifyCoreComm(string message)
{
//GlobalWin.OSD.AddMessage(message);
}
}
}