2022-03-22 20:46:16 +01:00
using ARMeilleure.Translation ;
2020-06-16 20:28:02 +02:00
using ARMeilleure.Translation.PTC ;
2019-09-02 18:03:57 +02:00
using Gtk ;
2020-03-25 18:09:38 +01:00
using LibHac.Common ;
2022-01-12 12:22:19 +01:00
using LibHac.Common.Keys ;
2021-05-16 17:12:14 +02:00
using LibHac.FsSystem ;
2022-03-22 20:46:16 +01:00
using LibHac.Ncm ;
2020-03-25 18:09:38 +01:00
using LibHac.Ns ;
2022-01-12 12:22:19 +01:00
using LibHac.Tools.FsSystem ;
2021-02-26 01:11:56 +01:00
using Ryujinx.Audio.Backends.Dummy ;
using Ryujinx.Audio.Backends.OpenAL ;
2021-05-05 23:37:09 +02:00
using Ryujinx.Audio.Backends.SDL2 ;
2021-02-26 01:11:56 +01:00
using Ryujinx.Audio.Backends.SoundIo ;
using Ryujinx.Audio.Integration ;
2021-05-16 17:12:14 +02:00
using Ryujinx.Common ;
2020-08-30 18:51:53 +02:00
using Ryujinx.Common.Configuration ;
2019-09-02 18:03:57 +02:00
using Ryujinx.Common.Logging ;
2020-08-18 03:49:37 +02:00
using Ryujinx.Common.System ;
2020-01-21 23:23:11 +01:00
using Ryujinx.Graphics.GAL ;
Add a Multithreading layer for the GAL, multi-thread shader compilation at runtime (#2501)
* Initial Implementation
About as fast as nvidia GL multithreading, can be improved with faster command queuing.
* Struct based command list
Speeds up a bit. Still a lot of time lost to resource copy.
* Do shader init while the render thread is active.
* Introduce circular span pool V1
Ideally should be able to use structs instead of references for storing these spans on commands. Will try that next.
* Refactor SpanRef some more
Use a struct to represent SpanRef, rather than a reference.
* Flush buffers on background thread
* Use a span for UpdateRenderScale.
Much faster than copying the array.
* Calculate command size using reflection
* WIP parallel shaders
* Some minor optimisation
* Only 2 max refs per command now.
The command with 3 refs is gone. :relieved:
* Don't cast on the GPU side
* Remove redundant casts, force sync on window present
* Fix Shader Cache
* Fix host shader save.
* Fixup to work with new renderer stuff
* Make command Run static, use array of delegates as lookup
Profile says this takes less time than the previous way.
* Bring up to date
* Add settings toggle. Fix Muiltithreading Off mode.
* Fix warning.
* Release tracking lock for flushes
* Fix Conditional Render fast path with threaded gal
* Make handle iteration safe when releasing the lock
This is mostly temporary.
* Attempt to set backend threading on driver
Only really works on nvidia before launching a game.
* Fix race condition with BufferModifiedRangeList, exceptions in tracking actions
* Update buffer set commands
* Some cleanup
* Only use stutter workaround when using opengl renderer non-threaded
* Add host-conditional reservation of counter events
There has always been the possibility that conditional rendering could use a query object just as it is disposed by the counter queue. This change makes it so that when the host decides to use host conditional rendering, the query object is reserved so that it cannot be deleted. Counter events can optionally start reserved, as the threaded implementation can reserve them before the backend creates them, and there would otherwise be a short amount of time where the counter queue could dispose the event before a call to reserve it could be made.
* Address Feedback
* Make counter flush tracked again.
Hopefully does not cause any issues this time.
* Wait for FlushTo on the main queue thread.
Currently assumes only one thread will want to FlushTo (in this case, the GPU thread)
* Add SDL2 headless integration
* Add HLE macro commands.
Co-authored-by: Mary <mary@mary.zone>
2021-08-27 00:31:29 +02:00
using Ryujinx.Graphics.GAL.Multithreading ;
2019-10-13 08:02:07 +02:00
using Ryujinx.Graphics.OpenGL ;
2020-01-05 12:49:44 +01:00
using Ryujinx.HLE.FileSystem ;
2020-09-21 05:45:30 +02:00
using Ryujinx.HLE.HOS ;
2021-04-13 03:16:43 +02:00
using Ryujinx.HLE.HOS.Services.Account.Acc ;
2021-05-16 17:12:14 +02:00
using Ryujinx.HLE.HOS.SystemState ;
2021-04-14 12:28:43 +02:00
using Ryujinx.Input.GTK3 ;
using Ryujinx.Input.HLE ;
using Ryujinx.Input.SDL2 ;
2021-01-08 09:14:13 +01:00
using Ryujinx.Modules ;
using Ryujinx.Ui.App ;
2022-05-15 13:30:15 +02:00
using Ryujinx.Ui.App.Common ;
2021-01-08 09:14:13 +01:00
using Ryujinx.Ui.Applet ;
2022-05-15 13:30:15 +02:00
using Ryujinx.Ui.Common ;
using Ryujinx.Ui.Common.Configuration ;
using Ryujinx.Ui.Common.Helper ;
2021-01-08 09:14:13 +01:00
using Ryujinx.Ui.Helper ;
using Ryujinx.Ui.Widgets ;
using Ryujinx.Ui.Windows ;
2022-03-22 20:46:16 +01:00
using System ;
using System.Diagnostics ;
using System.IO ;
using System.Reflection ;
using System.Threading ;
using System.Threading.Tasks ;
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
using GUI = Gtk . Builder . ObjectAttribute ;
2021-03-22 19:40:07 +01:00
using PtcLoadingState = ARMeilleure . Translation . PTC . PtcLoadingState ;
using ShaderCacheLoadingState = Ryujinx . Graphics . Gpu . Shader . ShaderCacheState ;
2019-11-29 05:32:51 +01:00
namespace Ryujinx.Ui
2019-09-02 18:03:57 +02:00
{
public class MainWindow : Window
{
2021-08-12 23:56:24 +02:00
private readonly VirtualFileSystem _virtualFileSystem ;
private readonly ContentManager _contentManager ;
private readonly AccountManager _accountManager ;
private readonly LibHacHorizonManager _libHacHorizonManager ;
2019-09-02 18:03:57 +02:00
2021-01-08 09:14:13 +01:00
private UserChannelPersistence _userChannelPersistence ;
2019-09-02 18:03:57 +02:00
2021-01-08 09:14:13 +01:00
private HLE . Switch _emulationContext ;
2019-09-02 18:03:57 +02:00
2021-01-08 09:14:13 +01:00
private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution ;
2020-09-21 05:45:30 +02:00
2021-01-08 09:14:13 +01:00
private readonly ApplicationLibrary _applicationLibrary ;
private readonly GtkHostUiHandler _uiHandler ;
private readonly AutoResetEvent _deviceExitStatus ;
private readonly ListStore _tableStore ;
2020-02-06 12:38:24 +01:00
2021-01-08 09:14:13 +01:00
private bool _updatingGameTable ;
private bool _gameLoaded ;
private bool _ending ;
2019-09-02 18:03:57 +02:00
2021-01-08 09:14:13 +01:00
private string _currentEmulatedGamePath = null ;
2021-03-18 21:40:20 +01:00
private string _lastScannedAmiiboId = "" ;
private bool _lastScannedAmiiboShowAll = false ;
2021-05-04 18:19:04 +02:00
public RendererWidgetBase RendererWidget ;
2021-04-14 12:28:43 +02:00
public InputManager InputManager ;
2020-05-03 04:00:53 +02:00
2021-06-23 23:44:09 +02:00
public bool IsFocused ;
2021-05-04 18:19:04 +02:00
private static bool UseVulkan = false ;
2020-04-20 23:59:59 +02:00
#pragma warning disable CS0169 , CS0649 , IDE0044
2020-02-12 01:56:19 +01:00
2020-09-29 22:05:25 +02:00
[GUI] public MenuItem ExitMenuItem ;
[GUI] public MenuItem UpdateMenuItem ;
[GUI] MenuBar _menuBar ;
[GUI] Box _footerBox ;
[GUI] Box _statusBar ;
2021-03-18 21:40:20 +01:00
[GUI] MenuItem _optionMenu ;
2021-04-23 22:26:31 +02:00
[GUI] MenuItem _manageUserProfiles ;
2021-06-28 20:54:45 +02:00
[GUI] MenuItem _fileMenu ;
[GUI] MenuItem _loadApplicationFile ;
[GUI] MenuItem _loadApplicationFolder ;
[GUI] MenuItem _appletMenu ;
2021-03-18 21:40:20 +01:00
[GUI] MenuItem _actionMenu ;
2021-09-11 22:08:25 +02:00
[GUI] MenuItem _pauseEmulation ;
[GUI] MenuItem _resumeEmulation ;
2020-09-29 22:05:25 +02:00
[GUI] MenuItem _stopEmulation ;
2021-01-11 16:03:37 +01:00
[GUI] MenuItem _simulateWakeUpMessage ;
2021-03-18 21:40:20 +01:00
[GUI] MenuItem _scanAmiibo ;
2021-06-28 22:09:43 +02:00
[GUI] MenuItem _takeScreenshot ;
2021-08-04 23:28:19 +02:00
[GUI] MenuItem _hideUi ;
2020-09-29 22:05:25 +02:00
[GUI] MenuItem _fullScreen ;
2020-12-01 23:02:27 +01:00
[GUI] CheckMenuItem _startFullScreen ;
2022-03-15 02:35:41 +01:00
[GUI] CheckMenuItem _showConsole ;
2020-09-29 22:05:25 +02:00
[GUI] CheckMenuItem _favToggle ;
[GUI] MenuItem _firmwareInstallDirectory ;
[GUI] MenuItem _firmwareInstallFile ;
2020-10-13 22:54:42 +02:00
[GUI] Label _fifoStatus ;
2020-09-29 22:05:25 +02:00
[GUI] CheckMenuItem _iconToggle ;
[GUI] CheckMenuItem _developerToggle ;
[GUI] CheckMenuItem _appToggle ;
[GUI] CheckMenuItem _timePlayedToggle ;
[GUI] CheckMenuItem _versionToggle ;
[GUI] CheckMenuItem _lastPlayedToggle ;
[GUI] CheckMenuItem _fileExtToggle ;
[GUI] CheckMenuItem _pathToggle ;
[GUI] CheckMenuItem _fileSizeToggle ;
[GUI] Label _dockedMode ;
2020-12-16 03:19:07 +01:00
[GUI] Label _aspectRatio ;
2020-09-29 22:05:25 +02:00
[GUI] Label _gameStatus ;
[GUI] TreeView _gameTable ;
[GUI] TreeSelection _gameTableSelection ;
[GUI] ScrolledWindow _gameTableWindow ;
[GUI] Label _gpuName ;
[GUI] Label _progressLabel ;
[GUI] Label _firmwareVersionLabel ;
2022-01-12 12:22:19 +01:00
[GUI] Gtk . ProgressBar _progressBar ;
2020-09-29 22:05:25 +02:00
[GUI] Box _viewBox ;
[GUI] Label _vSyncStatus ;
2021-12-23 17:33:56 +01:00
[GUI] Label _volumeStatus ;
2020-09-29 22:05:25 +02:00
[GUI] Box _listStatusBox ;
2021-03-03 01:39:36 +01:00
[GUI] Label _loadingStatusLabel ;
2022-01-12 12:22:19 +01:00
[GUI] Gtk . ProgressBar _loadingStatusBar ;
2021-03-03 01:39:36 +01:00
2020-04-20 23:59:59 +02:00
#pragma warning restore CS0649 , IDE0044 , CS0169
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
public MainWindow ( ) : this ( new Builder ( "Ryujinx.Ui.MainWindow.glade" ) ) { }
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
private MainWindow ( Builder builder ) : base ( builder . GetObject ( "_mainWin" ) . Handle )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
builder . Autoconnect ( this ) ;
2021-01-08 09:14:13 +01:00
// Apply custom theme if needed.
ThemeHelper . ApplyTheme ( ) ;
// Sets overridden fields.
2020-03-31 00:10:13 +02:00
int monitorWidth = Display . PrimaryMonitor . Geometry . Width * Display . PrimaryMonitor . ScaleFactor ;
int monitorHeight = Display . PrimaryMonitor . Geometry . Height * Display . PrimaryMonitor . ScaleFactor ;
2021-01-08 09:14:13 +01:00
DefaultWidth = monitorWidth < 1280 ? monitorWidth : 1280 ;
DefaultHeight = monitorHeight < 760 ? monitorHeight : 760 ;
2019-11-29 05:32:51 +01:00
2022-05-15 13:30:15 +02:00
Icon = new Gdk . Pixbuf ( Assembly . GetAssembly ( typeof ( ConfigurationState ) ) , "Ryujinx.Ui.Common.Resources.Logo_Ryujinx.png" ) ;
2021-01-08 09:14:13 +01:00
Title = $"Ryujinx {Program.Version}" ;
2019-12-22 03:49:51 +01:00
2021-01-08 09:14:13 +01:00
// Hide emulation context status bar.
_statusBar . Hide ( ) ;
2020-01-05 12:49:44 +01:00
2021-08-12 23:56:24 +02:00
// Instantiate HLE objects.
_virtualFileSystem = VirtualFileSystem . CreateInstance ( ) ;
_libHacHorizonManager = new LibHacHorizonManager ( ) ;
_libHacHorizonManager . InitializeFsServer ( _virtualFileSystem ) ;
_libHacHorizonManager . InitializeArpServer ( ) ;
_libHacHorizonManager . InitializeBcatServer ( ) ;
_libHacHorizonManager . InitializeSystemClients ( ) ;
// Save data created before we supported extra data in directory save data will not work properly if
// given empty extra data. Luckily some of that extra data can be created using the data from the
// save data indexer, which should be enough to check access permissions for user saves.
// Every single save data's extra data will be checked and fixed if needed each time the emulator is opened.
// Consider removing this at some point in the future when we don't need to worry about old saves.
VirtualFileSystem . FixExtraData ( _libHacHorizonManager . RyujinxClient ) ;
2020-11-19 01:34:28 +01:00
_contentManager = new ContentManager ( _virtualFileSystem ) ;
2022-03-02 09:51:37 +01:00
_accountManager = new AccountManager ( _libHacHorizonManager . RyujinxClient , Program . CommandLineProfile ) ;
2021-01-08 09:14:13 +01:00
_userChannelPersistence = new UserChannelPersistence ( ) ;
2019-09-02 18:03:57 +02:00
2021-08-12 23:56:24 +02:00
// Instantiate GUI objects.
2021-01-08 09:14:13 +01:00
_applicationLibrary = new ApplicationLibrary ( _virtualFileSystem ) ;
_uiHandler = new GtkHostUiHandler ( this ) ;
_deviceExitStatus = new AutoResetEvent ( false ) ;
2020-01-05 12:49:44 +01:00
2021-01-08 09:14:13 +01:00
WindowStateEvent + = WindowStateEvent_Changed ;
DeleteEvent + = Window_Close ;
2021-06-23 23:44:09 +02:00
FocusInEvent + = MainWindow_FocusInEvent ;
FocusOutEvent + = MainWindow_FocusOutEvent ;
2020-01-05 12:49:44 +01:00
2021-01-08 09:14:13 +01:00
_applicationLibrary . ApplicationAdded + = Application_Added ;
_applicationLibrary . ApplicationCountUpdated + = ApplicationCount_Updated ;
2021-06-28 20:54:45 +02:00
_fileMenu . StateChanged + = FileMenu_StateChanged ;
2021-03-18 21:40:20 +01:00
_actionMenu . StateChanged + = ActionMenu_StateChanged ;
2021-04-23 22:26:31 +02:00
_optionMenu . StateChanged + = OptionMenu_StateChanged ;
2021-03-18 21:40:20 +01:00
2021-01-08 09:14:13 +01:00
_gameTable . ButtonReleaseEvent + = Row_Clicked ;
_fullScreen . Activated + = FullScreen_Toggled ;
2020-01-21 23:23:11 +01:00
2021-05-04 18:19:04 +02:00
RendererWidgetBase . StatusUpdatedEvent + = Update_StatusBar ;
2019-09-02 18:03:57 +02:00
2021-05-16 17:12:14 +02:00
ConfigurationState . Instance . System . IgnoreMissingServices . Event + = UpdateIgnoreMissingServicesState ;
ConfigurationState . Instance . Graphics . AspectRatio . Event + = UpdateAspectRatioState ;
ConfigurationState . Instance . System . EnableDockedMode . Event + = UpdateDockedModeState ;
2021-12-26 15:17:13 +01:00
ConfigurationState . Instance . System . AudioVolume . Event + = UpdateAudioVolumeState ;
2021-05-16 17:12:14 +02:00
2020-12-01 23:02:27 +01:00
if ( ConfigurationState . Instance . Ui . StartFullscreen )
{
_startFullScreen . Active = true ;
}
2022-03-15 02:35:41 +01:00
_showConsole . Active = ConfigurationState . Instance . Ui . ShowConsole . Value ;
_showConsole . Visible = ConsoleHelper . SetConsoleWindowStateSupported ;
2021-03-18 21:40:20 +01:00
_actionMenu . Sensitive = false ;
2021-09-11 22:08:25 +02:00
_pauseEmulation . Sensitive = false ;
2021-09-18 14:31:44 +02:00
_resumeEmulation . Sensitive = false ;
2019-09-02 18:03:57 +02:00
2019-12-21 20:52:31 +01:00
if ( ConfigurationState . Instance . Ui . GuiColumns . FavColumn ) _favToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . IconColumn ) _iconToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . AppColumn ) _appToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . DevColumn ) _developerToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . VersionColumn ) _versionToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . TimePlayedColumn ) _timePlayedToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . LastPlayedColumn ) _lastPlayedToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileExtColumn ) _fileExtToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileSizeColumn ) _fileSizeToggle . Active = true ;
if ( ConfigurationState . Instance . Ui . GuiColumns . PathColumn ) _pathToggle . Active = true ;
2019-11-29 05:32:51 +01:00
2021-02-09 10:24:37 +01:00
_favToggle . Toggled + = Fav_Toggled ;
_iconToggle . Toggled + = Icon_Toggled ;
_appToggle . Toggled + = App_Toggled ;
_developerToggle . Toggled + = Developer_Toggled ;
_versionToggle . Toggled + = Version_Toggled ;
_timePlayedToggle . Toggled + = TimePlayed_Toggled ;
_lastPlayedToggle . Toggled + = LastPlayed_Toggled ;
_fileExtToggle . Toggled + = FileExt_Toggled ;
_fileSizeToggle . Toggled + = FileSize_Toggled ;
_pathToggle . Toggled + = Path_Toggled ;
2019-11-29 05:32:51 +01:00
_gameTable . Model = _tableStore = new ListStore (
2019-12-21 20:52:31 +01:00
typeof ( bool ) ,
typeof ( Gdk . Pixbuf ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
typeof ( string ) ,
2020-03-25 18:09:38 +01:00
typeof ( string ) ,
typeof ( BlitStruct < ApplicationControlProperty > ) ) ;
2019-12-21 20:52:31 +01:00
2021-01-08 09:14:13 +01:00
_tableStore . SetSortFunc ( 5 , SortHelper . TimePlayedSort ) ;
_tableStore . SetSortFunc ( 6 , SortHelper . LastPlayedSort ) ;
_tableStore . SetSortFunc ( 8 , SortHelper . FileSizeSort ) ;
2020-06-26 12:30:16 +02:00
int columnId = ConfigurationState . Instance . Ui . ColumnSort . SortColumnId ;
bool ascending = ConfigurationState . Instance . Ui . ColumnSort . SortAscending ;
_tableStore . SetSortColumnId ( columnId , ascending ? SortType . Ascending : SortType . Descending ) ;
2019-11-29 05:32:51 +01:00
2020-04-25 15:02:44 +02:00
_gameTable . EnableSearch = true ;
_gameTable . SearchColumn = 2 ;
2021-06-24 02:21:52 +02:00
_gameTable . SearchEqualFunc = ( model , col , key , iter ) = > ! ( ( string ) model . GetValue ( iter , col ) ) . Contains ( key , StringComparison . InvariantCultureIgnoreCase ) ;
2020-04-25 15:02:44 +02:00
2021-08-04 23:28:19 +02:00
_hideUi . Label = _hideUi . Label . Replace ( "SHOWUIKEY" , ConfigurationState . Instance . Hid . Hotkeys . Value . ShowUi . ToString ( ) ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
UpdateGameTable ( ) ;
2020-01-12 03:10:55 +01:00
2020-07-04 01:29:36 +02:00
ConfigurationState . Instance . Ui . GameDirs . Event + = ( sender , args ) = >
{
if ( args . OldValue ! = args . NewValue )
{
UpdateGameTable ( ) ;
}
} ;
2020-01-12 03:10:55 +01:00
Task . Run ( RefreshFirmwareLabel ) ;
2021-04-14 12:28:43 +02:00
InputManager = new InputManager ( new GTK3KeyboardDriver ( this ) , new SDL2GamepadDriver ( ) ) ;
2019-11-29 05:32:51 +01:00
}
2021-05-16 17:12:14 +02:00
private void UpdateIgnoreMissingServicesState ( object sender , ReactiveEventArgs < bool > args )
{
if ( _emulationContext ! = null )
{
_emulationContext . Configuration . IgnoreMissingServices = args . NewValue ;
}
}
private void UpdateAspectRatioState ( object sender , ReactiveEventArgs < AspectRatio > args )
{
if ( _emulationContext ! = null )
{
_emulationContext . Configuration . AspectRatio = args . NewValue ;
}
}
private void UpdateDockedModeState ( object sender , ReactiveEventArgs < bool > e )
{
if ( _emulationContext ! = null )
{
_emulationContext . System . ChangeDockedModeState ( e . NewValue ) ;
}
}
2021-12-23 17:33:56 +01:00
private void UpdateAudioVolumeState ( object sender , ReactiveEventArgs < float > e )
{
_emulationContext ? . SetVolume ( e . NewValue ) ;
}
2021-01-08 09:14:13 +01:00
private void WindowStateEvent_Changed ( object o , WindowStateEventArgs args )
2020-07-23 15:12:19 +02:00
{
_fullScreen . Label = args . Event . NewWindowState . HasFlag ( Gdk . WindowState . Fullscreen ) ? "Exit Fullscreen" : "Enter Fullscreen" ;
}
2021-06-23 23:44:09 +02:00
private void MainWindow_FocusOutEvent ( object o , FocusOutEventArgs args )
{
IsFocused = false ;
}
private void MainWindow_FocusInEvent ( object o , FocusInEventArgs args )
{
IsFocused = true ;
}
2019-11-29 05:32:51 +01:00
private void UpdateColumns ( )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
foreach ( TreeViewColumn column in _gameTable . Columns )
{
_gameTable . RemoveColumn ( column ) ;
}
2019-09-02 18:03:57 +02:00
2019-11-29 05:32:51 +01:00
CellRendererToggle favToggle = new CellRendererToggle ( ) ;
favToggle . Toggled + = FavToggle_Toggled ;
2019-12-21 20:52:31 +01:00
if ( ConfigurationState . Instance . Ui . GuiColumns . FavColumn ) _gameTable . AppendColumn ( "Fav" , favToggle , "active" , 0 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . IconColumn ) _gameTable . AppendColumn ( "Icon" , new CellRendererPixbuf ( ) , "pixbuf" , 1 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . AppColumn ) _gameTable . AppendColumn ( "Application" , new CellRendererText ( ) , "text" , 2 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . DevColumn ) _gameTable . AppendColumn ( "Developer" , new CellRendererText ( ) , "text" , 3 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . VersionColumn ) _gameTable . AppendColumn ( "Version" , new CellRendererText ( ) , "text" , 4 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . TimePlayedColumn ) _gameTable . AppendColumn ( "Time Played" , new CellRendererText ( ) , "text" , 5 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . LastPlayedColumn ) _gameTable . AppendColumn ( "Last Played" , new CellRendererText ( ) , "text" , 6 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileExtColumn ) _gameTable . AppendColumn ( "File Ext" , new CellRendererText ( ) , "text" , 7 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . FileSizeColumn ) _gameTable . AppendColumn ( "File Size" , new CellRendererText ( ) , "text" , 8 ) ;
if ( ConfigurationState . Instance . Ui . GuiColumns . PathColumn ) _gameTable . AppendColumn ( "Path" , new CellRendererText ( ) , "text" , 9 ) ;
2019-11-29 05:32:51 +01:00
foreach ( TreeViewColumn column in _gameTable . Columns )
2019-09-02 18:03:57 +02:00
{
2020-06-26 12:30:16 +02:00
switch ( column . Title )
{
case "Fav" :
column . SortColumnId = 0 ;
column . Clicked + = Column_Clicked ;
break ;
case "Application" :
column . SortColumnId = 2 ;
column . Clicked + = Column_Clicked ;
break ;
case "Developer" :
column . SortColumnId = 3 ;
column . Clicked + = Column_Clicked ;
break ;
case "Version" :
column . SortColumnId = 4 ;
column . Clicked + = Column_Clicked ;
break ;
case "Time Played" :
column . SortColumnId = 5 ;
column . Clicked + = Column_Clicked ;
break ;
case "Last Played" :
column . SortColumnId = 6 ;
column . Clicked + = Column_Clicked ;
break ;
case "File Ext" :
column . SortColumnId = 7 ;
column . Clicked + = Column_Clicked ;
break ;
case "File Size" :
column . SortColumnId = 8 ;
column . Clicked + = Column_Clicked ;
break ;
case "Path" :
column . SortColumnId = 9 ;
column . Clicked + = Column_Clicked ;
break ;
}
2019-09-02 18:03:57 +02:00
}
2019-12-21 20:52:31 +01:00
}
2021-04-14 12:28:43 +02:00
protected override void OnDestroyed ( )
{
InputManager . Dispose ( ) ;
}
2021-01-08 09:14:13 +01:00
private void InitializeSwitchInstance ( )
2019-12-21 20:52:31 +01:00
{
2021-08-12 23:56:24 +02:00
_virtualFileSystem . ReloadKeySet ( ) ;
2020-01-21 23:23:11 +01:00
2021-05-04 18:19:04 +02:00
IRenderer renderer ;
if ( UseVulkan )
{
throw new NotImplementedException ( ) ;
}
else
{
renderer = new Renderer ( ) ;
}
Add a Multithreading layer for the GAL, multi-thread shader compilation at runtime (#2501)
* Initial Implementation
About as fast as nvidia GL multithreading, can be improved with faster command queuing.
* Struct based command list
Speeds up a bit. Still a lot of time lost to resource copy.
* Do shader init while the render thread is active.
* Introduce circular span pool V1
Ideally should be able to use structs instead of references for storing these spans on commands. Will try that next.
* Refactor SpanRef some more
Use a struct to represent SpanRef, rather than a reference.
* Flush buffers on background thread
* Use a span for UpdateRenderScale.
Much faster than copying the array.
* Calculate command size using reflection
* WIP parallel shaders
* Some minor optimisation
* Only 2 max refs per command now.
The command with 3 refs is gone. :relieved:
* Don't cast on the GPU side
* Remove redundant casts, force sync on window present
* Fix Shader Cache
* Fix host shader save.
* Fixup to work with new renderer stuff
* Make command Run static, use array of delegates as lookup
Profile says this takes less time than the previous way.
* Bring up to date
* Add settings toggle. Fix Muiltithreading Off mode.
* Fix warning.
* Release tracking lock for flushes
* Fix Conditional Render fast path with threaded gal
* Make handle iteration safe when releasing the lock
This is mostly temporary.
* Attempt to set backend threading on driver
Only really works on nvidia before launching a game.
* Fix race condition with BufferModifiedRangeList, exceptions in tracking actions
* Update buffer set commands
* Some cleanup
* Only use stutter workaround when using opengl renderer non-threaded
* Add host-conditional reservation of counter events
There has always been the possibility that conditional rendering could use a query object just as it is disposed by the counter queue. This change makes it so that when the host decides to use host conditional rendering, the query object is reserved so that it cannot be deleted. Counter events can optionally start reserved, as the threaded implementation can reserve them before the backend creates them, and there would otherwise be a short amount of time where the counter queue could dispose the event before a call to reserve it could be made.
* Address Feedback
* Make counter flush tracked again.
Hopefully does not cause any issues this time.
* Wait for FlushTo on the main queue thread.
Currently assumes only one thread will want to FlushTo (in this case, the GPU thread)
* Add SDL2 headless integration
* Add HLE macro commands.
Co-authored-by: Mary <mary@mary.zone>
2021-08-27 00:31:29 +02:00
BackendThreading threadingMode = ConfigurationState . Instance . Graphics . BackendThreading ;
bool threadedGAL = threadingMode = = BackendThreading . On | | ( threadingMode = = BackendThreading . Auto & & renderer . PreferThreading ) ;
if ( threadedGAL )
{
renderer = new ThreadedRenderer ( renderer ) ;
}
Logger . Info ? . PrintMsg ( LogClass . Gpu , $"Backend Threading ({threadingMode}): {threadedGAL}" ) ;
2021-02-26 01:11:56 +01:00
IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver ( ) ;
2021-01-08 09:14:13 +01:00
2021-05-05 23:37:09 +02:00
if ( ConfigurationState . Instance . System . AudioBackend . Value = = AudioBackend . SDL2 )
{
if ( SDL2HardwareDeviceDriver . IsSupported )
{
deviceDriver = new SDL2HardwareDeviceDriver ( ) ;
}
else
{
2021-08-26 23:03:19 +02:00
Logger . Warning ? . Print ( LogClass . Audio , "SDL2 is not supported, trying to fall back to OpenAL." ) ;
2021-12-26 15:17:13 +01:00
2021-08-26 23:03:19 +02:00
if ( OpenALHardwareDeviceDriver . IsSupported )
{
Logger . Warning ? . Print ( LogClass . Audio , "Found OpenAL, changing configuration." ) ;
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . OpenAl ;
SaveConfig ( ) ;
deviceDriver = new OpenALHardwareDeviceDriver ( ) ;
}
else
{
Logger . Warning ? . Print ( LogClass . Audio , "OpenAL is not supported, trying to fall back to SoundIO." ) ;
2021-12-26 15:17:13 +01:00
2021-08-26 23:03:19 +02:00
if ( SoundIoHardwareDeviceDriver . IsSupported )
{
Logger . Warning ? . Print ( LogClass . Audio , "Found SoundIO, changing configuration." ) ;
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . SoundIo ;
SaveConfig ( ) ;
deviceDriver = new SoundIoHardwareDeviceDriver ( ) ;
}
else
{
Logger . Warning ? . Print ( LogClass . Audio , "SoundIO is not supported, falling back to dummy audio out." ) ;
2021-12-26 15:17:13 +01:00
}
}
2021-05-05 23:37:09 +02:00
}
}
else if ( ConfigurationState . Instance . System . AudioBackend . Value = = AudioBackend . SoundIo )
2021-01-08 09:14:13 +01:00
{
2021-02-26 01:11:56 +01:00
if ( SoundIoHardwareDeviceDriver . IsSupported )
2021-01-08 09:14:13 +01:00
{
2021-02-26 01:11:56 +01:00
deviceDriver = new SoundIoHardwareDeviceDriver ( ) ;
2021-01-08 09:14:13 +01:00
}
else
{
2021-08-26 23:03:19 +02:00
Logger . Warning ? . Print ( LogClass . Audio , "SoundIO is not supported, trying to fall back to SDL2." ) ;
if ( SDL2HardwareDeviceDriver . IsSupported )
{
Logger . Warning ? . Print ( LogClass . Audio , "Found SDL2, changing configuration." ) ;
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . SDL2 ;
SaveConfig ( ) ;
deviceDriver = new SDL2HardwareDeviceDriver ( ) ;
}
else
{
Logger . Warning ? . Print ( LogClass . Audio , "SDL2 is not supported, trying to fall back to OpenAL." ) ;
if ( OpenALHardwareDeviceDriver . IsSupported )
{
Logger . Warning ? . Print ( LogClass . Audio , "Found OpenAL, changing configuration." ) ;
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . OpenAl ;
SaveConfig ( ) ;
deviceDriver = new OpenALHardwareDeviceDriver ( ) ;
}
else
{
Logger . Warning ? . Print ( LogClass . Audio , "OpenAL is not supported, falling back to dummy audio out." ) ;
2021-12-26 15:17:13 +01:00
}
2021-08-26 23:03:19 +02:00
}
2021-01-08 09:14:13 +01:00
}
}
else if ( ConfigurationState . Instance . System . AudioBackend . Value = = AudioBackend . OpenAl )
{
2021-02-26 01:11:56 +01:00
if ( OpenALHardwareDeviceDriver . IsSupported )
2021-01-08 09:14:13 +01:00
{
2021-02-26 01:11:56 +01:00
deviceDriver = new OpenALHardwareDeviceDriver ( ) ;
2021-01-08 09:14:13 +01:00
}
else
{
2021-08-26 23:03:19 +02:00
Logger . Warning ? . Print ( LogClass . Audio , "OpenAL is not supported, trying to fall back to SDL2." ) ;
2021-01-08 09:14:13 +01:00
2021-08-26 23:03:19 +02:00
if ( SDL2HardwareDeviceDriver . IsSupported )
2021-01-08 09:14:13 +01:00
{
2021-08-26 23:03:19 +02:00
Logger . Warning ? . Print ( LogClass . Audio , "Found SDL2, changing configuration." ) ;
2021-01-08 09:14:13 +01:00
2021-08-26 23:03:19 +02:00
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . SDL2 ;
2021-01-08 09:14:13 +01:00
SaveConfig ( ) ;
2021-08-26 23:03:19 +02:00
deviceDriver = new SDL2HardwareDeviceDriver ( ) ;
2021-01-08 09:14:13 +01:00
}
else
{
2021-08-26 23:03:19 +02:00
Logger . Warning ? . Print ( LogClass . Audio , "SDL2 is not supported, trying to fall back to SoundIO." ) ;
2021-12-26 15:17:13 +01:00
2021-08-26 23:03:19 +02:00
if ( SoundIoHardwareDeviceDriver . IsSupported )
{
Logger . Warning ? . Print ( LogClass . Audio , "Found SoundIO, changing configuration." ) ;
ConfigurationState . Instance . System . AudioBackend . Value = AudioBackend . SoundIo ;
SaveConfig ( ) ;
deviceDriver = new SoundIoHardwareDeviceDriver ( ) ;
}
else
{
Logger . Warning ? . Print ( LogClass . Audio , "SoundIO is not supported, falling back to dummy audio out." ) ;
}
2021-01-08 09:14:13 +01:00
}
}
}
2021-04-04 14:06:59 +02:00
var memoryConfiguration = ConfigurationState . Instance . System . ExpandRam . Value
? HLE . MemoryConfiguration . MemoryConfiguration6GB
: HLE . MemoryConfiguration . MemoryConfiguration4GB ;
2021-05-16 17:12:14 +02:00
IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState . Instance . System . EnableFsIntegrityChecks ? IntegrityCheckLevel . ErrorOnInvalid : IntegrityCheckLevel . None ;
HLE . HLEConfiguration configuration = new HLE . HLEConfiguration ( _virtualFileSystem ,
2021-08-12 23:56:24 +02:00
_libHacHorizonManager ,
2021-05-16 17:12:14 +02:00
_contentManager ,
_accountManager ,
_userChannelPersistence ,
renderer ,
deviceDriver ,
memoryConfiguration ,
_uiHandler ,
( SystemLanguage ) ConfigurationState . Instance . System . Language . Value ,
( RegionCode ) ConfigurationState . Instance . System . Region . Value ,
ConfigurationState . Instance . Graphics . EnableVsync ,
ConfigurationState . Instance . System . EnableDockedMode ,
ConfigurationState . Instance . System . EnablePtc ,
2021-12-26 15:17:13 +01:00
ConfigurationState . Instance . System . EnableInternetAccess ,
2021-05-16 17:12:14 +02:00
fsIntegrityCheckLevel ,
ConfigurationState . Instance . System . FsGlobalAccessLogMode ,
ConfigurationState . Instance . System . SystemTimeOffset ,
ConfigurationState . Instance . System . TimeZone ,
POWER - Performance Optimizations With Extensive Ramifications (#2286)
* Refactoring of KMemoryManager class
* Replace some trivial uses of DRAM address with VA
* Get rid of GetDramAddressFromVa
* Abstracting more operations on derived page table class
* Run auto-format on KPageTableBase
* Managed to make TryConvertVaToPa private, few uses remains now
* Implement guest physical pages ref counting, remove manual freeing
* Make DoMmuOperation private and call new abstract methods only from the base class
* Pass pages count rather than size on Map/UnmapMemory
* Change memory managers to take host pointers
* Fix a guest memory leak and simplify KPageTable
* Expose new methods for host range query and mapping
* Some refactoring of MapPagesFromClientProcess to allow proper page ref counting and mapping without KPageLists
* Remove more uses of AddVaRangeToPageList, now only one remains (shared memory page checking)
* Add a SharedMemoryStorage class, will be useful for host mapping
* Sayonara AddVaRangeToPageList, you served us well
* Start to implement host memory mapping (WIP)
* Support memory tracking through host exception handling
* Fix some access violations from HLE service guest memory access and CPU
* Fix memory tracking
* Fix mapping list bugs, including a race and a error adding mapping ranges
* Simple page table for memory tracking
* Simple "volatile" region handle mode
* Update UBOs directly (experimental, rough)
* Fix the overlap check
* Only set non-modified buffers as volatile
* Fix some memory tracking issues
* Fix possible race in MapBufferFromClientProcess (block list updates were not locked)
* Write uniform update to memory immediately, only defer the buffer set.
* Fix some memory tracking issues
* Pass correct pages count on shared memory unmap
* Armeilleure Signal Handler v1 + Unix changes
Unix currently behaves like windows, rather than remapping physical
* Actually check if the host platform is unix
* Fix decommit on linux.
* Implement windows 10 placeholder shared memory, fix a buffer issue.
* Make PTC version something that will never match with master
* Remove testing variable for block count
* Add reference count for memory manager, fix dispose
Can still deadlock with OpenAL
* Add address validation, use page table for mapped check, add docs
Might clean up the page table traversing routines.
* Implement batched mapping/tracking.
* Move documentation, fix tests.
* Cleanup uniform buffer update stuff.
* Remove unnecessary assignment.
* Add unsafe host mapped memory switch
On by default. Would be good to turn this off for untrusted code (homebrew, exefs mods) and give the user the option to turn it on manually, though that requires some UI work.
* Remove C# exception handlers
They have issues due to current .NET limitations, so the meilleure one fully replaces them for now.
* Fix MapPhysicalMemory on the software MemoryManager.
* Null check for GetHostAddress, docs
* Add configuration for setting memory manager mode (not in UI yet)
* Add config to UI
* Fix type mismatch on Unix signal handler code emit
* Fix 6GB DRAM mode.
The size can be greater than `uint.MaxValue` when the DRAM is >4GB.
* Address some feedback.
* More detailed error if backing memory cannot be mapped.
* SetLastError on all OS functions for consistency
* Force pages dirty with UBO update instead of setting them directly.
Seems to be much faster across a few games. Need retesting.
* Rebase, configuration rework, fix mem tracking regression
* Fix race in FreePages
* Set memory managers null after decrementing ref count
* Remove readonly keyword, as this is now modified.
* Use a local variable for the signal handler rather than a register.
* Fix bug with buffer resize, and index/uniform buffer binding.
Should fix flickering in games.
* Add InvalidAccessHandler to MemoryTracking
Doesn't do anything yet
* Call invalid access handler on unmapped read/write.
Same rules as the regular memory manager.
* Make unsafe mapped memory its own MemoryManagerType
* Move FlushUboDirty into UpdateState.
* Buffer dirty cache, rather than ubo cache
Much cleaner, may be reusable for Inline2Memory updates.
* This doesn't return anything anymore.
* Add sigaction remove methods, correct a few function signatures.
* Return empty list of physical regions for size 0.
* Also on AddressSpaceManager
Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2021-05-24 22:52:44 +02:00
ConfigurationState . Instance . System . MemoryManagerMode ,
2021-05-16 17:12:14 +02:00
ConfigurationState . Instance . System . IgnoreMissingServices ,
2021-12-23 17:33:56 +01:00
ConfigurationState . Instance . Graphics . AspectRatio ,
ConfigurationState . Instance . System . AudioVolume ) ;
2021-05-16 17:12:14 +02:00
_emulationContext = new HLE . Switch ( configuration ) ;
2019-09-02 18:03:57 +02:00
}
2021-03-03 01:39:36 +01:00
private void SetupProgressUiHandlers ( )
{
2021-03-22 19:40:07 +01:00
Ptc . PtcStateChanged - = ProgressHandler ;
Ptc . PtcStateChanged + = ProgressHandler ;
2021-03-03 01:39:36 +01:00
2021-03-22 19:40:07 +01:00
_emulationContext . Gpu . ShaderCacheStateChanged - = ProgressHandler ;
_emulationContext . Gpu . ShaderCacheStateChanged + = ProgressHandler ;
2021-03-03 01:39:36 +01:00
}
2021-03-22 19:40:07 +01:00
private void ProgressHandler < T > ( T state , int current , int total ) where T : Enum
2021-03-03 01:39:36 +01:00
{
2021-03-22 19:40:07 +01:00
bool visible ;
string label ;
2021-03-03 01:39:36 +01:00
2021-03-22 19:40:07 +01:00
switch ( state )
2021-03-03 01:39:36 +01:00
{
2021-03-22 19:40:07 +01:00
case PtcLoadingState ptcState :
visible = ptcState ! = PtcLoadingState . Loaded ;
label = $"PTC : {current}/{total}" ;
break ;
case ShaderCacheLoadingState shaderCacheState :
visible = shaderCacheState ! = ShaderCacheLoadingState . Loaded ;
label = $"Shaders : {current}/{total}" ;
break ;
default :
throw new ArgumentException ( $"Unknown Progress Handler type {typeof(T)}" ) ;
}
2021-03-03 01:39:36 +01:00
Application . Invoke ( delegate
{
2021-03-22 19:40:07 +01:00
_loadingStatusLabel . Text = label ;
_loadingStatusBar . Fraction = total > 0 ? ( double ) current / total : 0 ;
_loadingStatusBar . Visible = visible ;
_loadingStatusLabel . Visible = visible ;
2021-03-03 01:39:36 +01:00
} ) ;
}
2021-01-08 09:14:13 +01:00
public void UpdateGameTable ( )
2019-09-02 18:03:57 +02:00
{
2020-07-04 01:29:36 +02:00
if ( _updatingGameTable | | _gameLoaded )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
return ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
_updatingGameTable = true ;
_tableStore . Clear ( ) ;
2020-01-31 19:21:46 +01:00
Thread applicationLibraryThread = new Thread ( ( ) = >
{
2021-01-08 09:14:13 +01:00
_applicationLibrary . LoadApplications ( ConfigurationState . Instance . Ui . GameDirs , ConfigurationState . Instance . System . Language ) ;
2019-11-29 05:32:51 +01:00
2020-01-31 19:21:46 +01:00
_updatingGameTable = false ;
} ) ;
2021-01-08 09:14:13 +01:00
applicationLibraryThread . Name = "GUI.ApplicationLibraryThread" ;
2020-01-31 19:21:46 +01:00
applicationLibraryThread . IsBackground = true ;
applicationLibraryThread . Start ( ) ;
2019-09-02 18:03:57 +02:00
}
2021-01-08 09:14:13 +01:00
[Conditional("RELEASE")]
public void PerformanceCheck ( )
2019-09-02 18:03:57 +02:00
{
2022-02-18 01:08:07 +01:00
if ( ConfigurationState . Instance . Logger . EnableTrace . Value )
2019-09-02 18:03:57 +02:00
{
2021-01-08 09:14:13 +01:00
MessageDialog debugWarningDialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Warning , ButtonsType . YesNo , null )
{
Title = "Ryujinx - Warning" ,
2022-02-18 01:08:07 +01:00
Text = "You have trace logging enabled, which is designed to be used by developers only." ,
SecondaryText = "For optimal performance, it's recommended to disable trace logging. Would you like to disable trace logging now?"
2021-01-08 09:14:13 +01:00
} ;
if ( debugWarningDialog . Run ( ) = = ( int ) ResponseType . Yes )
{
2022-02-18 01:08:07 +01:00
ConfigurationState . Instance . Logger . EnableTrace . Value = false ;
2021-01-08 09:14:13 +01:00
SaveConfig ( ) ;
}
debugWarningDialog . Dispose ( ) ;
2019-09-02 18:03:57 +02:00
}
2021-01-08 09:14:13 +01:00
if ( ! string . IsNullOrWhiteSpace ( ConfigurationState . Instance . Graphics . ShadersDumpPath . Value ) )
2019-09-02 18:03:57 +02:00
{
2021-01-08 09:14:13 +01:00
MessageDialog shadersDumpWarningDialog = new MessageDialog ( this , DialogFlags . Modal , MessageType . Warning , ButtonsType . YesNo , null )
2020-03-30 23:39:46 +02:00
{
2021-01-08 09:14:13 +01:00
Title = "Ryujinx - Warning" ,
Text = "You have shader dumping enabled, which is designed to be used by developers only." ,
SecondaryText = "For optimal performance, it's recommended to disable shader dumping. Would you like to disable shader dumping now?"
} ;
2020-03-30 23:39:46 +02:00
2021-01-08 09:14:13 +01:00
if ( shadersDumpWarningDialog . Run ( ) = = ( int ) ResponseType . Yes )
{
ConfigurationState . Instance . Graphics . ShadersDumpPath . Value = "" ;
SaveConfig ( ) ;
2020-03-30 23:39:46 +02:00
}
2021-01-08 09:14:13 +01:00
shadersDumpWarningDialog . Dispose ( ) ;
}
}
2020-03-30 23:39:46 +02:00
2021-07-06 20:04:21 +02:00
public void LoadApplication ( string path , bool startFullscreen = false )
2021-01-08 09:14:13 +01:00
{
if ( _gameLoaded )
{
2021-03-18 23:44:39 +01:00
GtkDialog . CreateInfoDialog ( "A game has already been loaded" , "Please stop emulation or close the emulator before launching another game." ) ;
2021-01-08 09:14:13 +01:00
}
else
{
PerformanceCheck ( ) ;
2020-03-30 23:39:46 +02:00
2019-09-20 01:59:48 +02:00
Logger . RestartTime ( ) ;
2021-05-04 18:19:04 +02:00
RendererWidget = CreateRendererWidget ( ) ;
2021-07-06 20:04:21 +02:00
SwitchToRenderWidget ( startFullscreen ) ;
2021-05-04 18:19:04 +02:00
2021-01-08 09:14:13 +01:00
InitializeSwitchInstance ( ) ;
2020-01-21 23:23:11 +01:00
2020-07-07 04:41:07 +02:00
UpdateGraphicsConfig ( ) ;
2019-12-21 20:52:31 +01:00
2021-03-22 19:40:07 +01:00
SetupProgressUiHandlers ( ) ;
2020-09-01 11:09:42 +02:00
SystemVersion firmwareVersion = _contentManager . GetCurrentFirmwareVersion ( ) ;
2021-06-28 20:54:45 +02:00
bool isDirectory = Directory . Exists ( path ) ;
bool isFirmwareTitle = false ;
if ( path . StartsWith ( "@SystemContent" ) )
{
path = _virtualFileSystem . SwitchPathToSystemPath ( path ) ;
isFirmwareTitle = true ;
}
2020-09-01 11:09:42 +02:00
if ( ! SetupValidator . CanStartApplication ( _contentManager , path , out UserError userError ) )
{
if ( SetupValidator . CanFixStartApplication ( _contentManager , path , userError , out firmwareVersion ) )
{
if ( userError = = UserError . NoFirmware )
{
2021-01-08 09:14:13 +01:00
string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})" ;
2020-09-01 11:09:42 +02:00
2021-01-08 09:14:13 +01:00
ResponseType responseDialog = ( ResponseType ) GtkDialog . CreateConfirmationDialog ( "No Firmware Installed" , message ) . Run ( ) ;
2020-09-01 11:09:42 +02:00
2021-01-08 09:14:13 +01:00
if ( responseDialog ! = ResponseType . Yes )
{
2020-09-01 11:09:42 +02:00
UserErrorDialog . CreateUserErrorDialog ( userError ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . Dispose ( ) ;
2021-05-04 18:19:04 +02:00
SwitchToGameTable ( ) ;
RendererWidget . Dispose ( ) ;
2020-09-01 11:09:42 +02:00
return ;
}
}
if ( ! SetupValidator . TryFixStartApplication ( _contentManager , path , userError , out _ ) )
{
UserErrorDialog . CreateUserErrorDialog ( userError ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . Dispose ( ) ;
2021-05-04 18:19:04 +02:00
SwitchToGameTable ( ) ;
RendererWidget . Dispose ( ) ;
2020-09-01 11:09:42 +02:00
return ;
}
// Tell the user that we installed a firmware for them.
if ( userError = = UserError . NoFirmware )
{
firmwareVersion = _contentManager . GetCurrentFirmwareVersion ( ) ;
RefreshFirmwareLabel ( ) ;
2021-01-08 09:14:13 +01:00
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." ;
GtkDialog . CreateInfoDialog ( $"Firmware {firmwareVersion.VersionString} was installed" , message ) ;
2020-09-01 11:09:42 +02:00
}
}
else
{
UserErrorDialog . CreateUserErrorDialog ( userError ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . Dispose ( ) ;
2021-05-04 18:19:04 +02:00
SwitchToGameTable ( ) ;
RendererWidget . Dispose ( ) ;
2020-09-01 11:09:42 +02:00
return ;
}
}
Logger . Notice . Print ( LogClass . Application , $"Using Firmware Version: {firmwareVersion?.VersionString}" ) ;
2020-05-05 13:51:04 +02:00
2021-06-28 20:54:45 +02:00
if ( isFirmwareTitle )
{
Logger . Info ? . Print ( LogClass . Application , "Loading as Firmware Title (NCA)." ) ;
_emulationContext . LoadNca ( path ) ;
}
else if ( Directory . Exists ( path ) )
2019-09-02 18:03:57 +02:00
{
string [ ] romFsFiles = Directory . GetFiles ( path , "*.istorage" ) ;
if ( romFsFiles . Length = = 0 )
{
romFsFiles = Directory . GetFiles ( path , "*.romfs" ) ;
}
if ( romFsFiles . Length > 0 )
{
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . Application , "Loading as cart with RomFS." ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . LoadCart ( path , romFsFiles [ 0 ] ) ;
2019-09-02 18:03:57 +02:00
}
else
{
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . Application , "Loading as cart WITHOUT RomFS." ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . LoadCart ( path ) ;
2019-09-02 18:03:57 +02:00
}
}
else if ( File . Exists ( path ) )
{
switch ( System . IO . Path . GetExtension ( path ) . ToLowerInvariant ( ) )
{
case ".xci" :
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . Application , "Loading as XCI." ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . LoadXci ( path ) ;
2019-09-02 18:03:57 +02:00
break ;
case ".nca" :
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . Application , "Loading as NCA." ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . LoadNca ( path ) ;
2019-09-02 18:03:57 +02:00
break ;
case ".nsp" :
case ".pfs0" :
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . Application , "Loading as NSP." ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . LoadNsp ( path ) ;
2019-09-02 18:03:57 +02:00
break ;
default :
2021-03-18 23:44:39 +01:00
Logger . Info ? . Print ( LogClass . Application , "Loading as Homebrew." ) ;
2019-09-02 18:03:57 +02:00
try
{
2021-01-08 09:14:13 +01:00
_emulationContext . LoadProgram ( path ) ;
2019-09-02 18:03:57 +02:00
}
catch ( ArgumentOutOfRangeException )
{
2021-03-18 23:44:39 +01:00
Logger . Error ? . Print ( LogClass . Application , "The specified file is not supported by Ryujinx." ) ;
2019-09-02 18:03:57 +02:00
}
break ;
}
}
else
{
2020-08-04 01:32:53 +02:00
Logger . Warning ? . Print ( LogClass . Application , "Please specify a valid XCI/NCA/NSP/PFS0/NRO file." ) ;
2021-01-08 09:14:13 +01:00
_emulationContext . Dispose ( ) ;
2021-05-04 18:19:04 +02:00
RendererWidget . Dispose ( ) ;
2020-03-29 05:25:54 +02:00
return ;
2019-09-02 18:03:57 +02:00
}
2021-01-08 09:14:13 +01:00
_currentEmulatedGamePath = path ;
2020-01-21 23:23:11 +01:00
2020-02-13 18:43:29 +01:00
_deviceExitStatus . Reset ( ) ;
2020-02-06 12:38:24 +01:00
2020-11-17 22:40:19 +01:00
Translator . IsReadyForTranslation . Reset ( ) ;
#if MACOS_BUILD
2021-01-08 09:14:13 +01:00
CreateGameWindow ( ) ;
2020-11-17 22:40:19 +01:00
#else
Thread windowThread = new Thread ( ( ) = >
{
2021-01-08 09:14:13 +01:00
CreateGameWindow ( ) ;
2020-11-17 22:40:19 +01:00
} )
{
Name = "GUI.WindowThread"
} ;
windowThread . Start ( ) ;
#endif
2021-03-18 21:40:20 +01:00
_gameLoaded = true ;
_actionMenu . Sensitive = true ;
_lastScannedAmiiboId = "" ;
2019-09-02 18:03:57 +02:00
2020-01-12 03:10:55 +01:00
_firmwareInstallFile . Sensitive = false ;
_firmwareInstallDirectory . Sensitive = false ;
2021-01-08 09:14:13 +01:00
DiscordIntegrationModule . SwitchToPlayingState ( _emulationContext . Application . TitleIdText , _emulationContext . Application . TitleName ) ;
2019-09-02 18:03:57 +02:00
2021-01-08 09:14:13 +01:00
_applicationLibrary . LoadAndSaveMetaData ( _emulationContext . Application . TitleIdText , appMetadata = >
2019-09-02 18:03:57 +02:00
{
2020-01-12 04:01:04 +01:00
appMetadata . LastPlayed = DateTime . UtcNow . ToString ( ) ;
} ) ;
2019-09-02 18:03:57 +02:00
}
}
2021-05-04 18:19:04 +02:00
private RendererWidgetBase CreateRendererWidget ( )
2019-09-02 18:03:57 +02:00
{
2021-05-04 18:19:04 +02:00
if ( UseVulkan )
2020-08-18 03:49:37 +02:00
{
2021-05-04 18:19:04 +02:00
return new VKRenderer ( InputManager , ConfigurationState . Instance . Logger . GraphicsDebugLevel ) ;
2020-08-18 03:49:37 +02:00
}
2021-05-04 18:19:04 +02:00
else
{
return new GlRenderer ( InputManager , ConfigurationState . Instance . Logger . GraphicsDebugLevel ) ;
}
}
2020-08-18 03:49:37 +02:00
2021-07-06 20:04:21 +02:00
private void SwitchToRenderWidget ( bool startFullscreen = false )
2021-05-04 18:19:04 +02:00
{
_viewBox . Remove ( _gameTableWindow ) ;
RendererWidget . Expand = true ;
_viewBox . Child = RendererWidget ;
2021-01-25 00:02:00 +01:00
2021-05-04 18:19:04 +02:00
RendererWidget . ShowAll ( ) ;
EditFooterForGameRenderer ( ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
if ( Window . State . HasFlag ( Gdk . WindowState . Fullscreen ) )
{
ToggleExtraWidgets ( false ) ;
}
2021-07-06 20:04:21 +02:00
else if ( startFullscreen | | ConfigurationState . Instance . Ui . StartFullscreen . Value )
2021-05-04 18:19:04 +02:00
{
FullScreen_Toggled ( null , null ) ;
}
}
private void SwitchToGameTable ( )
{
if ( Window . State . HasFlag ( Gdk . WindowState . Fullscreen ) )
2019-09-02 18:03:57 +02:00
{
2021-05-04 18:19:04 +02:00
ToggleExtraWidgets ( true ) ;
}
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
RendererWidget . Exit ( ) ;
2020-07-23 15:12:19 +02:00
2021-05-04 18:19:04 +02:00
if ( RendererWidget . Window ! = Window & & RendererWidget . Window ! = null )
{
RendererWidget . Window . Dispose ( ) ;
}
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
RendererWidget . Dispose ( ) ;
2020-02-12 01:56:19 +01:00
2021-12-05 00:02:30 +01:00
if ( OperatingSystem . IsWindows ( ) )
{
_windowsMultimediaTimerResolution ? . Dispose ( ) ;
_windowsMultimediaTimerResolution = null ;
}
2021-05-04 18:19:04 +02:00
DisplaySleep . Restore ( ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
_viewBox . Remove ( RendererWidget ) ;
_viewBox . Add ( _gameTableWindow ) ;
2020-06-16 20:28:02 +02:00
2021-05-04 18:19:04 +02:00
_gameTableWindow . Expand = true ;
2020-02-13 18:43:29 +01:00
2021-05-04 18:19:04 +02:00
Window . Title = $"Ryujinx {Program.Version}" ;
2020-07-23 15:12:19 +02:00
2021-05-04 18:19:04 +02:00
_emulationContext = null ;
_gameLoaded = false ;
RendererWidget = null ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
DiscordIntegrationModule . SwitchToMainMenu ( ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
RecreateFooterForMenu ( ) ;
2021-01-08 09:14:13 +01:00
2021-05-04 18:19:04 +02:00
UpdateColumns ( ) ;
UpdateGameTable ( ) ;
2020-02-13 18:43:29 +01:00
2021-05-04 18:19:04 +02:00
Task . Run ( RefreshFirmwareLabel ) ;
Task . Run ( HandleRelaunch ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
_actionMenu . Sensitive = false ;
_firmwareInstallFile . Sensitive = true ;
_firmwareInstallDirectory . Sensitive = true ;
}
private void CreateGameWindow ( )
{
2021-12-05 00:02:30 +01:00
if ( OperatingSystem . IsWindows ( ) )
2021-05-04 18:19:04 +02:00
{
_windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution ( 1 ) ;
}
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
DisplaySleep . Prevent ( ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
RendererWidget . Initialize ( _emulationContext ) ;
2020-02-13 18:43:29 +01:00
2021-05-04 18:19:04 +02:00
RendererWidget . WaitEvent . WaitOne ( ) ;
2020-02-13 18:43:29 +01:00
2021-05-04 18:19:04 +02:00
RendererWidget . Start ( ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
Ptc . Close ( ) ;
PtcProfiler . Stop ( ) ;
2020-02-12 01:56:19 +01:00
2021-05-04 18:19:04 +02:00
_emulationContext . Dispose ( ) ;
_deviceExitStatus . Set ( ) ;
2020-01-21 23:23:11 +01:00
2021-05-04 18:19:04 +02:00
// NOTE: Everything that is here will not be executed when you close the UI.
Application . Invoke ( delegate
{
SwitchToGameTable ( ) ;
2020-01-21 23:23:11 +01:00
} ) ;
2020-02-13 18:43:29 +01:00
}
private void RecreateFooterForMenu ( )
{
2020-03-07 03:40:06 +01:00
_listStatusBox . Show ( ) ;
_statusBar . Hide ( ) ;
2020-02-13 18:43:29 +01:00
}
2020-02-12 01:56:19 +01:00
2021-01-08 09:14:13 +01:00
private void EditFooterForGameRenderer ( )
2020-02-13 18:43:29 +01:00
{
2020-03-07 03:40:06 +01:00
_listStatusBox . Hide ( ) ;
_statusBar . Show ( ) ;
2020-02-12 01:56:19 +01:00
}
public void ToggleExtraWidgets ( bool show )
{
2021-05-04 18:19:04 +02:00
if ( RendererWidget ! = null )
2020-02-12 01:56:19 +01:00
{
if ( show )
{
_menuBar . ShowAll ( ) ;
2020-03-07 03:40:06 +01:00
_footerBox . Show ( ) ;
_statusBar . Show ( ) ;
2020-02-12 01:56:19 +01:00
}
else
{
_menuBar . Hide ( ) ;
_footerBox . Hide ( ) ;
}
}
2020-01-21 23:23:11 +01:00
}
2021-01-08 09:14:13 +01:00
private void UpdateGameMetadata ( string titleId )
2020-01-21 23:23:11 +01:00
{
if ( _gameLoaded )
{
2021-01-08 09:14:13 +01:00
_applicationLibrary . LoadAndSaveMetaData ( titleId , appMetadata = >
2020-01-21 23:23:11 +01:00
{
DateTime lastPlayedDateTime = DateTime . Parse ( appMetadata . LastPlayed ) ;
double sessionTimePlayed = DateTime . UtcNow . Subtract ( lastPlayedDateTime ) . TotalSeconds ;
2019-09-02 18:03:57 +02:00
2020-01-21 23:23:11 +01:00
appMetadata . TimePlayed + = Math . Round ( sessionTimePlayed , MidpointRounding . AwayFromZero ) ;
} ) ;
2019-09-02 18:03:57 +02:00
}
}
2021-01-08 09:14:13 +01:00
public void UpdateGraphicsConfig ( )
2020-07-07 04:41:07 +02:00
{
2020-12-16 03:19:07 +01:00
int resScale = ConfigurationState . Instance . Graphics . ResScale ;
2020-07-07 04:41:07 +02:00
float resScaleCustom = ConfigurationState . Instance . Graphics . ResScaleCustom ;
2020-12-16 03:19:07 +01:00
Graphics . Gpu . GraphicsConfig . ResScale = ( resScale = = - 1 ) ? resScaleCustom : resScale ;
Graphics . Gpu . GraphicsConfig . MaxAnisotropy = ConfigurationState . Instance . Graphics . MaxAnisotropy ;
Graphics . Gpu . GraphicsConfig . ShadersDumpPath = ConfigurationState . Instance . Graphics . ShadersDumpPath ;
2020-11-13 00:15:34 +01:00
Graphics . Gpu . GraphicsConfig . EnableShaderCache = ConfigurationState . Instance . Graphics . EnableShaderCache ;
2020-07-07 04:41:07 +02:00
}
2021-01-08 09:14:13 +01:00
public void SaveConfig ( )
2020-05-03 04:00:53 +02:00
{
2020-05-03 12:08:21 +02:00
ConfigurationState . Instance . ToFileFormat ( ) . SaveConfig ( Program . ConfigurationPath ) ;
2020-05-03 04:00:53 +02:00
}
2021-01-08 09:14:13 +01:00
private void End ( )
2019-09-02 18:03:57 +02:00
{
2019-11-29 05:32:51 +01:00
if ( _ending )
{
return ;
}
_ending = true ;
2021-01-08 09:14:13 +01:00
if ( _emulationContext ! = null )
2019-09-02 18:03:57 +02:00
{
2021-01-08 09:14:13 +01:00
UpdateGameMetadata ( _emulationContext . Application . TitleIdText ) ;
2020-02-06 12:38:24 +01:00
2021-05-04 18:19:04 +02:00
if ( RendererWidget ! = null )
2020-02-06 12:38:24 +01:00
{
2021-01-08 09:14:13 +01:00
// We tell the widget that we are exiting.
2021-05-04 18:19:04 +02:00
RendererWidget . Exit ( ) ;
2020-02-13 18:43:29 +01:00
// Wait for the other thread to dispose the HLE context before exiting.
_deviceExitStatus . WaitOne ( ) ;
2021-05-04 18:19:04 +02:00
RendererWidget . Dispose ( ) ;
2020-02-06 12:38:24 +01:00
}
2019-09-02 18:03:57 +02:00
}
2020-01-21 23:23:11 +01:00
Dispose ( ) ;
2021-01-08 09:14:13 +01:00
Program . Exit ( ) ;
2020-01-21 23:23:11 +01:00
Application . Quit ( ) ;
}
2021-01-08 09:14:13 +01:00
//
// Events
//
2019-12-22 03:49:51 +01:00
private void Application_Added ( object sender , ApplicationAddedEventArgs args )
2019-11-29 05:32:51 +01:00
{
Application . Invoke ( delegate
{
_tableStore . AppendValues (
2019-12-22 03:49:51 +01:00
args . AppData . Favorite ,
new Gdk . Pixbuf ( args . AppData . Icon , 75 , 75 ) ,
$"{args.AppData.TitleName}\n{args.AppData.TitleId.ToUpper()}" ,
args . AppData . Developer ,
args . AppData . Version ,
args . AppData . TimePlayed ,
args . AppData . LastPlayed ,
args . AppData . FileExtension ,
args . AppData . FileSize ,
2020-03-25 18:09:38 +01:00
args . AppData . Path ,
args . AppData . ControlHolder ) ;
2020-01-31 19:21:46 +01:00
} ) ;
}
2019-12-22 03:49:51 +01:00
2020-01-31 19:21:46 +01:00
private void ApplicationCount_Updated ( object sender , ApplicationCountUpdatedEventArgs args )
{
Application . Invoke ( delegate
{
2019-12-22 03:49:51 +01:00
_progressLabel . Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded" ;
2020-01-31 19:21:46 +01:00
float barValue = 0 ;
if ( args . NumAppsFound ! = 0 )
{
barValue = ( float ) args . NumAppsLoaded / args . NumAppsFound ;
}
2021-03-03 01:39:36 +01:00
_progressBar . Fraction = barValue ;
2020-06-26 12:30:16 +02:00
2021-01-08 09:14:13 +01:00
// Reset the vertical scrollbar to the top when titles finish loading
if ( args . NumAppsLoaded = = args . NumAppsFound )
2020-06-26 12:30:16 +02:00
{
_gameTableWindow . Vadjustment . Value = 0 ;
}
2019-11-29 05:32:51 +01:00
} ) ;
}
2020-03-07 03:40:06 +01:00
private void Update_StatusBar ( object sender , StatusUpdatedEventArgs args )
{
Application . Invoke ( delegate
{
2021-12-23 17:33:56 +01:00
_gameStatus . Text = args . GameStatus ;
_fifoStatus . Text = args . FifoStatus ;
_gpuName . Text = args . GpuName ;
_dockedMode . Text = args . DockedMode ;
_aspectRatio . Text = args . AspectRatio ;
_volumeStatus . Text = GetVolumeLabelText ( args . Volume ) ;
2020-03-07 03:40:06 +01:00
if ( args . VSyncEnabled )
{
_vSyncStatus . Attributes = new Pango . AttrList ( ) ;
_vSyncStatus . Attributes . Insert ( new Pango . AttrForeground ( 11822 , 60138 , 51657 ) ) ;
}
else
{
_vSyncStatus . Attributes = new Pango . AttrList ( ) ;
_vSyncStatus . Attributes . Insert ( new Pango . AttrForeground ( ushort . MaxValue , 17733 , 21588 ) ) ;
}
} ) ;
}
2019-11-29 05:32:51 +01:00
private void FavToggle_Toggled ( object sender , ToggledArgs args )
{
_tableStore . GetIter ( out TreeIter treeIter , new TreePath ( args . Path ) ) ;
2021-01-08 09:14:13 +01:00
string titleId = _tableStore . GetValue ( treeIter , 2 ) . ToString ( ) . Split ( "\n" ) [ 1 ] . ToLower ( ) ;
bool newToggleValue = ! ( bool ) _tableStore . GetValue ( treeIter , 0 ) ;
2019-10-13 08:02:07 +02:00
2020-01-12 04:01:04 +01:00
_tableStore . SetValue ( treeIter , 0 , newToggleValue ) ;
2019-11-29 05:32:51 +01:00
2021-01-08 09:14:13 +01:00
_applicationLibrary . LoadAndSaveMetaData ( titleId , appMetadata = >
2019-11-29 05:32:51 +01:00
{
2020-01-12 04:01:04 +01:00
appMetadata . Favorite = newToggleValue ;
} ) ;
2019-11-29 05:32:51 +01:00
}
2020-06-26 12:30:16 +02:00
private void Column_Clicked ( object sender , EventArgs args )
{
TreeViewColumn column = ( TreeViewColumn ) sender ;
ConfigurationState . Instance . Ui . ColumnSort . SortColumnId . Value = column . SortColumnId ;
ConfigurationState . Instance . Ui . ColumnSort . SortAscending . Value = column . SortOrder = = SortType . Ascending ;
SaveConfig ( ) ;
}
2019-11-29 05:32:51 +01:00
private void Row_Activated ( object sender , RowActivatedArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-22 03:49:51 +01:00
_gameTableSelection . GetSelected ( out TreeIter treeIter ) ;
2021-01-08 09:14:13 +01:00
2019-11-29 05:32:51 +01:00
string path = ( string ) _tableStore . GetValue ( treeIter , 9 ) ;
2019-09-02 18:03:57 +02:00
LoadApplication ( path ) ;
}
2020-11-19 01:34:28 +01:00
private void VSyncStatus_Clicked ( object sender , ButtonReleaseEventArgs args )
{
_emulationContext . EnableDeviceVsync = ! _emulationContext . EnableDeviceVsync ;
2021-03-19 00:09:33 +01:00
Logger . Info ? . Print ( LogClass . Application , $"VSync toggled to: {_emulationContext.EnableDeviceVsync}" ) ;
2020-11-19 01:34:28 +01:00
}
private void DockedMode_Clicked ( object sender , ButtonReleaseEventArgs args )
{
ConfigurationState . Instance . System . EnableDockedMode . Value = ! ConfigurationState . Instance . System . EnableDockedMode . Value ;
}
2021-12-23 17:33:56 +01:00
private string GetVolumeLabelText ( float volume )
{
string icon = volume = = 0 ? "🔇" : "🔊" ;
return $"{icon} {(int)(volume * 100)}%" ;
}
private void VolumeStatus_Clicked ( object sender , ButtonReleaseEventArgs args )
{
if ( _emulationContext ! = null )
{
if ( _emulationContext . IsAudioMuted ( ) )
{
_emulationContext . SetVolume ( ConfigurationState . Instance . System . AudioVolume ) ;
}
else
{
_emulationContext . SetVolume ( 0 ) ;
}
}
}
2020-12-16 03:19:07 +01:00
private void AspectRatio_Clicked ( object sender , ButtonReleaseEventArgs args )
{
AspectRatio aspectRatio = ConfigurationState . Instance . Graphics . AspectRatio . Value ;
2022-02-13 14:50:07 +01:00
ConfigurationState . Instance . Graphics . AspectRatio . Value = ( ( int ) aspectRatio + 1 ) > Enum . GetNames < AspectRatio > ( ) . Length - 1 ? AspectRatio . Fixed4x3 : aspectRatio + 1 ;
2020-12-16 03:19:07 +01:00
}
2019-12-22 03:49:51 +01:00
private void Row_Clicked ( object sender , ButtonReleaseEventArgs args )
{
2021-01-08 09:14:13 +01:00
if ( args . Event . Button ! = 3 /* Right Click */ )
{
return ;
}
2019-12-22 03:49:51 +01:00
_gameTableSelection . GetSelected ( out TreeIter treeIter ) ;
2021-01-08 09:14:13 +01:00
if ( treeIter . UserData = = IntPtr . Zero )
{
return ;
}
string titleFilePath = _tableStore . GetValue ( treeIter , 9 ) . ToString ( ) ;
string titleName = _tableStore . GetValue ( treeIter , 2 ) . ToString ( ) . Split ( "\n" ) [ 0 ] ;
string titleId = _tableStore . GetValue ( treeIter , 2 ) . ToString ( ) . Split ( "\n" ) [ 1 ] . ToLower ( ) ;
2019-12-22 03:49:51 +01:00
2020-03-25 18:09:38 +01:00
BlitStruct < ApplicationControlProperty > controlData = ( BlitStruct < ApplicationControlProperty > ) _tableStore . GetValue ( treeIter , 10 ) ;
2021-08-12 23:56:24 +02:00
_ = new GameTableContextMenu ( this , _virtualFileSystem , _accountManager , _libHacHorizonManager . RyujinxClient , titleFilePath , titleName , titleId , controlData ) ;
2019-12-22 03:49:51 +01:00
}
2019-11-29 05:32:51 +01:00
private void Load_Application_File ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-09-14 23:52:08 +02:00
using ( FileChooserNative fileChooser = new FileChooserNative ( "Choose the file to open" , this , FileChooserAction . Open , "Open" , "Cancel" ) )
2019-09-02 18:03:57 +02:00
{
2021-09-14 23:52:08 +02:00
FileFilter filter = new FileFilter ( )
{
Name = "Switch Executables"
} ;
filter . AddPattern ( "*.xci" ) ;
filter . AddPattern ( "*.nsp" ) ;
filter . AddPattern ( "*.pfs0" ) ;
filter . AddPattern ( "*.nca" ) ;
filter . AddPattern ( "*.nro" ) ;
filter . AddPattern ( "*.nso" ) ;
fileChooser . AddFilter ( filter ) ;
2021-01-08 09:14:13 +01:00
if ( fileChooser . Run ( ) = = ( int ) ResponseType . Accept )
{
LoadApplication ( fileChooser . Filename ) ;
}
2019-09-02 18:03:57 +02:00
}
}
2019-11-29 05:32:51 +01:00
private void Load_Application_Folder ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-09-14 23:52:08 +02:00
using ( FileChooserNative fileChooser = new FileChooserNative ( "Choose the folder to open" , this , FileChooserAction . SelectFolder , "Open" , "Cancel" ) )
2019-09-02 18:03:57 +02:00
{
2021-01-08 09:14:13 +01:00
if ( fileChooser . Run ( ) = = ( int ) ResponseType . Accept )
{
LoadApplication ( fileChooser . Filename ) ;
}
2019-09-02 18:03:57 +02:00
}
}
2021-06-28 20:54:45 +02:00
private void FileMenu_StateChanged ( object o , StateChangedArgs args )
{
_appletMenu . Sensitive = _emulationContext = = null & & _contentManager . GetCurrentFirmwareVersion ( ) ! = null & & _contentManager . GetCurrentFirmwareVersion ( ) . Major > 3 ;
_loadApplicationFile . Sensitive = _emulationContext = = null ;
_loadApplicationFolder . Sensitive = _emulationContext = = null ;
}
private void Load_Mii_Edit_Applet ( object sender , EventArgs args )
{
2022-03-22 20:46:16 +01:00
string contentPath = _contentManager . GetInstalledContentPath ( 0x0100000000001009 , StorageId . BuiltInSystem , NcaContentType . Program ) ;
2021-06-28 20:54:45 +02:00
LoadApplication ( contentPath ) ;
}
2019-11-29 05:32:51 +01:00
private void Open_Ryu_Folder ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-01-08 09:14:13 +01:00
OpenHelper . OpenFolder ( AppDataManager . BaseDirPath ) ;
2019-09-02 18:03:57 +02:00
}
2020-09-20 06:31:05 +02:00
private void OpenLogsFolder_Pressed ( object sender , EventArgs args )
{
2022-03-04 18:03:16 +01:00
string logPath = System . IO . Path . Combine ( ReleaseInformations . GetBaseApplicationDirectory ( ) , "Logs" ) ;
2020-09-20 06:31:05 +02:00
2021-01-08 09:14:13 +01:00
new DirectoryInfo ( logPath ) . Create ( ) ;
2020-09-20 06:31:05 +02:00
2021-01-08 09:14:13 +01:00
OpenHelper . OpenFolder ( logPath ) ;
2020-09-20 06:31:05 +02:00
}
2019-11-29 05:32:51 +01:00
private void Exit_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-01-14 23:30:52 +01:00
if ( ! _gameLoaded | | ! ConfigurationState . Instance . ShowConfirmExit | | GtkDialog . CreateExitDialog ( ) )
2020-10-10 01:06:48 +02:00
{
2021-01-08 09:14:13 +01:00
End ( ) ;
2020-10-10 01:06:48 +02:00
}
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Window_Close ( object sender , DeleteEventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-01-14 23:30:52 +01:00
if ( ! _gameLoaded | | ! ConfigurationState . Instance . ShowConfirmExit | | GtkDialog . CreateExitDialog ( ) )
2020-10-10 01:06:48 +02:00
{
2021-01-08 09:14:13 +01:00
End ( ) ;
2020-10-10 01:06:48 +02:00
}
else
{
args . RetVal = true ;
}
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void StopEmulation_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-09-11 21:16:48 +02:00
if ( _emulationContext ! = null )
{
UpdateGameMetadata ( _emulationContext . Application . TitleIdText ) ;
}
2021-09-11 22:08:25 +02:00
_pauseEmulation . Sensitive = false ;
2021-09-18 14:31:44 +02:00
_resumeEmulation . Sensitive = false ;
2021-05-04 18:19:04 +02:00
RendererWidget ? . Exit ( ) ;
2019-09-02 18:03:57 +02:00
}
2021-09-11 22:08:25 +02:00
private void PauseEmulation_Pressed ( object sender , EventArgs args )
{
2021-09-18 14:31:44 +02:00
_pauseEmulation . Sensitive = false ;
_resumeEmulation . Sensitive = true ;
2021-09-11 22:08:25 +02:00
_emulationContext . System . TogglePauseEmulation ( true ) ;
}
private void ResumeEmulation_Pressed ( object sender , EventArgs args )
{
2021-09-18 14:31:44 +02:00
_pauseEmulation . Sensitive = true ;
_resumeEmulation . Sensitive = false ;
2021-09-11 22:08:25 +02:00
_emulationContext . System . TogglePauseEmulation ( false ) ;
}
public void ActivatePauseMenu ( )
{
_pauseEmulation . Sensitive = true ;
2021-09-18 14:31:44 +02:00
_resumeEmulation . Sensitive = false ;
2021-09-11 22:08:25 +02:00
}
public void TogglePause ( )
{
2021-09-18 14:31:44 +02:00
_pauseEmulation . Sensitive ^ = true ;
_resumeEmulation . Sensitive ^ = true ;
_emulationContext . System . TogglePauseEmulation ( _resumeEmulation . Sensitive ) ;
2021-09-11 22:08:25 +02:00
}
2020-01-12 03:10:55 +01:00
private void Installer_File_Pressed ( object o , EventArgs args )
{
2021-09-14 23:52:08 +02:00
FileChooserNative fileChooser = new FileChooserNative ( "Choose the firmware file to open" , this , FileChooserAction . Open , "Open" , "Cancel" ) ;
FileFilter filter = new FileFilter
{
Name = "Switch Firmware Files"
} ;
filter . AddPattern ( "*.zip" ) ;
filter . AddPattern ( "*.xci" ) ;
2020-01-12 03:10:55 +01:00
2021-09-14 23:52:08 +02:00
fileChooser . AddFilter ( filter ) ;
2020-01-12 03:10:55 +01:00
HandleInstallerDialog ( fileChooser ) ;
}
private void Installer_Directory_Pressed ( object o , EventArgs args )
{
2021-09-14 23:52:08 +02:00
FileChooserNative directoryChooser = new FileChooserNative ( "Choose the firmware directory to open" , this , FileChooserAction . SelectFolder , "Open" , "Cancel" ) ;
2020-01-12 03:10:55 +01:00
HandleInstallerDialog ( directoryChooser ) ;
}
2021-09-14 23:52:08 +02:00
private void HandleInstallerDialog ( FileChooserNative fileChooser )
2020-01-12 03:10:55 +01:00
{
if ( fileChooser . Run ( ) = = ( int ) ResponseType . Accept )
{
try
{
string filename = fileChooser . Filename ;
fileChooser . Dispose ( ) ;
2020-05-03 04:00:53 +02:00
SystemVersion firmwareVersion = _contentManager . VerifyFirmwarePackage ( filename ) ;
2020-01-12 03:10:55 +01:00
2021-08-12 23:56:24 +02:00
if ( firmwareVersion is null )
2020-01-12 03:10:55 +01:00
{
2021-01-08 09:14:13 +01:00
GtkDialog . CreateErrorDialog ( $"A valid system firmware was not found in {filename}." ) ;
2020-01-12 03:10:55 +01:00
return ;
}
2021-08-12 23:56:24 +02:00
string dialogTitle = $"Install Firmware {firmwareVersion.VersionString}" ;
2020-05-03 04:00:53 +02:00
SystemVersion currentVersion = _contentManager . GetCurrentFirmwareVersion ( ) ;
2020-01-12 03:10:55 +01:00
string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed." ;
if ( currentVersion ! = null )
{
2021-01-08 09:14:13 +01:00
dialogMessage + = $"\n\nThis will replace the current system version {currentVersion.VersionString}. " ;
2020-01-12 03:10:55 +01:00
}
2021-01-08 09:14:13 +01:00
dialogMessage + = "\n\nDo you want to continue?" ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
ResponseType responseInstallDialog = ( ResponseType ) GtkDialog . CreateConfirmationDialog ( dialogTitle , dialogMessage ) . Run ( ) ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
MessageDialog waitingDialog = GtkDialog . CreateWaitingDialog ( dialogTitle , "Installing firmware..." ) ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
if ( responseInstallDialog = = ResponseType . Yes )
2020-01-12 03:10:55 +01:00
{
2020-08-04 01:32:53 +02:00
Logger . Info ? . Print ( LogClass . Application , $"Installing firmware {firmwareVersion.VersionString}" ) ;
2021-04-04 14:06:59 +02:00
2020-01-12 03:10:55 +01:00
Thread thread = new Thread ( ( ) = >
{
2021-01-08 09:14:13 +01:00
Application . Invoke ( delegate
2020-01-12 03:10:55 +01:00
{
2021-01-08 09:14:13 +01:00
waitingDialog . Run ( ) ;
} ) ;
2020-01-12 03:10:55 +01:00
try
{
2020-01-21 23:23:11 +01:00
_contentManager . InstallFirmware ( filename ) ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
Application . Invoke ( delegate
2020-01-12 03:10:55 +01:00
{
2021-01-08 09:14:13 +01:00
waitingDialog . Dispose ( ) ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
string message = $"System version {firmwareVersion.VersionString} successfully installed." ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
GtkDialog . CreateInfoDialog ( dialogTitle , message ) ;
Logger . Info ? . Print ( LogClass . Application , message ) ;
2021-06-28 20:54:45 +02:00
// Purge Applet Cache.
DirectoryInfo miiEditorCacheFolder = new DirectoryInfo ( System . IO . Path . Combine ( AppDataManager . GamesDirPath , "0100000000001009" , "cache" ) ) ;
if ( miiEditorCacheFolder . Exists )
{
miiEditorCacheFolder . Delete ( true ) ;
}
2021-01-08 09:14:13 +01:00
} ) ;
2020-01-12 03:10:55 +01:00
}
catch ( Exception ex )
{
2021-01-08 09:14:13 +01:00
Application . Invoke ( delegate
2020-01-12 03:10:55 +01:00
{
2021-01-08 09:14:13 +01:00
waitingDialog . Dispose ( ) ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
GtkDialog . CreateErrorDialog ( ex . Message ) ;
} ) ;
2020-01-12 03:10:55 +01:00
}
finally
{
RefreshFirmwareLabel ( ) ;
}
} ) ;
2020-01-13 01:21:54 +01:00
thread . Name = "GUI.FirmwareInstallerThread" ;
2020-01-12 03:10:55 +01:00
thread . Start ( ) ;
}
}
2022-01-12 12:22:19 +01:00
catch ( MissingKeyException ex )
2021-01-26 18:45:07 +01:00
{
Logger . Error ? . Print ( LogClass . Application , ex . ToString ( ) ) ;
2021-08-12 22:48:15 +02:00
UserErrorDialog . CreateUserErrorDialog ( UserError . FirmwareParsingFailed ) ;
2021-01-26 18:45:07 +01:00
}
2020-01-12 03:10:55 +01:00
catch ( Exception ex )
{
2021-01-08 09:14:13 +01:00
GtkDialog . CreateErrorDialog ( ex . Message ) ;
}
}
else
{
fileChooser . Dispose ( ) ;
}
}
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
private void RefreshFirmwareLabel ( )
{
SystemVersion currentFirmware = _contentManager . GetCurrentFirmwareVersion ( ) ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
Application . Invoke ( delegate
{
_firmwareVersionLabel . Text = currentFirmware ! = null ? currentFirmware . VersionString : "0.0.0" ;
} ) ;
}
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
private void HandleRelaunch ( )
{
if ( _userChannelPersistence . PreviousIndex ! = - 1 & & _userChannelPersistence . ShouldRestart )
{
_userChannelPersistence . ShouldRestart = false ;
2020-01-12 03:10:55 +01:00
2021-01-08 09:14:13 +01:00
LoadApplication ( _currentEmulatedGamePath ) ;
2020-01-12 03:10:55 +01:00
}
else
{
2021-01-08 09:14:13 +01:00
// otherwise, clear state.
_userChannelPersistence = new UserChannelPersistence ( ) ;
_currentEmulatedGamePath = null ;
2020-01-12 03:10:55 +01:00
}
}
2020-12-01 23:02:27 +01:00
private void FullScreen_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-01-08 09:14:13 +01:00
if ( ! Window . State . HasFlag ( Gdk . WindowState . Fullscreen ) )
2019-09-02 18:03:57 +02:00
{
Fullscreen ( ) ;
2020-02-12 01:56:19 +01:00
ToggleExtraWidgets ( false ) ;
2019-09-02 18:03:57 +02:00
}
else
{
Unfullscreen ( ) ;
2020-02-12 01:56:19 +01:00
ToggleExtraWidgets ( true ) ;
2019-09-02 18:03:57 +02:00
}
}
2020-12-01 23:02:27 +01:00
private void StartFullScreen_Toggled ( object sender , EventArgs args )
{
ConfigurationState . Instance . Ui . StartFullscreen . Value = _startFullScreen . Active ;
SaveConfig ( ) ;
}
2022-03-15 02:35:41 +01:00
private void ShowConsole_Toggled ( object sender , EventArgs args )
{
ConfigurationState . Instance . Ui . ShowConsole . Value = _showConsole . Active ;
SaveConfig ( ) ;
}
2021-04-23 22:26:31 +02:00
private void OptionMenu_StateChanged ( object o , StateChangedArgs args )
{
_manageUserProfiles . Sensitive = _emulationContext = = null ;
}
2019-11-29 05:32:51 +01:00
private void Settings_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-02-20 01:34:41 +01:00
SettingsWindow settingsWindow = new SettingsWindow ( this , _virtualFileSystem , _contentManager ) ;
settingsWindow . SetSizeRequest ( ( int ) ( settingsWindow . DefaultWidth * Program . WindowScaleFactor ) , ( int ) ( settingsWindow . DefaultHeight * Program . WindowScaleFactor ) ) ;
settingsWindow . Show ( ) ;
2019-09-02 18:03:57 +02:00
}
2021-07-24 20:48:00 +02:00
private void HideUi_Pressed ( object sender , EventArgs args )
{
ToggleExtraWidgets ( false ) ;
}
2022-01-03 09:39:43 +01:00
private void ManageCheats_Pressed ( object sender , EventArgs args )
{
var window = new CheatWindow ( _virtualFileSystem , _emulationContext . Application . TitleId , _emulationContext . Application . TitleName ) ;
window . Destroyed + = CheatWindow_Destroyed ;
window . Show ( ) ;
}
private void CheatWindow_Destroyed ( object sender , EventArgs e )
{
_emulationContext . EnableCheats ( ) ;
( sender as CheatWindow ) . Destroyed - = CheatWindow_Destroyed ;
}
2021-04-23 22:26:31 +02:00
private void ManageUserProfiles_Pressed ( object sender , EventArgs args )
{
UserProfilesManagerWindow userProfilesManagerWindow = new UserProfilesManagerWindow ( _accountManager , _contentManager , _virtualFileSystem ) ;
userProfilesManagerWindow . SetSizeRequest ( ( int ) ( userProfilesManagerWindow . DefaultWidth * Program . WindowScaleFactor ) , ( int ) ( userProfilesManagerWindow . DefaultHeight * Program . WindowScaleFactor ) ) ;
userProfilesManagerWindow . Show ( ) ;
}
2020-12-16 01:41:42 +01:00
private void Simulate_WakeUp_Message_Pressed ( object sender , EventArgs args )
{
2021-01-08 09:14:13 +01:00
if ( _emulationContext ! = null )
{
_emulationContext . System . SimulateWakeUpMessage ( ) ;
}
2020-12-16 01:41:42 +01:00
}
2021-03-18 21:40:20 +01:00
private void ActionMenu_StateChanged ( object o , StateChangedArgs args )
{
2021-06-28 22:09:43 +02:00
_scanAmiibo . Sensitive = _emulationContext ! = null & & _emulationContext . System . SearchingForAmiibo ( out int _ ) ;
_takeScreenshot . Sensitive = _emulationContext ! = null ;
2021-03-18 21:40:20 +01:00
}
private void Scan_Amiibo ( object sender , EventArgs args )
{
if ( _emulationContext . System . SearchingForAmiibo ( out int deviceId ) )
{
AmiiboWindow amiiboWindow = new AmiiboWindow
{
LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll ,
LastScannedAmiiboId = _lastScannedAmiiboId ,
DeviceId = deviceId ,
TitleId = _emulationContext . Application . TitleIdText . ToUpper ( )
} ;
amiiboWindow . DeleteEvent + = AmiiboWindow_DeleteEvent ;
amiiboWindow . Show ( ) ;
}
else
{
GtkDialog . CreateInfoDialog ( $"Amiibo" , "The game is currently not ready to receive Amiibo scan data. Ensure that you have an Amiibo-compatible game open and ready to receive Amiibo scan data." ) ;
}
}
2021-06-28 22:09:43 +02:00
private void Take_Screenshot ( object sender , EventArgs args )
{
if ( _emulationContext ! = null & & RendererWidget ! = null )
{
RendererWidget . ScreenshotRequested = true ;
}
}
2021-03-18 21:40:20 +01:00
private void AmiiboWindow_DeleteEvent ( object sender , DeleteEventArgs args )
{
if ( ( ( AmiiboWindow ) sender ) . AmiiboId ! = "" & & ( ( AmiiboWindow ) sender ) . Response = = ResponseType . Ok )
{
_lastScannedAmiiboId = ( ( AmiiboWindow ) sender ) . AmiiboId ;
_lastScannedAmiiboShowAll = ( ( AmiiboWindow ) sender ) . LastScannedAmiiboShowAll ;
_emulationContext . System . ScanAmiibo ( ( ( AmiiboWindow ) sender ) . DeviceId , ( ( AmiiboWindow ) sender ) . AmiiboId , ( ( AmiiboWindow ) sender ) . UseRandomUuid ) ;
}
}
2019-11-29 05:32:51 +01:00
private void Update_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2020-09-29 22:05:25 +02:00
if ( Updater . CanUpdate ( true ) )
2019-09-02 18:03:57 +02:00
{
2021-02-23 16:19:02 +01:00
Updater . BeginParse ( this , true ) . ContinueWith ( task = >
{
2021-03-18 23:44:39 +01:00
Logger . Error ? . Print ( LogClass . Application , $"Updater error: {task.Exception}" ) ;
2021-02-23 16:19:02 +01:00
} , TaskContinuationOptions . OnlyOnFaulted ) ;
2019-09-02 18:03:57 +02:00
}
}
2019-11-29 05:32:51 +01:00
private void About_Pressed ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2021-02-20 01:34:41 +01:00
AboutWindow aboutWindow = new AboutWindow ( ) ;
aboutWindow . SetSizeRequest ( ( int ) ( aboutWindow . DefaultWidth * Program . WindowScaleFactor ) , ( int ) ( aboutWindow . DefaultHeight * Program . WindowScaleFactor ) ) ;
aboutWindow . Show ( ) ;
2019-11-29 05:32:51 +01:00
}
private void Fav_Toggled ( object sender , EventArgs args )
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . FavColumn . Value = _favToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Icon_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . IconColumn . Value = _iconToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2021-02-09 10:24:37 +01:00
private void App_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . AppColumn . Value = _appToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Developer_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . DevColumn . Value = _developerToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Version_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . VersionColumn . Value = _versionToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void TimePlayed_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . TimePlayedColumn . Value = _timePlayedToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void LastPlayed_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . LastPlayedColumn . Value = _lastPlayedToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void FileExt_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . FileExtColumn . Value = _fileExtToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void FileSize_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . FileSizeColumn . Value = _fileSizeToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
2019-09-02 18:03:57 +02:00
}
2019-11-29 05:32:51 +01:00
private void Path_Toggled ( object sender , EventArgs args )
2019-09-02 18:03:57 +02:00
{
2019-12-21 20:52:31 +01:00
ConfigurationState . Instance . Ui . GuiColumns . PathColumn . Value = _pathToggle . Active ;
2019-11-29 05:32:51 +01:00
2019-12-21 20:52:31 +01:00
SaveConfig ( ) ;
2019-11-29 05:32:51 +01:00
UpdateColumns ( ) ;
}
private void RefreshList_Pressed ( object sender , ButtonReleaseEventArgs args )
{
UpdateGameTable ( ) ;
}
2019-09-02 18:03:57 +02:00
}
2021-01-19 03:31:59 +01:00
}