MainWindow.cs 48 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. using ARMeilleure.Translation;
  2. using ARMeilleure.Translation.PTC;
  3. using Gtk;
  4. using LibHac.Common;
  5. using LibHac.Ns;
  6. using Ryujinx.Audio;
  7. using Ryujinx.Audio.Backends.Dummy;
  8. using Ryujinx.Audio.Backends.OpenAL;
  9. using Ryujinx.Audio.Backends.SoundIo;
  10. using Ryujinx.Audio.Integration;
  11. using Ryujinx.Common.Configuration;
  12. using Ryujinx.Common.Logging;
  13. using Ryujinx.Common.System;
  14. using Ryujinx.Configuration;
  15. using Ryujinx.Graphics.GAL;
  16. using Ryujinx.Graphics.OpenGL;
  17. using Ryujinx.HLE.FileSystem;
  18. using Ryujinx.HLE.FileSystem.Content;
  19. using Ryujinx.HLE.HOS;
  20. using Ryujinx.Modules;
  21. using Ryujinx.Ui.App;
  22. using Ryujinx.Ui.Applet;
  23. using Ryujinx.Ui.Helper;
  24. using Ryujinx.Ui.Widgets;
  25. using Ryujinx.Ui.Windows;
  26. using System;
  27. using System.Diagnostics;
  28. using System.IO;
  29. using System.Reflection;
  30. using System.Runtime.InteropServices;
  31. using System.Threading;
  32. using System.Threading.Tasks;
  33. using GUI = Gtk.Builder.ObjectAttribute;
  34. namespace Ryujinx.Ui
  35. {
  36. public class MainWindow : Window
  37. {
  38. private readonly VirtualFileSystem _virtualFileSystem;
  39. private readonly ContentManager _contentManager;
  40. private UserChannelPersistence _userChannelPersistence;
  41. private HLE.Switch _emulationContext;
  42. private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution;
  43. private readonly ApplicationLibrary _applicationLibrary;
  44. private readonly GtkHostUiHandler _uiHandler;
  45. private readonly AutoResetEvent _deviceExitStatus;
  46. private readonly ListStore _tableStore;
  47. private bool _updatingGameTable;
  48. private bool _gameLoaded;
  49. private bool _ending;
  50. private string _currentEmulatedGamePath = null;
  51. public GlRenderer GlRendererWidget;
  52. #pragma warning disable CS0169, CS0649, IDE0044
  53. [GUI] public MenuItem ExitMenuItem;
  54. [GUI] public MenuItem UpdateMenuItem;
  55. [GUI] MenuBar _menuBar;
  56. [GUI] Box _footerBox;
  57. [GUI] Box _statusBar;
  58. [GUI] MenuItem _stopEmulation;
  59. [GUI] MenuItem _simulateWakeUpMessage;
  60. [GUI] MenuItem _fullScreen;
  61. [GUI] CheckMenuItem _startFullScreen;
  62. [GUI] CheckMenuItem _favToggle;
  63. [GUI] MenuItem _firmwareInstallDirectory;
  64. [GUI] MenuItem _firmwareInstallFile;
  65. [GUI] Label _fifoStatus;
  66. [GUI] CheckMenuItem _iconToggle;
  67. [GUI] CheckMenuItem _developerToggle;
  68. [GUI] CheckMenuItem _appToggle;
  69. [GUI] CheckMenuItem _timePlayedToggle;
  70. [GUI] CheckMenuItem _versionToggle;
  71. [GUI] CheckMenuItem _lastPlayedToggle;
  72. [GUI] CheckMenuItem _fileExtToggle;
  73. [GUI] CheckMenuItem _pathToggle;
  74. [GUI] CheckMenuItem _fileSizeToggle;
  75. [GUI] Label _dockedMode;
  76. [GUI] Label _aspectRatio;
  77. [GUI] Label _gameStatus;
  78. [GUI] TreeView _gameTable;
  79. [GUI] TreeSelection _gameTableSelection;
  80. [GUI] ScrolledWindow _gameTableWindow;
  81. [GUI] Label _gpuName;
  82. [GUI] Label _progressLabel;
  83. [GUI] Label _firmwareVersionLabel;
  84. [GUI] ProgressBar _progressBar;
  85. [GUI] Box _viewBox;
  86. [GUI] Label _vSyncStatus;
  87. [GUI] Box _listStatusBox;
  88. [GUI] Label _loadingStatusLabel;
  89. [GUI] ProgressBar _loadingStatusBar;
  90. private string _loadingStatusTitle = "";
  91. #pragma warning restore CS0649, IDE0044, CS0169
  92. public MainWindow() : this(new Builder("Ryujinx.Ui.MainWindow.glade")) { }
  93. private MainWindow(Builder builder) : base(builder.GetObject("_mainWin").Handle)
  94. {
  95. builder.Autoconnect(this);
  96. // Apply custom theme if needed.
  97. ThemeHelper.ApplyTheme();
  98. // Sets overridden fields.
  99. int monitorWidth = Display.PrimaryMonitor.Geometry.Width * Display.PrimaryMonitor.ScaleFactor;
  100. int monitorHeight = Display.PrimaryMonitor.Geometry.Height * Display.PrimaryMonitor.ScaleFactor;
  101. DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280;
  102. DefaultHeight = monitorHeight < 760 ? monitorHeight : 760;
  103. Icon = new Gdk.Pixbuf(Assembly.GetExecutingAssembly(), "Ryujinx.Ui.Resources.Logo_Ryujinx.png");
  104. Title = $"Ryujinx {Program.Version}";
  105. // Hide emulation context status bar.
  106. _statusBar.Hide();
  107. // Instanciate HLE objects.
  108. _virtualFileSystem = VirtualFileSystem.CreateInstance();
  109. _contentManager = new ContentManager(_virtualFileSystem);
  110. _userChannelPersistence = new UserChannelPersistence();
  111. // Instanciate GUI objects.
  112. _applicationLibrary = new ApplicationLibrary(_virtualFileSystem);
  113. _uiHandler = new GtkHostUiHandler(this);
  114. _deviceExitStatus = new AutoResetEvent(false);
  115. WindowStateEvent += WindowStateEvent_Changed;
  116. DeleteEvent += Window_Close;
  117. _applicationLibrary.ApplicationAdded += Application_Added;
  118. _applicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated;
  119. _gameTable.ButtonReleaseEvent += Row_Clicked;
  120. _fullScreen.Activated += FullScreen_Toggled;
  121. GlRenderer.StatusUpdatedEvent += Update_StatusBar;
  122. if (ConfigurationState.Instance.Ui.StartFullscreen)
  123. {
  124. _startFullScreen.Active = true;
  125. }
  126. _stopEmulation.Sensitive = false;
  127. _simulateWakeUpMessage.Sensitive = false;
  128. if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _favToggle.Active = true;
  129. if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _iconToggle.Active = true;
  130. if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _appToggle.Active = true;
  131. if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _developerToggle.Active = true;
  132. if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _versionToggle.Active = true;
  133. if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _timePlayedToggle.Active = true;
  134. if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _lastPlayedToggle.Active = true;
  135. if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _fileExtToggle.Active = true;
  136. if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _fileSizeToggle.Active = true;
  137. if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _pathToggle.Active = true;
  138. _favToggle.Toggled += Fav_Toggled;
  139. _iconToggle.Toggled += Icon_Toggled;
  140. _appToggle.Toggled += App_Toggled;
  141. _developerToggle.Toggled += Developer_Toggled;
  142. _versionToggle.Toggled += Version_Toggled;
  143. _timePlayedToggle.Toggled += TimePlayed_Toggled;
  144. _lastPlayedToggle.Toggled += LastPlayed_Toggled;
  145. _fileExtToggle.Toggled += FileExt_Toggled;
  146. _fileSizeToggle.Toggled += FileSize_Toggled;
  147. _pathToggle.Toggled += Path_Toggled;
  148. _gameTable.Model = _tableStore = new ListStore(
  149. typeof(bool),
  150. typeof(Gdk.Pixbuf),
  151. typeof(string),
  152. typeof(string),
  153. typeof(string),
  154. typeof(string),
  155. typeof(string),
  156. typeof(string),
  157. typeof(string),
  158. typeof(string),
  159. typeof(BlitStruct<ApplicationControlProperty>));
  160. _tableStore.SetSortFunc(5, SortHelper.TimePlayedSort);
  161. _tableStore.SetSortFunc(6, SortHelper.LastPlayedSort);
  162. _tableStore.SetSortFunc(8, SortHelper.FileSizeSort);
  163. int columnId = ConfigurationState.Instance.Ui.ColumnSort.SortColumnId;
  164. bool ascending = ConfigurationState.Instance.Ui.ColumnSort.SortAscending;
  165. _tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending);
  166. _gameTable.EnableSearch = true;
  167. _gameTable.SearchColumn = 2;
  168. UpdateColumns();
  169. UpdateGameTable();
  170. ConfigurationState.Instance.Ui.GameDirs.Event += (sender, args) =>
  171. {
  172. if (args.OldValue != args.NewValue)
  173. {
  174. UpdateGameTable();
  175. }
  176. };
  177. Task.Run(RefreshFirmwareLabel);
  178. }
  179. private void WindowStateEvent_Changed(object o, WindowStateEventArgs args)
  180. {
  181. _fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen";
  182. }
  183. private void UpdateColumns()
  184. {
  185. foreach (TreeViewColumn column in _gameTable.Columns)
  186. {
  187. _gameTable.RemoveColumn(column);
  188. }
  189. CellRendererToggle favToggle = new CellRendererToggle();
  190. favToggle.Toggled += FavToggle_Toggled;
  191. if (ConfigurationState.Instance.Ui.GuiColumns.FavColumn) _gameTable.AppendColumn("Fav", favToggle, "active", 0);
  192. if (ConfigurationState.Instance.Ui.GuiColumns.IconColumn) _gameTable.AppendColumn("Icon", new CellRendererPixbuf(), "pixbuf", 1);
  193. if (ConfigurationState.Instance.Ui.GuiColumns.AppColumn) _gameTable.AppendColumn("Application", new CellRendererText(), "text", 2);
  194. if (ConfigurationState.Instance.Ui.GuiColumns.DevColumn) _gameTable.AppendColumn("Developer", new CellRendererText(), "text", 3);
  195. if (ConfigurationState.Instance.Ui.GuiColumns.VersionColumn) _gameTable.AppendColumn("Version", new CellRendererText(), "text", 4);
  196. if (ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn) _gameTable.AppendColumn("Time Played", new CellRendererText(), "text", 5);
  197. if (ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn) _gameTable.AppendColumn("Last Played", new CellRendererText(), "text", 6);
  198. if (ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn) _gameTable.AppendColumn("File Ext", new CellRendererText(), "text", 7);
  199. if (ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn) _gameTable.AppendColumn("File Size", new CellRendererText(), "text", 8);
  200. if (ConfigurationState.Instance.Ui.GuiColumns.PathColumn) _gameTable.AppendColumn("Path", new CellRendererText(), "text", 9);
  201. foreach (TreeViewColumn column in _gameTable.Columns)
  202. {
  203. switch (column.Title)
  204. {
  205. case "Fav":
  206. column.SortColumnId = 0;
  207. column.Clicked += Column_Clicked;
  208. break;
  209. case "Application":
  210. column.SortColumnId = 2;
  211. column.Clicked += Column_Clicked;
  212. break;
  213. case "Developer":
  214. column.SortColumnId = 3;
  215. column.Clicked += Column_Clicked;
  216. break;
  217. case "Version":
  218. column.SortColumnId = 4;
  219. column.Clicked += Column_Clicked;
  220. break;
  221. case "Time Played":
  222. column.SortColumnId = 5;
  223. column.Clicked += Column_Clicked;
  224. break;
  225. case "Last Played":
  226. column.SortColumnId = 6;
  227. column.Clicked += Column_Clicked;
  228. break;
  229. case "File Ext":
  230. column.SortColumnId = 7;
  231. column.Clicked += Column_Clicked;
  232. break;
  233. case "File Size":
  234. column.SortColumnId = 8;
  235. column.Clicked += Column_Clicked;
  236. break;
  237. case "Path":
  238. column.SortColumnId = 9;
  239. column.Clicked += Column_Clicked;
  240. break;
  241. }
  242. }
  243. }
  244. private void InitializeSwitchInstance()
  245. {
  246. _virtualFileSystem.Reload();
  247. IRenderer renderer = new Renderer();
  248. IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver();
  249. if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo)
  250. {
  251. if (SoundIoHardwareDeviceDriver.IsSupported)
  252. {
  253. deviceDriver = new SoundIoHardwareDeviceDriver();
  254. }
  255. else
  256. {
  257. Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out.");
  258. }
  259. }
  260. else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl)
  261. {
  262. if (OpenALHardwareDeviceDriver.IsSupported)
  263. {
  264. deviceDriver = new OpenALHardwareDeviceDriver();
  265. }
  266. else
  267. {
  268. Logger.Warning?.Print(LogClass.Audio, "OpenAL is not supported, trying to fall back to SoundIO.");
  269. if (SoundIoHardwareDeviceDriver.IsSupported)
  270. {
  271. Logger.Warning?.Print(LogClass.Audio, "Found SoundIO, changing configuration.");
  272. ConfigurationState.Instance.System.AudioBackend.Value = AudioBackend.SoundIo;
  273. SaveConfig();
  274. deviceDriver = new SoundIoHardwareDeviceDriver();
  275. }
  276. else
  277. {
  278. Logger.Warning?.Print(LogClass.Audio, "SoundIO is not supported, falling back to dummy audio out.");
  279. }
  280. }
  281. }
  282. _emulationContext = new HLE.Switch(_virtualFileSystem, _contentManager, _userChannelPersistence, renderer, deviceDriver)
  283. {
  284. UiHandler = _uiHandler
  285. };
  286. _emulationContext.Initialize();
  287. }
  288. private void SetupProgressUiHandlers()
  289. {
  290. Ptc.PtcTranslationStateChanged -= PtcStatusChanged;
  291. Ptc.PtcTranslationStateChanged += PtcStatusChanged;
  292. Ptc.PtcTranslationProgressChanged -= LoadingProgressChanged;
  293. Ptc.PtcTranslationProgressChanged += LoadingProgressChanged;
  294. _emulationContext.Gpu.ShaderCacheStateChanged -= ShaderCacheStatusChanged;
  295. _emulationContext.Gpu.ShaderCacheStateChanged += ShaderCacheStatusChanged;
  296. _emulationContext.Gpu.ShaderCacheProgressChanged -= LoadingProgressChanged;
  297. _emulationContext.Gpu.ShaderCacheProgressChanged += LoadingProgressChanged;
  298. }
  299. private void ShaderCacheStatusChanged(bool state)
  300. {
  301. _loadingStatusTitle = "Shaders";
  302. Application.Invoke(delegate
  303. {
  304. _loadingStatusBar.Visible = _loadingStatusLabel.Visible = state;
  305. });
  306. }
  307. private void PtcStatusChanged(bool state)
  308. {
  309. _loadingStatusTitle = "PTC";
  310. Application.Invoke(delegate
  311. {
  312. _loadingStatusBar.Visible = _loadingStatusLabel.Visible = state;
  313. });
  314. }
  315. private void LoadingProgressChanged(int value, int total)
  316. {
  317. Application.Invoke(delegate
  318. {
  319. _loadingStatusBar.Fraction = (double)value / total;
  320. _loadingStatusLabel.Text = $"{_loadingStatusTitle} : {value}/{total}";
  321. });
  322. }
  323. public void UpdateGameTable()
  324. {
  325. if (_updatingGameTable || _gameLoaded)
  326. {
  327. return;
  328. }
  329. _updatingGameTable = true;
  330. _tableStore.Clear();
  331. Thread applicationLibraryThread = new Thread(() =>
  332. {
  333. _applicationLibrary.LoadApplications(ConfigurationState.Instance.Ui.GameDirs, ConfigurationState.Instance.System.Language);
  334. _updatingGameTable = false;
  335. });
  336. applicationLibraryThread.Name = "GUI.ApplicationLibraryThread";
  337. applicationLibraryThread.IsBackground = true;
  338. applicationLibraryThread.Start();
  339. }
  340. [Conditional("RELEASE")]
  341. public void PerformanceCheck()
  342. {
  343. if (ConfigurationState.Instance.Logger.EnableDebug.Value)
  344. {
  345. MessageDialog debugWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
  346. {
  347. Title = "Ryujinx - Warning",
  348. Text = "You have debug logging enabled, which is designed to be used by developers only.",
  349. SecondaryText = "For optimal performance, it's recommended to disable debug logging. Would you like to disable debug logging now?"
  350. };
  351. if (debugWarningDialog.Run() == (int)ResponseType.Yes)
  352. {
  353. ConfigurationState.Instance.Logger.EnableDebug.Value = false;
  354. SaveConfig();
  355. }
  356. debugWarningDialog.Dispose();
  357. }
  358. if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value))
  359. {
  360. MessageDialog shadersDumpWarningDialog = new MessageDialog(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null)
  361. {
  362. Title = "Ryujinx - Warning",
  363. Text = "You have shader dumping enabled, which is designed to be used by developers only.",
  364. SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?"
  365. };
  366. if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes)
  367. {
  368. ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = "";
  369. SaveConfig();
  370. }
  371. shadersDumpWarningDialog.Dispose();
  372. }
  373. }
  374. public void LoadApplication(string path)
  375. {
  376. if (_gameLoaded)
  377. {
  378. GtkDialog.CreateInfoDialog("A game has already been loaded", "Please close it first and try again.");
  379. }
  380. else
  381. {
  382. PerformanceCheck();
  383. Logger.RestartTime();
  384. InitializeSwitchInstance();
  385. UpdateGraphicsConfig();
  386. SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
  387. bool isDirectory = Directory.Exists(path);
  388. if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError))
  389. {
  390. if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion))
  391. {
  392. if (userError == UserError.NoFirmware)
  393. {
  394. string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})";
  395. ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run();
  396. if (responseDialog != ResponseType.Yes)
  397. {
  398. UserErrorDialog.CreateUserErrorDialog(userError);
  399. _emulationContext.Dispose();
  400. return;
  401. }
  402. }
  403. if (!SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _))
  404. {
  405. UserErrorDialog.CreateUserErrorDialog(userError);
  406. _emulationContext.Dispose();
  407. return;
  408. }
  409. // Tell the user that we installed a firmware for them.
  410. if (userError == UserError.NoFirmware)
  411. {
  412. firmwareVersion = _contentManager.GetCurrentFirmwareVersion();
  413. RefreshFirmwareLabel();
  414. string message = $"No installed firmware was found but Ryujinx was able to install firmware {firmwareVersion.VersionString} from the provided game.\nThe emulator will now start.";
  415. GtkDialog.CreateInfoDialog($"Firmware {firmwareVersion.VersionString} was installed", message);
  416. }
  417. }
  418. else
  419. {
  420. UserErrorDialog.CreateUserErrorDialog(userError);
  421. _emulationContext.Dispose();
  422. return;
  423. }
  424. }
  425. Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}");
  426. if (Directory.Exists(path))
  427. {
  428. string[] romFsFiles = Directory.GetFiles(path, "*.istorage");
  429. if (romFsFiles.Length == 0)
  430. {
  431. romFsFiles = Directory.GetFiles(path, "*.romfs");
  432. }
  433. if (romFsFiles.Length > 0)
  434. {
  435. Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS.");
  436. _emulationContext.LoadCart(path, romFsFiles[0]);
  437. }
  438. else
  439. {
  440. Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS.");
  441. _emulationContext.LoadCart(path);
  442. }
  443. }
  444. else if (File.Exists(path))
  445. {
  446. switch (System.IO.Path.GetExtension(path).ToLowerInvariant())
  447. {
  448. case ".xci":
  449. Logger.Info?.Print(LogClass.Application, "Loading as XCI.");
  450. _emulationContext.LoadXci(path);
  451. break;
  452. case ".nca":
  453. Logger.Info?.Print(LogClass.Application, "Loading as NCA.");
  454. _emulationContext.LoadNca(path);
  455. break;
  456. case ".nsp":
  457. case ".pfs0":
  458. Logger.Info?.Print(LogClass.Application, "Loading as NSP.");
  459. _emulationContext.LoadNsp(path);
  460. break;
  461. default:
  462. Logger.Info?.Print(LogClass.Application, "Loading as homebrew.");
  463. try
  464. {
  465. _emulationContext.LoadProgram(path);
  466. }
  467. catch (ArgumentOutOfRangeException)
  468. {
  469. Logger.Error?.Print(LogClass.Application, "The file which you have specified is unsupported by Ryujinx.");
  470. }
  471. break;
  472. }
  473. }
  474. else
  475. {
  476. Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file.");
  477. _emulationContext.Dispose();
  478. return;
  479. }
  480. _currentEmulatedGamePath = path;
  481. _deviceExitStatus.Reset();
  482. SetupProgressUiHandlers();
  483. Translator.IsReadyForTranslation.Reset();
  484. #if MACOS_BUILD
  485. CreateGameWindow();
  486. #else
  487. Thread windowThread = new Thread(() =>
  488. {
  489. CreateGameWindow();
  490. })
  491. {
  492. Name = "GUI.WindowThread"
  493. };
  494. windowThread.Start();
  495. #endif
  496. _gameLoaded = true;
  497. _stopEmulation.Sensitive = true;
  498. _simulateWakeUpMessage.Sensitive = true;
  499. _firmwareInstallFile.Sensitive = false;
  500. _firmwareInstallDirectory.Sensitive = false;
  501. DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Application.TitleIdText, _emulationContext.Application.TitleName);
  502. _applicationLibrary.LoadAndSaveMetaData(_emulationContext.Application.TitleIdText, appMetadata =>
  503. {
  504. appMetadata.LastPlayed = DateTime.UtcNow.ToString();
  505. });
  506. }
  507. }
  508. private void CreateGameWindow()
  509. {
  510. if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
  511. {
  512. _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1);
  513. }
  514. DisplaySleep.Prevent();
  515. GlRendererWidget = new GlRenderer(_emulationContext, ConfigurationState.Instance.Logger.GraphicsDebugLevel);
  516. Application.Invoke(delegate
  517. {
  518. _viewBox.Remove(_gameTableWindow);
  519. GlRendererWidget.Expand = true;
  520. _viewBox.Child = GlRendererWidget;
  521. GlRendererWidget.ShowAll();
  522. EditFooterForGameRenderer();
  523. if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
  524. {
  525. ToggleExtraWidgets(false);
  526. }
  527. else if (ConfigurationState.Instance.Ui.StartFullscreen.Value)
  528. {
  529. FullScreen_Toggled(null, null);
  530. }
  531. });
  532. GlRendererWidget.WaitEvent.WaitOne();
  533. GlRendererWidget.Start();
  534. Ptc.Close();
  535. PtcProfiler.Stop();
  536. _emulationContext.Dispose();
  537. _deviceExitStatus.Set();
  538. // NOTE: Everything that is here will not be executed when you close the UI.
  539. Application.Invoke(delegate
  540. {
  541. if (Window.State.HasFlag(Gdk.WindowState.Fullscreen))
  542. {
  543. ToggleExtraWidgets(true);
  544. }
  545. GlRendererWidget.Exit();
  546. if(GlRendererWidget.Window != Window && GlRendererWidget.Window != null)
  547. {
  548. GlRendererWidget.Window.Dispose();
  549. }
  550. GlRendererWidget.Dispose();
  551. _windowsMultimediaTimerResolution?.Dispose();
  552. _windowsMultimediaTimerResolution = null;
  553. DisplaySleep.Restore();
  554. _viewBox.Remove(GlRendererWidget);
  555. _viewBox.Add(_gameTableWindow);
  556. _gameTableWindow.Expand = true;
  557. Window.Title = $"Ryujinx {Program.Version}";
  558. _emulationContext = null;
  559. _gameLoaded = false;
  560. GlRendererWidget = null;
  561. DiscordIntegrationModule.SwitchToMainMenu();
  562. RecreateFooterForMenu();
  563. UpdateColumns();
  564. UpdateGameTable();
  565. Task.Run(RefreshFirmwareLabel);
  566. Task.Run(HandleRelaunch);
  567. _stopEmulation.Sensitive = false;
  568. _simulateWakeUpMessage.Sensitive = false;
  569. _firmwareInstallFile.Sensitive = true;
  570. _firmwareInstallDirectory.Sensitive = true;
  571. });
  572. }
  573. private void RecreateFooterForMenu()
  574. {
  575. _listStatusBox.Show();
  576. _statusBar.Hide();
  577. }
  578. private void EditFooterForGameRenderer()
  579. {
  580. _listStatusBox.Hide();
  581. _statusBar.Show();
  582. }
  583. public void ToggleExtraWidgets(bool show)
  584. {
  585. if (GlRendererWidget != null)
  586. {
  587. if (show)
  588. {
  589. _menuBar.ShowAll();
  590. _footerBox.Show();
  591. _statusBar.Show();
  592. }
  593. else
  594. {
  595. _menuBar.Hide();
  596. _footerBox.Hide();
  597. }
  598. }
  599. }
  600. private void UpdateGameMetadata(string titleId)
  601. {
  602. if (_gameLoaded)
  603. {
  604. _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
  605. {
  606. DateTime lastPlayedDateTime = DateTime.Parse(appMetadata.LastPlayed);
  607. double sessionTimePlayed = DateTime.UtcNow.Subtract(lastPlayedDateTime).TotalSeconds;
  608. appMetadata.TimePlayed += Math.Round(sessionTimePlayed, MidpointRounding.AwayFromZero);
  609. });
  610. }
  611. }
  612. public void UpdateGraphicsConfig()
  613. {
  614. int resScale = ConfigurationState.Instance.Graphics.ResScale;
  615. float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom;
  616. Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale;
  617. Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy;
  618. Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath;
  619. Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache;
  620. }
  621. public void SaveConfig()
  622. {
  623. ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath);
  624. }
  625. private void End()
  626. {
  627. if (_ending)
  628. {
  629. return;
  630. }
  631. _ending = true;
  632. if (_emulationContext != null)
  633. {
  634. UpdateGameMetadata(_emulationContext.Application.TitleIdText);
  635. if (GlRendererWidget != null)
  636. {
  637. // We tell the widget that we are exiting.
  638. GlRendererWidget.Exit();
  639. // Wait for the other thread to dispose the HLE context before exiting.
  640. _deviceExitStatus.WaitOne();
  641. GlRendererWidget.Dispose();
  642. }
  643. }
  644. Dispose();
  645. Program.Exit();
  646. Application.Quit();
  647. }
  648. //
  649. // Events
  650. //
  651. private void Application_Added(object sender, ApplicationAddedEventArgs args)
  652. {
  653. Application.Invoke(delegate
  654. {
  655. _tableStore.AppendValues(
  656. args.AppData.Favorite,
  657. new Gdk.Pixbuf(args.AppData.Icon, 75, 75),
  658. $"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}",
  659. args.AppData.Developer,
  660. args.AppData.Version,
  661. args.AppData.TimePlayed,
  662. args.AppData.LastPlayed,
  663. args.AppData.FileExtension,
  664. args.AppData.FileSize,
  665. args.AppData.Path,
  666. args.AppData.ControlHolder);
  667. });
  668. }
  669. private void ApplicationCount_Updated(object sender, ApplicationCountUpdatedEventArgs args)
  670. {
  671. Application.Invoke(delegate
  672. {
  673. _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded";
  674. float barValue = 0;
  675. if (args.NumAppsFound != 0)
  676. {
  677. barValue = (float)args.NumAppsLoaded / args.NumAppsFound;
  678. }
  679. _progressBar.Fraction = barValue;
  680. // Reset the vertical scrollbar to the top when titles finish loading
  681. if (args.NumAppsLoaded == args.NumAppsFound)
  682. {
  683. _gameTableWindow.Vadjustment.Value = 0;
  684. }
  685. });
  686. }
  687. private void Update_StatusBar(object sender, StatusUpdatedEventArgs args)
  688. {
  689. Application.Invoke(delegate
  690. {
  691. _gameStatus.Text = args.GameStatus;
  692. _fifoStatus.Text = args.FifoStatus;
  693. _gpuName.Text = args.GpuName;
  694. _dockedMode.Text = args.DockedMode;
  695. _aspectRatio.Text = args.AspectRatio;
  696. if (args.VSyncEnabled)
  697. {
  698. _vSyncStatus.Attributes = new Pango.AttrList();
  699. _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(11822, 60138, 51657));
  700. }
  701. else
  702. {
  703. _vSyncStatus.Attributes = new Pango.AttrList();
  704. _vSyncStatus.Attributes.Insert(new Pango.AttrForeground(ushort.MaxValue, 17733, 21588));
  705. }
  706. });
  707. }
  708. private void FavToggle_Toggled(object sender, ToggledArgs args)
  709. {
  710. _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path));
  711. string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
  712. bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0);
  713. _tableStore.SetValue(treeIter, 0, newToggleValue);
  714. _applicationLibrary.LoadAndSaveMetaData(titleId, appMetadata =>
  715. {
  716. appMetadata.Favorite = newToggleValue;
  717. });
  718. }
  719. private void Column_Clicked(object sender, EventArgs args)
  720. {
  721. TreeViewColumn column = (TreeViewColumn)sender;
  722. ConfigurationState.Instance.Ui.ColumnSort.SortColumnId.Value = column.SortColumnId;
  723. ConfigurationState.Instance.Ui.ColumnSort.SortAscending.Value = column.SortOrder == SortType.Ascending;
  724. SaveConfig();
  725. }
  726. private void Row_Activated(object sender, RowActivatedArgs args)
  727. {
  728. _gameTableSelection.GetSelected(out TreeIter treeIter);
  729. string path = (string)_tableStore.GetValue(treeIter, 9);
  730. LoadApplication(path);
  731. }
  732. private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args)
  733. {
  734. _emulationContext.EnableDeviceVsync = !_emulationContext.EnableDeviceVsync;
  735. }
  736. private void DockedMode_Clicked(object sender, ButtonReleaseEventArgs args)
  737. {
  738. ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value;
  739. }
  740. private void AspectRatio_Clicked(object sender, ButtonReleaseEventArgs args)
  741. {
  742. AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
  743. ConfigurationState.Instance.Graphics.AspectRatio.Value = ((int)aspectRatio + 1) > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
  744. }
  745. private void Row_Clicked(object sender, ButtonReleaseEventArgs args)
  746. {
  747. if (args.Event.Button != 3 /* Right Click */)
  748. {
  749. return;
  750. }
  751. _gameTableSelection.GetSelected(out TreeIter treeIter);
  752. if (treeIter.UserData == IntPtr.Zero)
  753. {
  754. return;
  755. }
  756. string titleFilePath = _tableStore.GetValue(treeIter, 9).ToString();
  757. string titleName = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[0];
  758. string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower();
  759. BlitStruct<ApplicationControlProperty> controlData = (BlitStruct<ApplicationControlProperty>)_tableStore.GetValue(treeIter, 10);
  760. _ = new GameTableContextMenu(this, _virtualFileSystem, titleFilePath, titleName, titleId, controlData);
  761. }
  762. private void Load_Application_File(object sender, EventArgs args)
  763. {
  764. using (FileChooserDialog fileChooser = new FileChooserDialog("Choose the file to open", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept))
  765. {
  766. fileChooser.Filter = new FileFilter();
  767. fileChooser.Filter.AddPattern("*.nsp");
  768. fileChooser.Filter.AddPattern("*.pfs0");
  769. fileChooser.Filter.AddPattern("*.xci");
  770. fileChooser.Filter.AddPattern("*.nca");
  771. fileChooser.Filter.AddPattern("*.nro");
  772. fileChooser.Filter.AddPattern("*.nso");
  773. if (fileChooser.Run() == (int)ResponseType.Accept)
  774. {
  775. LoadApplication(fileChooser.Filename);
  776. }
  777. }
  778. }
  779. private void Load_Application_Folder(object sender, EventArgs args)
  780. {
  781. using (FileChooserDialog fileChooser = new FileChooserDialog("Choose the folder to open", this, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept))
  782. {
  783. if (fileChooser.Run() == (int)ResponseType.Accept)
  784. {
  785. LoadApplication(fileChooser.Filename);
  786. }
  787. }
  788. }
  789. private void Open_Ryu_Folder(object sender, EventArgs args)
  790. {
  791. OpenHelper.OpenFolder(AppDataManager.BaseDirPath);
  792. }
  793. private void OpenLogsFolder_Pressed(object sender, EventArgs args)
  794. {
  795. string logPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs");
  796. new DirectoryInfo(logPath).Create();
  797. OpenHelper.OpenFolder(logPath);
  798. }
  799. private void Exit_Pressed(object sender, EventArgs args)
  800. {
  801. if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
  802. {
  803. End();
  804. }
  805. }
  806. private void Window_Close(object sender, DeleteEventArgs args)
  807. {
  808. if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog())
  809. {
  810. End();
  811. }
  812. else
  813. {
  814. args.RetVal = true;
  815. }
  816. }
  817. private void StopEmulation_Pressed(object sender, EventArgs args)
  818. {
  819. GlRendererWidget?.Exit();
  820. }
  821. private void Installer_File_Pressed(object o, EventArgs args)
  822. {
  823. FileChooserDialog fileChooser = new FileChooserDialog("Choose the firmware file to open", this, FileChooserAction.Open, "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept);
  824. fileChooser.Filter = new FileFilter();
  825. fileChooser.Filter.AddPattern("*.zip");
  826. fileChooser.Filter.AddPattern("*.xci");
  827. HandleInstallerDialog(fileChooser);
  828. }
  829. private void Installer_Directory_Pressed(object o, EventArgs args)
  830. {
  831. FileChooserDialog directoryChooser = new FileChooserDialog("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Cancel", ResponseType.Cancel, "Open", ResponseType.Accept);
  832. HandleInstallerDialog(directoryChooser);
  833. }
  834. private void HandleInstallerDialog(FileChooserDialog fileChooser)
  835. {
  836. if (fileChooser.Run() == (int)ResponseType.Accept)
  837. {
  838. try
  839. {
  840. string filename = fileChooser.Filename;
  841. fileChooser.Dispose();
  842. SystemVersion firmwareVersion = _contentManager.VerifyFirmwarePackage(filename);
  843. string dialogTitle = $"Install Firmware {firmwareVersion.VersionString}";
  844. if (firmwareVersion == null)
  845. {
  846. GtkDialog.CreateErrorDialog($"A valid system firmware was not found in {filename}.");
  847. return;
  848. }
  849. SystemVersion currentVersion = _contentManager.GetCurrentFirmwareVersion();
  850. string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed.";
  851. if (currentVersion != null)
  852. {
  853. dialogMessage += $"\n\nThis will replace the current system version {currentVersion.VersionString}. ";
  854. }
  855. dialogMessage += "\n\nDo you want to continue?";
  856. ResponseType responseInstallDialog = (ResponseType)GtkDialog.CreateConfirmationDialog(dialogTitle, dialogMessage).Run();
  857. MessageDialog waitingDialog = GtkDialog.CreateWaitingDialog(dialogTitle, "Installing firmware...");
  858. if (responseInstallDialog == ResponseType.Yes)
  859. {
  860. Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}");
  861. Thread thread = new Thread(() =>
  862. {
  863. Application.Invoke(delegate
  864. {
  865. waitingDialog.Run();
  866. });
  867. try
  868. {
  869. _contentManager.InstallFirmware(filename);
  870. Application.Invoke(delegate
  871. {
  872. waitingDialog.Dispose();
  873. string message = $"System version {firmwareVersion.VersionString} successfully installed.";
  874. GtkDialog.CreateInfoDialog(dialogTitle, message);
  875. Logger.Info?.Print(LogClass.Application, message);
  876. });
  877. }
  878. catch (Exception ex)
  879. {
  880. Application.Invoke(delegate
  881. {
  882. waitingDialog.Dispose();
  883. GtkDialog.CreateErrorDialog(ex.Message);
  884. });
  885. }
  886. finally
  887. {
  888. RefreshFirmwareLabel();
  889. }
  890. });
  891. thread.Name = "GUI.FirmwareInstallerThread";
  892. thread.Start();
  893. }
  894. }
  895. catch (LibHac.MissingKeyException ex)
  896. {
  897. Logger.Error?.Print(LogClass.Application, ex.ToString());
  898. UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys);
  899. }
  900. catch (Exception ex)
  901. {
  902. GtkDialog.CreateErrorDialog(ex.Message);
  903. }
  904. }
  905. else
  906. {
  907. fileChooser.Dispose();
  908. }
  909. }
  910. private void RefreshFirmwareLabel()
  911. {
  912. SystemVersion currentFirmware = _contentManager.GetCurrentFirmwareVersion();
  913. Application.Invoke(delegate
  914. {
  915. _firmwareVersionLabel.Text = currentFirmware != null ? currentFirmware.VersionString : "0.0.0";
  916. });
  917. }
  918. private void HandleRelaunch()
  919. {
  920. if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart)
  921. {
  922. _userChannelPersistence.ShouldRestart = false;
  923. LoadApplication(_currentEmulatedGamePath);
  924. }
  925. else
  926. {
  927. // otherwise, clear state.
  928. _userChannelPersistence = new UserChannelPersistence();
  929. _currentEmulatedGamePath = null;
  930. }
  931. }
  932. private void FullScreen_Toggled(object sender, EventArgs args)
  933. {
  934. if (!Window.State.HasFlag(Gdk.WindowState.Fullscreen))
  935. {
  936. Fullscreen();
  937. ToggleExtraWidgets(false);
  938. }
  939. else
  940. {
  941. Unfullscreen();
  942. ToggleExtraWidgets(true);
  943. }
  944. }
  945. private void StartFullScreen_Toggled(object sender, EventArgs args)
  946. {
  947. ConfigurationState.Instance.Ui.StartFullscreen.Value = _startFullScreen.Active;
  948. SaveConfig();
  949. }
  950. private void Settings_Pressed(object sender, EventArgs args)
  951. {
  952. SettingsWindow settingsWindow = new SettingsWindow(this, _virtualFileSystem, _contentManager);
  953. settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor));
  954. settingsWindow.Show();
  955. }
  956. private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args)
  957. {
  958. if (_emulationContext != null)
  959. {
  960. _emulationContext.System.SimulateWakeUpMessage();
  961. }
  962. }
  963. private void Update_Pressed(object sender, EventArgs args)
  964. {
  965. if (Updater.CanUpdate(true))
  966. {
  967. Updater.BeginParse(this, true).ContinueWith(task =>
  968. {
  969. Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}");
  970. }, TaskContinuationOptions.OnlyOnFaulted);
  971. }
  972. }
  973. private void About_Pressed(object sender, EventArgs args)
  974. {
  975. AboutWindow aboutWindow = new AboutWindow();
  976. aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor));
  977. aboutWindow.Show();
  978. }
  979. private void Fav_Toggled(object sender, EventArgs args)
  980. {
  981. ConfigurationState.Instance.Ui.GuiColumns.FavColumn.Value = _favToggle.Active;
  982. SaveConfig();
  983. UpdateColumns();
  984. }
  985. private void Icon_Toggled(object sender, EventArgs args)
  986. {
  987. ConfigurationState.Instance.Ui.GuiColumns.IconColumn.Value = _iconToggle.Active;
  988. SaveConfig();
  989. UpdateColumns();
  990. }
  991. private void App_Toggled(object sender, EventArgs args)
  992. {
  993. ConfigurationState.Instance.Ui.GuiColumns.AppColumn.Value = _appToggle.Active;
  994. SaveConfig();
  995. UpdateColumns();
  996. }
  997. private void Developer_Toggled(object sender, EventArgs args)
  998. {
  999. ConfigurationState.Instance.Ui.GuiColumns.DevColumn.Value = _developerToggle.Active;
  1000. SaveConfig();
  1001. UpdateColumns();
  1002. }
  1003. private void Version_Toggled(object sender, EventArgs args)
  1004. {
  1005. ConfigurationState.Instance.Ui.GuiColumns.VersionColumn.Value = _versionToggle.Active;
  1006. SaveConfig();
  1007. UpdateColumns();
  1008. }
  1009. private void TimePlayed_Toggled(object sender, EventArgs args)
  1010. {
  1011. ConfigurationState.Instance.Ui.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active;
  1012. SaveConfig();
  1013. UpdateColumns();
  1014. }
  1015. private void LastPlayed_Toggled(object sender, EventArgs args)
  1016. {
  1017. ConfigurationState.Instance.Ui.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active;
  1018. SaveConfig();
  1019. UpdateColumns();
  1020. }
  1021. private void FileExt_Toggled(object sender, EventArgs args)
  1022. {
  1023. ConfigurationState.Instance.Ui.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active;
  1024. SaveConfig();
  1025. UpdateColumns();
  1026. }
  1027. private void FileSize_Toggled(object sender, EventArgs args)
  1028. {
  1029. ConfigurationState.Instance.Ui.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active;
  1030. SaveConfig();
  1031. UpdateColumns();
  1032. }
  1033. private void Path_Toggled(object sender, EventArgs args)
  1034. {
  1035. ConfigurationState.Instance.Ui.GuiColumns.PathColumn.Value = _pathToggle.Active;
  1036. SaveConfig();
  1037. UpdateColumns();
  1038. }
  1039. private void RefreshList_Pressed(object sender, ButtonReleaseEventArgs args)
  1040. {
  1041. UpdateGameTable();
  1042. }
  1043. }
  1044. }