From 9a1863c752f3b87701a722838bc02b37aa1cbcda Mon Sep 17 00:00:00 2001 From: Evan Husted Date: Mon, 7 Oct 2024 18:34:04 -0500 Subject: [PATCH] You see, when I said remove GTK3, what I actually meant was remove references to it. Here's removing GTK3. --- src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs | 205 -- .../Input/GTK3/GTK3KeyboardDriver.cs | 99 - .../Input/GTK3/GTK3MappingHelper.cs | 178 - src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs | 90 - .../Input/GTK3/GTK3MouseDriver.cs | 108 - .../Modules/Updater/UpdateDialog.cs | 95 - .../Modules/Updater/UpdateDialog.glade | 127 - src/Ryujinx.Gtk3/Modules/Updater/Updater.cs | 622 ---- src/Ryujinx.Gtk3/Program.cs | 403 --- src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj | 103 - src/Ryujinx.Gtk3/Ryujinx.ico | Bin 108122 -> 0 bytes .../UI/Applet/ErrorAppletDialog.cs | 31 - .../UI/Applet/GtkDynamicTextInputHandler.cs | 108 - .../UI/Applet/GtkHostUIHandler.cs | 203 -- src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs | 90 - .../UI/Applet/SwkbdAppletDialog.cs | 127 - src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs | 158 - src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs | 135 - src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs | 33 - src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs | 36 - src/Ryujinx.Gtk3/UI/MainWindow.cs | 1993 ---------- src/Ryujinx.Gtk3/UI/MainWindow.glade | 1006 ----- src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs | 142 - .../UI/OpenToolkitBindingsContext.cs | 20 - src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs | 813 ----- src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs | 49 - src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs | 28 - src/Ryujinx.Gtk3/UI/VulkanRenderer.cs | 93 - .../Widgets/GameTableContextMenu.Designer.cs | 233 -- .../UI/Widgets/GameTableContextMenu.cs | 634 ---- src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs | 114 - src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs | 37 - src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs | 57 - .../UI/Widgets/ProfileDialog.glade | 124 - .../UI/Widgets/RawInputToTextEntry.cs | 27 - .../UI/Widgets/UserErrorDialog.cs | 123 - .../UI/Windows/AboutWindow.Designer.cs | 511 --- src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs | 85 - .../UI/Windows/AmiiboWindow.Designer.cs | 190 - src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs | 438 --- src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs | 298 -- src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs | 163 - src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade | 150 - .../UI/Windows/ControllerWindow.cs | 1232 ------- .../UI/Windows/ControllerWindow.glade | 2241 ------------ src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs | 288 -- src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade | 202 -- src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs | 847 ----- .../UI/Windows/SettingsWindow.glade | 3221 ----------------- .../UI/Windows/TitleUpdateWindow.cs | 234 -- .../UI/Windows/TitleUpdateWindow.glade | 214 -- .../UserProfilesManagerWindow.Designer.cs | 255 -- .../UI/Windows/UserProfilesManagerWindow.cs | 326 -- 53 files changed, 19339 deletions(-) delete mode 100644 src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs delete mode 100644 src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs delete mode 100644 src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs delete mode 100644 src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs delete mode 100644 src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs delete mode 100644 src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs delete mode 100644 src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade delete mode 100644 src/Ryujinx.Gtk3/Modules/Updater/Updater.cs delete mode 100644 src/Ryujinx.Gtk3/Program.cs delete mode 100644 src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj delete mode 100644 src/Ryujinx.Gtk3/Ryujinx.ico delete mode 100644 src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs delete mode 100644 src/Ryujinx.Gtk3/UI/MainWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/MainWindow.glade delete mode 100644 src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs delete mode 100644 src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs delete mode 100644 src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs delete mode 100644 src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs delete mode 100644 src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs delete mode 100644 src/Ryujinx.Gtk3/UI/VulkanRenderer.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs delete mode 100644 src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs deleted file mode 100644 index ff7a2c3b6..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs +++ /dev/null @@ -1,205 +0,0 @@ -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using System; -using System.Collections.Generic; -using System.Numerics; -using ConfigKey = Ryujinx.Common.Configuration.Hid.Key; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3Keyboard : IKeyboard - { - private class ButtonMappingEntry - { - public readonly GamepadButtonInputId To; - public readonly Key From; - - public ButtonMappingEntry(GamepadButtonInputId to, Key from) - { - To = to; - From = from; - } - } - - private readonly object _userMappingLock = new(); - - private readonly GTK3KeyboardDriver _driver; - private StandardKeyboardInputConfig _configuration; - private readonly List _buttonsUserMapping; - - public GTK3Keyboard(GTK3KeyboardDriver driver, string id, string name) - { - _driver = driver; - Id = id; - Name = name; - _buttonsUserMapping = new List(); - } - - private bool HasConfiguration => _configuration != null; - - public string Id { get; } - - public string Name { get; } - - public bool IsConnected => true; - - public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; - - public void Dispose() - { - // No operations - GC.SuppressFinalize(this); - } - - public KeyboardStateSnapshot GetKeyboardStateSnapshot() - { - return IKeyboard.GetStateSnapshot(this); - } - - private static float ConvertRawStickValue(short value) - { - const float ConvertRate = 1.0f / (short.MaxValue + 0.5f); - - return value * ConvertRate; - } - - private static (short, short) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick stickConfig) - { - short stickX = 0; - short stickY = 0; - - if (snapshot.IsPressed((Key)stickConfig.StickUp)) - { - stickY += 1; - } - - if (snapshot.IsPressed((Key)stickConfig.StickDown)) - { - stickY -= 1; - } - - if (snapshot.IsPressed((Key)stickConfig.StickRight)) - { - stickX += 1; - } - - if (snapshot.IsPressed((Key)stickConfig.StickLeft)) - { - stickX -= 1; - } - - OpenTK.Mathematics.Vector2 stick = new(stickX, stickY); - - stick.NormalizeFast(); - - return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue)); - } - - public GamepadStateSnapshot GetMappedStateSnapshot() - { - KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot(); - GamepadStateSnapshot result = default; - - lock (_userMappingLock) - { - if (!HasConfiguration) - { - return result; - } - - foreach (ButtonMappingEntry entry in _buttonsUserMapping) - { - if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound) - { - continue; - } - - // Do not touch state of button already pressed - if (!result.IsPressed(entry.To)) - { - result.SetPressed(entry.To, rawState.IsPressed(entry.From)); - } - } - - (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick); - (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick); - - result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY)); - result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY)); - } - - return result; - } - - public GamepadStateSnapshot GetStateSnapshot() - { - throw new NotSupportedException(); - } - - public (float, float) GetStick(StickInputId inputId) - { - throw new NotSupportedException(); - } - - public bool IsPressed(GamepadButtonInputId inputId) - { - throw new NotSupportedException(); - } - - public bool IsPressed(Key key) - { - return _driver.IsPressed(key); - } - - public void SetConfiguration(InputConfig configuration) - { - lock (_userMappingLock) - { - _configuration = (StandardKeyboardInputConfig)configuration; - - _buttonsUserMapping.Clear(); - - // Then left joycon - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); - - // Finally right joycon - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); - _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); - } - } - - public void SetTriggerThreshold(float triggerThreshold) - { - // No operations - } - - public void Rumble(float lowFrequency, float highFrequency, uint durationMs) - { - // No operations - } - - public Vector3 GetMotionData(MotionInputId inputId) - { - // No operations - - return Vector3.Zero; - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs deleted file mode 100644 index bd71c7933..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs +++ /dev/null @@ -1,99 +0,0 @@ -using Gdk; -using Gtk; -using System; -using System.Collections.Generic; -using GtkKey = Gdk.Key; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3KeyboardDriver : IGamepadDriver - { - private readonly Widget _widget; - private readonly HashSet _pressedKeys; - - public GTK3KeyboardDriver(Widget widget) - { - _widget = widget; - _pressedKeys = new HashSet(); - - _widget.KeyPressEvent += OnKeyPress; - _widget.KeyReleaseEvent += OnKeyRelease; - } - - public string DriverName => "GTK3"; - - private static readonly string[] _keyboardIdentifers = new string[1] { "0" }; - - public ReadOnlySpan GamepadsIds => _keyboardIdentifers; - - public event Action OnGamepadConnected - { - add { } - remove { } - } - - public event Action OnGamepadDisconnected - { - add { } - remove { } - } - - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - _widget.KeyPressEvent -= OnKeyPress; - _widget.KeyReleaseEvent -= OnKeyRelease; - } - } - - public void Dispose() - { - GC.SuppressFinalize(this); - Dispose(true); - } - - [GLib.ConnectBefore] - protected void OnKeyPress(object sender, KeyPressEventArgs args) - { - GtkKey key = (GtkKey)Keyval.ToLower((uint)args.Event.Key); - - _pressedKeys.Add(key); - } - - [GLib.ConnectBefore] - protected void OnKeyRelease(object sender, KeyReleaseEventArgs args) - { - GtkKey key = (GtkKey)Keyval.ToLower((uint)args.Event.Key); - - _pressedKeys.Remove(key); - } - - internal bool IsPressed(Key key) - { - if (key == Key.Unbound || key == Key.Unknown) - { - return false; - } - - GtkKey nativeKey = GTK3MappingHelper.ToGtkKey(key); - - return _pressedKeys.Contains(nativeKey); - } - - public void Clear() - { - _pressedKeys.Clear(); - } - - public IGamepad GetGamepad(string id) - { - if (!_keyboardIdentifers[0].Equals(id)) - { - return null; - } - - return new GTK3Keyboard(this, _keyboardIdentifers[0], "All keyboards"); - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs deleted file mode 100644 index 422a96030..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using GtkKey = Gdk.Key; - -namespace Ryujinx.Input.GTK3 -{ - public static class GTK3MappingHelper - { - private static readonly GtkKey[] _keyMapping = new GtkKey[(int)Key.Count] - { - // NOTE: invalid - GtkKey.blank, - - GtkKey.Shift_L, - GtkKey.Shift_R, - GtkKey.Control_L, - GtkKey.Control_R, - GtkKey.Alt_L, - GtkKey.Alt_R, - GtkKey.Super_L, - GtkKey.Super_R, - GtkKey.Menu, - GtkKey.F1, - GtkKey.F2, - GtkKey.F3, - GtkKey.F4, - GtkKey.F5, - GtkKey.F6, - GtkKey.F7, - GtkKey.F8, - GtkKey.F9, - GtkKey.F10, - GtkKey.F11, - GtkKey.F12, - GtkKey.F13, - GtkKey.F14, - GtkKey.F15, - GtkKey.F16, - GtkKey.F17, - GtkKey.F18, - GtkKey.F19, - GtkKey.F20, - GtkKey.F21, - GtkKey.F22, - GtkKey.F23, - GtkKey.F24, - GtkKey.F25, - GtkKey.F26, - GtkKey.F27, - GtkKey.F28, - GtkKey.F29, - GtkKey.F30, - GtkKey.F31, - GtkKey.F32, - GtkKey.F33, - GtkKey.F34, - GtkKey.F35, - GtkKey.Up, - GtkKey.Down, - GtkKey.Left, - GtkKey.Right, - GtkKey.Return, - GtkKey.Escape, - GtkKey.space, - GtkKey.Tab, - GtkKey.BackSpace, - GtkKey.Insert, - GtkKey.Delete, - GtkKey.Page_Up, - GtkKey.Page_Down, - GtkKey.Home, - GtkKey.End, - GtkKey.Caps_Lock, - GtkKey.Scroll_Lock, - GtkKey.Print, - GtkKey.Pause, - GtkKey.Num_Lock, - GtkKey.Clear, - GtkKey.KP_0, - GtkKey.KP_1, - GtkKey.KP_2, - GtkKey.KP_3, - GtkKey.KP_4, - GtkKey.KP_5, - GtkKey.KP_6, - GtkKey.KP_7, - GtkKey.KP_8, - GtkKey.KP_9, - GtkKey.KP_Divide, - GtkKey.KP_Multiply, - GtkKey.KP_Subtract, - GtkKey.KP_Add, - GtkKey.KP_Decimal, - GtkKey.KP_Enter, - GtkKey.a, - GtkKey.b, - GtkKey.c, - GtkKey.d, - GtkKey.e, - GtkKey.f, - GtkKey.g, - GtkKey.h, - GtkKey.i, - GtkKey.j, - GtkKey.k, - GtkKey.l, - GtkKey.m, - GtkKey.n, - GtkKey.o, - GtkKey.p, - GtkKey.q, - GtkKey.r, - GtkKey.s, - GtkKey.t, - GtkKey.u, - GtkKey.v, - GtkKey.w, - GtkKey.x, - GtkKey.y, - GtkKey.z, - GtkKey.Key_0, - GtkKey.Key_1, - GtkKey.Key_2, - GtkKey.Key_3, - GtkKey.Key_4, - GtkKey.Key_5, - GtkKey.Key_6, - GtkKey.Key_7, - GtkKey.Key_8, - GtkKey.Key_9, - GtkKey.grave, - GtkKey.grave, - GtkKey.minus, - GtkKey.plus, - GtkKey.bracketleft, - GtkKey.bracketright, - GtkKey.semicolon, - GtkKey.quoteright, - GtkKey.comma, - GtkKey.period, - GtkKey.slash, - GtkKey.backslash, - - // NOTE: invalid - GtkKey.blank, - }; - - private static readonly Dictionary _gtkKeyMapping; - - static GTK3MappingHelper() - { - var inputKeys = Enum.GetValues().SkipLast(1); - - // GtkKey is not contiguous and quite large, so use a dictionary instead of an array. - _gtkKeyMapping = new Dictionary(); - - foreach (var key in inputKeys) - { - var index = ToGtkKey(key); - _gtkKeyMapping[index] = key; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static GtkKey ToGtkKey(Key key) - { - return _keyMapping[(int)key]; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Key ToInputKey(GtkKey key) - { - return _gtkKeyMapping.GetValueOrDefault(key, Key.Unknown); - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs deleted file mode 100644 index 0ab817ecb..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Ryujinx.Common.Configuration.Hid; -using System; -using System.Drawing; -using System.Numerics; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3Mouse : IMouse - { - private GTK3MouseDriver _driver; - - public GamepadFeaturesFlag Features => throw new NotImplementedException(); - - public string Id => "0"; - - public string Name => "GTKMouse"; - - public bool IsConnected => true; - - public bool[] Buttons => _driver.PressedButtons; - - public GTK3Mouse(GTK3MouseDriver driver) - { - _driver = driver; - } - - public Size ClientSize => _driver.GetClientSize(); - - public Vector2 GetPosition() - { - return _driver.CurrentPosition; - } - - public Vector2 GetScroll() - { - return _driver.Scroll; - } - - public GamepadStateSnapshot GetMappedStateSnapshot() - { - throw new NotImplementedException(); - } - - public Vector3 GetMotionData(MotionInputId inputId) - { - throw new NotImplementedException(); - } - - public GamepadStateSnapshot GetStateSnapshot() - { - throw new NotImplementedException(); - } - - public (float, float) GetStick(StickInputId inputId) - { - throw new NotImplementedException(); - } - - public bool IsButtonPressed(MouseButton button) - { - return _driver.IsButtonPressed(button); - } - - public bool IsPressed(GamepadButtonInputId inputId) - { - throw new NotImplementedException(); - } - - public void Rumble(float lowFrequency, float highFrequency, uint durationMs) - { - throw new NotImplementedException(); - } - - public void SetConfiguration(InputConfig configuration) - { - throw new NotImplementedException(); - } - - public void SetTriggerThreshold(float triggerThreshold) - { - throw new NotImplementedException(); - } - - public void Dispose() - { - GC.SuppressFinalize(this); - _driver = null; - } - } -} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs deleted file mode 100644 index 5962bcb25..000000000 --- a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Gdk; -using Gtk; -using System; -using System.Numerics; -using Size = System.Drawing.Size; - -namespace Ryujinx.Input.GTK3 -{ - public class GTK3MouseDriver : IGamepadDriver - { - private Widget _widget; - private bool _isDisposed; - - public bool[] PressedButtons { get; } - - public Vector2 CurrentPosition { get; private set; } - public Vector2 Scroll { get; private set; } - - public GTK3MouseDriver(Widget parent) - { - _widget = parent; - - _widget.MotionNotifyEvent += Parent_MotionNotifyEvent; - _widget.ButtonPressEvent += Parent_ButtonPressEvent; - _widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent; - _widget.ScrollEvent += Parent_ScrollEvent; - - PressedButtons = new bool[(int)MouseButton.Count]; - } - - - [GLib.ConnectBefore] - private void Parent_ScrollEvent(object o, ScrollEventArgs args) - { - Scroll = new Vector2((float)args.Event.X, (float)args.Event.Y); - } - - [GLib.ConnectBefore] - private void Parent_ButtonReleaseEvent(object o, ButtonReleaseEventArgs args) - { - PressedButtons[args.Event.Button - 1] = false; - } - - [GLib.ConnectBefore] - private void Parent_ButtonPressEvent(object o, ButtonPressEventArgs args) - { - PressedButtons[args.Event.Button - 1] = true; - } - - [GLib.ConnectBefore] - private void Parent_MotionNotifyEvent(object o, MotionNotifyEventArgs args) - { - if (args.Event.Device.InputSource == InputSource.Mouse) - { - CurrentPosition = new Vector2((float)args.Event.X, (float)args.Event.Y); - } - } - - public bool IsButtonPressed(MouseButton button) - { - return PressedButtons[(int)button]; - } - - public Size GetClientSize() - { - return new Size(_widget.AllocatedWidth, _widget.AllocatedHeight); - } - - public string DriverName => "GTK3"; - - public event Action OnGamepadConnected - { - add { } - remove { } - } - - public event Action OnGamepadDisconnected - { - add { } - remove { } - } - - public ReadOnlySpan GamepadsIds => new[] { "0" }; - - public IGamepad GetGamepad(string id) - { - return new GTK3Mouse(this); - } - - public void Dispose() - { - if (_isDisposed) - { - return; - } - - GC.SuppressFinalize(this); - - _isDisposed = true; - - _widget.MotionNotifyEvent -= Parent_MotionNotifyEvent; - _widget.ButtonPressEvent -= Parent_ButtonPressEvent; - _widget.ButtonReleaseEvent -= Parent_ButtonReleaseEvent; - - _widget = null; - } - } -} diff --git a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs b/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs deleted file mode 100644 index 43bde9420..000000000 --- a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.cs +++ /dev/null @@ -1,95 +0,0 @@ -using Gdk; -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.UI; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using System; -using System.Diagnostics; -using System.Reflection; - -namespace Ryujinx.Modules -{ - public class UpdateDialog : Gtk.Window - { -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [Builder.Object] public Label MainText; - [Builder.Object] public Label SecondaryText; - [Builder.Object] public LevelBar ProgressBar; - [Builder.Object] public Button YesButton; - [Builder.Object] public Button NoButton; -#pragma warning restore CS0649, IDE0044 - - private readonly MainWindow _mainWindow; - private readonly string _buildUrl; - private bool _restartQuery; - - public UpdateDialog(MainWindow mainWindow, Version newVersion, string buildUrl) : this(new Builder("Ryujinx.Gtk3.Modules.Updater.UpdateDialog.glade"), mainWindow, newVersion, buildUrl) { } - - private UpdateDialog(Builder builder, MainWindow mainWindow, Version newVersion, string buildUrl) : base(builder.GetRawOwnedObject("UpdateDialog")) - { - builder.Autoconnect(this); - - _mainWindow = mainWindow; - _buildUrl = buildUrl; - - Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"); - MainText.Text = "Do you want to update Ryujinx to the latest version?"; - SecondaryText.Text = $"{Program.Version} -> {newVersion}"; - - ProgressBar.Hide(); - - YesButton.Clicked += YesButton_Clicked; - NoButton.Clicked += NoButton_Clicked; - } - - private void YesButton_Clicked(object sender, EventArgs args) - { - if (_restartQuery) - { - string ryuName = OperatingSystem.IsWindows() ? "Ryujinx.exe" : "Ryujinx"; - - ProcessStartInfo processStart = new(ryuName) - { - UseShellExecute = true, - WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory - }; - - foreach (string argument in CommandLineState.Arguments) - { - processStart.ArgumentList.Add(argument); - } - - Process.Start(processStart); - - Environment.Exit(0); - } - else - { - Window.Functions = _mainWindow.Window.Functions = WMFunction.All & WMFunction.Close; - _mainWindow.ExitMenuItem.Sensitive = false; - - YesButton.Hide(); - NoButton.Hide(); - ProgressBar.Show(); - - SecondaryText.Text = ""; - _restartQuery = true; - - Updater.UpdateRyujinx(this, _buildUrl); - } - } - - private void NoButton_Clicked(object sender, EventArgs args) - { - Updater.Running = false; - _mainWindow.Window.Functions = WMFunction.All; - - _mainWindow.ExitMenuItem.Sensitive = true; - _mainWindow.UpdateMenuItem.Sensitive = true; - - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade b/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade deleted file mode 100644 index cc80167e0..000000000 --- a/src/Ryujinx.Gtk3/Modules/Updater/UpdateDialog.glade +++ /dev/null @@ -1,127 +0,0 @@ - - - - - - False - Ryujinx - Updater - False - center - 400 - 130 - - - - - - True - False - 10 - 10 - 10 - 10 - vertical - - - True - False - vertical - - - True - False - 5 - 5 - - - - - - - False - True - 0 - - - - - True - False - 5 - 5 - - - False - True - 1 - - - - - 20 - True - False - 5 - 5 - 100 - - - False - True - 2 - - - - - True - True - 0 - - - - - True - False - - - Yes - True - True - True - 5 - 5 - 5 - - - True - True - 0 - - - - - No - True - True - True - 5 - 5 - 5 - - - True - True - 1 - - - - - False - True - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/Modules/Updater/Updater.cs b/src/Ryujinx.Gtk3/Modules/Updater/Updater.cs deleted file mode 100644 index 8b006f63f..000000000 --- a/src/Ryujinx.Gtk3/Modules/Updater/Updater.cs +++ /dev/null @@ -1,622 +0,0 @@ -using Gtk; -using ICSharpCode.SharpZipLib.GZip; -using ICSharpCode.SharpZipLib.Tar; -using ICSharpCode.SharpZipLib.Zip; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.UI; -using Ryujinx.UI.Common.Models.Github; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.NetworkInformation; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Ryujinx.Modules -{ - public static class Updater - { - private const string GitHubApiUrl = "https://api.github.com"; - private const int ConnectionCount = 4; - - internal static bool Running; - - private static readonly string _homeDir = AppDomain.CurrentDomain.BaseDirectory; - private static readonly string _updateDir = Path.Combine(Path.GetTempPath(), "Ryujinx", "update"); - private static readonly string _updatePublishDir = Path.Combine(_updateDir, "publish"); - - private static string _buildVer; - private static string _platformExt; - private static string _buildUrl; - private static long _buildSize; - - private static readonly GithubReleasesJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - // On Windows, GtkSharp.Dependencies adds these extra dirs that must be cleaned during updates. - private static readonly string[] _windowsDependencyDirs = { "bin", "etc", "lib", "share" }; - - private static HttpClient ConstructHttpClient() - { - HttpClient result = new(); - - // Required by GitHub to interact with APIs. - result.DefaultRequestHeaders.Add("User-Agent", "Ryujinx-Updater/1.0.0"); - - return result; - } - - public static async Task BeginParse(MainWindow mainWindow, bool showVersionUpToDate) - { - if (Running) - { - return; - } - - Running = true; - mainWindow.UpdateMenuItem.Sensitive = false; - - int artifactIndex = -1; - - // Detect current platform - if (OperatingSystem.IsMacOS()) - { - _platformExt = "osx_x64.zip"; - artifactIndex = 1; - } - else if (OperatingSystem.IsWindows()) - { - _platformExt = "win_x64.zip"; - artifactIndex = 2; - } - else if (OperatingSystem.IsLinux()) - { - var arch = RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64"; - _platformExt = $"linux_{arch}.tar.gz"; - artifactIndex = 0; - } - - if (artifactIndex == -1) - { - GtkDialog.CreateErrorDialog("Your platform is not supported!"); - - return; - } - - Version newVersion; - Version currentVersion; - - try - { - currentVersion = Version.Parse(Program.Version); - } - catch - { - GtkDialog.CreateWarningDialog("Failed to convert the current Ryujinx version.", "Cancelling Update!"); - Logger.Error?.Print(LogClass.Application, "Failed to convert the current Ryujinx version!"); - - return; - } - - // Get latest version number from GitHub API - try - { - using HttpClient jsonClient = ConstructHttpClient(); - string buildInfoUrl = $"{GitHubApiUrl}/repos/{ReleaseInformation.ReleaseChannelOwner}/{ReleaseInformation.ReleaseChannelRepo}/releases/latest"; - - // Fetch latest build information - string fetchedJson = await jsonClient.GetStringAsync(buildInfoUrl); - var fetched = JsonHelper.Deserialize(fetchedJson, _serializerContext.GithubReleasesJsonResponse); - _buildVer = fetched.Name; - - foreach (var asset in fetched.Assets) - { - if (asset.Name.StartsWith("gtk-ryujinx") && asset.Name.EndsWith(_platformExt)) - { - _buildUrl = asset.BrowserDownloadUrl; - - if (asset.State != "uploaded") - { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; - } - - break; - } - } - - if (_buildUrl == null) - { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - return; - } - } - catch (Exception exception) - { - Logger.Error?.Print(LogClass.Application, exception.Message); - GtkDialog.CreateErrorDialog("An error occurred when trying to get release information from GitHub Release. This can be caused if a new release is being compiled by GitHub Actions. Try again in a few minutes."); - - return; - } - - try - { - newVersion = Version.Parse(_buildVer); - } - catch - { - GtkDialog.CreateWarningDialog("Failed to convert the received Ryujinx version from GitHub Release.", "Cancelling Update!"); - Logger.Error?.Print(LogClass.Application, "Failed to convert the received Ryujinx version from GitHub Release!"); - - return; - } - - if (newVersion <= currentVersion) - { - if (showVersionUpToDate) - { - GtkDialog.CreateUpdaterInfoDialog("You are already using the latest version of Ryujinx!", ""); - } - - Running = false; - mainWindow.UpdateMenuItem.Sensitive = true; - - return; - } - - // Fetch build size information to learn chunk sizes. - using HttpClient buildSizeClient = ConstructHttpClient(); - try - { - buildSizeClient.DefaultRequestHeaders.Add("Range", "bytes=0-0"); - - HttpResponseMessage message = await buildSizeClient.GetAsync(new Uri(_buildUrl), HttpCompletionOption.ResponseHeadersRead); - - _buildSize = message.Content.Headers.ContentRange.Length.Value; - } - catch (Exception ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Couldn't determine build size for update, using single-threaded updater"); - - _buildSize = -1; - } - - // Show a message asking the user if they want to update - UpdateDialog updateDialog = new(mainWindow, newVersion, _buildUrl); - updateDialog.Show(); - } - - public static void UpdateRyujinx(UpdateDialog updateDialog, string downloadUrl) - { - // Empty update dir, although it shouldn't ever have anything inside it - if (Directory.Exists(_updateDir)) - { - Directory.Delete(_updateDir, true); - } - - Directory.CreateDirectory(_updateDir); - - string updateFile = Path.Combine(_updateDir, "update.bin"); - - // Download the update .zip - updateDialog.MainText.Text = "Downloading Update..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = 100; - - if (_buildSize >= 0) - { - DoUpdateWithMultipleThreads(updateDialog, downloadUrl, updateFile); - } - else - { - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - } - } - - private static void DoUpdateWithMultipleThreads(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - // Multi-Threaded Updater - long chunkSize = _buildSize / ConnectionCount; - long remainderChunk = _buildSize % ConnectionCount; - - int completedRequests = 0; - int totalProgressPercentage = 0; - int[] progressPercentage = new int[ConnectionCount]; - - List list = new(ConnectionCount); - List webClients = new(ConnectionCount); - - for (int i = 0; i < ConnectionCount; i++) - { - list.Add(Array.Empty()); - } - - for (int i = 0; i < ConnectionCount; i++) - { -#pragma warning disable SYSLIB0014 - // TODO: WebClient is obsolete and need to be replaced with a more complex logic using HttpClient. - using WebClient client = new(); -#pragma warning restore SYSLIB0014 - webClients.Add(client); - - if (i == ConnectionCount - 1) - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{(chunkSize * (i + 1) - 1) + remainderChunk}"); - } - else - { - client.Headers.Add("Range", $"bytes={chunkSize * i}-{chunkSize * (i + 1) - 1}"); - } - - client.DownloadProgressChanged += (_, args) => - { - int index = (int)args.UserState; - - Interlocked.Add(ref totalProgressPercentage, -1 * progressPercentage[index]); - Interlocked.Exchange(ref progressPercentage[index], args.ProgressPercentage); - Interlocked.Add(ref totalProgressPercentage, args.ProgressPercentage); - - updateDialog.ProgressBar.Value = totalProgressPercentage / ConnectionCount; - }; - - client.DownloadDataCompleted += (_, args) => - { - int index = (int)args.UserState; - - if (args.Cancelled) - { - webClients[index].Dispose(); - - return; - } - - list[index] = args.Result; - Interlocked.Increment(ref completedRequests); - - if (Equals(completedRequests, ConnectionCount)) - { - byte[] mergedFileBytes = new byte[_buildSize]; - for (int connectionIndex = 0, destinationOffset = 0; connectionIndex < ConnectionCount; connectionIndex++) - { - Array.Copy(list[connectionIndex], 0, mergedFileBytes, destinationOffset, list[connectionIndex].Length); - destinationOffset += list[connectionIndex].Length; - } - - File.WriteAllBytes(updateFile, mergedFileBytes); - - try - { - InstallUpdate(updateDialog, updateFile); - } - catch (Exception e) - { - Logger.Warning?.Print(LogClass.Application, e.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - - return; - } - } - }; - - try - { - client.DownloadDataAsync(new Uri(downloadUrl), i); - } - catch (WebException ex) - { - Logger.Warning?.Print(LogClass.Application, ex.Message); - Logger.Warning?.Print(LogClass.Application, "Multi-Threaded update failed, falling back to single-threaded updater."); - - foreach (WebClient webClient in webClients) - { - webClient.CancelAsync(); - } - - DoUpdateWithSingleThread(updateDialog, downloadUrl, updateFile); - - return; - } - } - } - - private static void DoUpdateWithSingleThreadWorker(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - using HttpClient client = new(); - // We do not want to timeout while downloading - client.Timeout = TimeSpan.FromDays(1); - - using HttpResponseMessage response = client.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead).Result; - using Stream remoteFileStream = response.Content.ReadAsStreamAsync().Result; - using Stream updateFileStream = File.Open(updateFile, FileMode.Create); - - long totalBytes = response.Content.Headers.ContentLength.Value; - long byteWritten = 0; - - byte[] buffer = new byte[32 * 1024]; - - while (true) - { - int readSize = remoteFileStream.Read(buffer); - - if (readSize == 0) - { - break; - } - - byteWritten += readSize; - - updateDialog.ProgressBar.Value = ((double)byteWritten / totalBytes) * 100; - updateFileStream.Write(buffer, 0, readSize); - } - - InstallUpdate(updateDialog, updateFile); - } - - private static void DoUpdateWithSingleThread(UpdateDialog updateDialog, string downloadUrl, string updateFile) - { - Thread worker = new(() => DoUpdateWithSingleThreadWorker(updateDialog, downloadUrl, updateFile)) - { - Name = "Updater.SingleThreadWorker", - }; - worker.Start(); - } - - private static async void InstallUpdate(UpdateDialog updateDialog, string updateFile) - { - // Extract Update - updateDialog.MainText.Text = "Extracting Update..."; - updateDialog.ProgressBar.Value = 0; - - if (OperatingSystem.IsLinux()) - { - using Stream inStream = File.OpenRead(updateFile); - using Stream gzipStream = new GZipInputStream(inStream); - using TarInputStream tarStream = new(gzipStream, Encoding.ASCII); - updateDialog.ProgressBar.MaxValue = inStream.Length; - - await Task.Run(() => - { - TarEntry tarEntry; - - if (!OperatingSystem.IsWindows()) - { - while ((tarEntry = tarStream.GetNextEntry()) != null) - { - if (tarEntry.IsDirectory) - { - continue; - } - - string outPath = Path.Combine(_updateDir, tarEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using FileStream outStream = File.OpenWrite(outPath); - tarStream.CopyEntryContents(outStream); - - File.SetUnixFileMode(outPath, (UnixFileMode)tarEntry.TarHeader.Mode); - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(tarEntry.ModTime, DateTimeKind.Utc)); - - TarEntry entry = tarEntry; - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value += entry.Size; - }); - } - } - }); - - updateDialog.ProgressBar.Value = inStream.Length; - } - else - { - using Stream inStream = File.OpenRead(updateFile); - using ZipFile zipFile = new(inStream); - updateDialog.ProgressBar.MaxValue = zipFile.Count; - - await Task.Run(() => - { - foreach (ZipEntry zipEntry in zipFile) - { - if (zipEntry.IsDirectory) - { - continue; - } - - string outPath = Path.Combine(_updateDir, zipEntry.Name); - - Directory.CreateDirectory(Path.GetDirectoryName(outPath)); - - using Stream zipStream = zipFile.GetInputStream(zipEntry); - using FileStream outStream = File.OpenWrite(outPath); - zipStream.CopyTo(outStream); - - File.SetLastWriteTime(outPath, DateTime.SpecifyKind(zipEntry.DateTime, DateTimeKind.Utc)); - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value++; - }); - } - }); - } - - // Delete downloaded zip - File.Delete(updateFile); - - List allFiles = EnumerateFilesToDelete().ToList(); - - updateDialog.MainText.Text = "Renaming Old Files..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = allFiles.Count; - - // Replace old files - await Task.Run(() => - { - foreach (string file in allFiles) - { - try - { - File.Move(file, file + ".ryuold"); - - Application.Invoke(delegate - { - updateDialog.ProgressBar.Value++; - }); - } - catch - { - Logger.Warning?.Print(LogClass.Application, "Updater was unable to rename file: " + file); - } - } - - Application.Invoke(delegate - { - updateDialog.MainText.Text = "Adding New Files..."; - updateDialog.ProgressBar.Value = 0; - updateDialog.ProgressBar.MaxValue = Directory.GetFiles(_updatePublishDir, "*", SearchOption.AllDirectories).Length; - }); - - MoveAllFilesOver(_updatePublishDir, _homeDir, updateDialog); - }); - - Directory.Delete(_updateDir, true); - - updateDialog.MainText.Text = "Update Complete!"; - updateDialog.SecondaryText.Text = "Do you want to restart Ryujinx now?"; - updateDialog.Modal = true; - - updateDialog.ProgressBar.Hide(); - updateDialog.YesButton.Show(); - updateDialog.NoButton.Show(); - } - - public static bool CanUpdate(bool showWarnings) - { -#if !DISABLE_UPDATER - if (!NetworkInterface.GetIsNetworkAvailable()) - { - if (showWarnings) - { - GtkDialog.CreateWarningDialog("You are not connected to the Internet!", "Please verify that you have a working Internet connection!"); - } - - return false; - } - - if (Program.Version.Contains("dirty") || !ReleaseInformation.IsValid) - { - if (showWarnings) - { - GtkDialog.CreateWarningDialog("You cannot update a Dirty build of Ryujinx!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version."); - } - - return false; - } - - return true; -#else - if (showWarnings) - { - if (ReleaseInformation.IsFlatHubBuild) - { - GtkDialog.CreateWarningDialog("Updater Disabled!", "Please update Ryujinx via FlatHub."); - } - else - { - GtkDialog.CreateWarningDialog("Updater Disabled!", "Please download Ryujinx at https://ryujinx.org/ if you are looking for a supported version."); - } - } - - return false; -#endif - } - - // NOTE: This method should always reflect the latest build layout. - private static IEnumerable EnumerateFilesToDelete() - { - var files = Directory.EnumerateFiles(_homeDir); // All files directly in base dir. - - // Determine and exclude user files only when the updater is running, not when cleaning old files - if (Running) - { - // Compare the loose files in base directory against the loose files from the incoming update, and store foreign ones in a user list. - var oldFiles = Directory.EnumerateFiles(_homeDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var newFiles = Directory.EnumerateFiles(_updatePublishDir, "*", SearchOption.TopDirectoryOnly).Select(Path.GetFileName); - var userFiles = oldFiles.Except(newFiles).Select(filename => Path.Combine(_homeDir, filename)); - - // Remove user files from the paths in files. - files = files.Except(userFiles); - } - - if (OperatingSystem.IsWindows()) - { - foreach (string dir in _windowsDependencyDirs) - { - string dirPath = Path.Combine(_homeDir, dir); - if (Directory.Exists(dirPath)) - { - files = files.Concat(Directory.EnumerateFiles(dirPath, "*", SearchOption.AllDirectories)); - } - } - } - - return files.Where(f => !new FileInfo(f).Attributes.HasFlag(FileAttributes.Hidden | FileAttributes.System)); - } - - private static void MoveAllFilesOver(string root, string dest, UpdateDialog dialog) - { - foreach (string directory in Directory.GetDirectories(root)) - { - string dirName = Path.GetFileName(directory); - - if (!Directory.Exists(Path.Combine(dest, dirName))) - { - Directory.CreateDirectory(Path.Combine(dest, dirName)); - } - - MoveAllFilesOver(directory, Path.Combine(dest, dirName), dialog); - } - - foreach (string file in Directory.GetFiles(root)) - { - File.Move(file, Path.Combine(dest, Path.GetFileName(file)), true); - - Application.Invoke(delegate - { - dialog.ProgressBar.Value++; - }); - } - } - - public static void CleanupUpdate() - { - foreach (string file in EnumerateFilesToDelete()) - { - if (Path.GetExtension(file).EndsWith(".ryuold")) - { - File.Delete(file); - } - } - } - } -} diff --git a/src/Ryujinx.Gtk3/Program.cs b/src/Ryujinx.Gtk3/Program.cs deleted file mode 100644 index 2d350374b..000000000 --- a/src/Ryujinx.Gtk3/Program.cs +++ /dev/null @@ -1,403 +0,0 @@ -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.GraphicsDriver; -using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.Vulkan.MoltenVK; -using Ryujinx.Modules; -using Ryujinx.SDL2.Common; -using Ryujinx.UI; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Common.SystemInfo; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading.Tasks; - -namespace Ryujinx -{ - partial class Program - { - public static double WindowScaleFactor { get; private set; } - - public static string Version { get; private set; } - - public static string ConfigurationPath { get; set; } - - public static string CommandLineProfile { get; set; } - - private const string X11LibraryName = "libX11"; - - [LibraryImport(X11LibraryName)] - private static partial int XInitThreads(); - - [LibraryImport("user32.dll", SetLastError = true)] - public static partial int MessageBoxA(IntPtr hWnd, [MarshalAs(UnmanagedType.LPStr)] string text, [MarshalAs(UnmanagedType.LPStr)] string caption, uint type); - - private const uint MbIconWarning = 0x30; - - static Program() - { - if (OperatingSystem.IsLinux()) - { - NativeLibrary.SetDllImportResolver(typeof(Program).Assembly, (name, assembly, path) => - { - if (name != X11LibraryName) - { - return IntPtr.Zero; - } - - if (!NativeLibrary.TryLoad("libX11.so.6", assembly, path, out IntPtr result)) - { - if (!NativeLibrary.TryLoad("libX11.so", assembly, path, out result)) - { - return IntPtr.Zero; - } - } - - return result; - }); - } - } - - static void Main(string[] args) - { - Version = ReleaseInformation.Version; - - if (OperatingSystem.IsWindows() && !OperatingSystem.IsWindowsVersionAtLeast(10, 0, 17134)) - { - MessageBoxA(IntPtr.Zero, "You are running an outdated version of Windows.\n\nRyujinx supports Windows 10 version 1803 and newer.\n", $"Ryujinx {Version}", MbIconWarning); - } - - // Parse arguments - CommandLineState.ParseArguments(args); - - // Hook unhandled exception and process exit events. - GLib.ExceptionManager.UnhandledException += (GLib.UnhandledExceptionArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.UnhandledException += (object sender, UnhandledExceptionEventArgs e) => ProcessUnhandledException(e.ExceptionObject as Exception, e.IsTerminating); - AppDomain.CurrentDomain.ProcessExit += (object sender, EventArgs e) => Exit(); - - // Make process DPI aware for proper window sizing on high-res screens. - ForceDpiAware.Windows(); - WindowScaleFactor = ForceDpiAware.GetWindowScaleFactor(); - - // Delete backup files after updating. - Task.Run(Updater.CleanupUpdate); - - Console.Title = $"Ryujinx Console {Version}"; - - // NOTE: GTK3 doesn't init X11 in a multi threaded way. - // This ends up causing race condition and abort of XCB when a context is created by SPB (even if SPB do call XInitThreads). - if (OperatingSystem.IsLinux()) - { - if (XInitThreads() == 0) - { - throw new NotSupportedException("Failed to initialize multi-threading support."); - } - - OsUtils.SetEnvironmentVariableNoCaching("GDK_BACKEND", "x11"); - } - - if (OperatingSystem.IsMacOS()) - { - MVKInitialization.InitializeResolver(); - - string baseDirectory = Path.GetDirectoryName(AppDomain.CurrentDomain.BaseDirectory); - string resourcesDataDir; - - if (Path.GetFileName(baseDirectory) == "MacOS") - { - resourcesDataDir = Path.Combine(Directory.GetParent(baseDirectory).FullName, "Resources"); - } - else - { - resourcesDataDir = baseDirectory; - } - - // On macOS, GTK3 needs XDG_DATA_DIRS to be set, otherwise it will try searching for "gschemas.compiled" in system directories. - OsUtils.SetEnvironmentVariableNoCaching("XDG_DATA_DIRS", Path.Combine(resourcesDataDir, "share")); - - // On macOS, GTK3 needs GDK_PIXBUF_MODULE_FILE to be set, otherwise it will try searching for "loaders.cache" in system directories. - OsUtils.SetEnvironmentVariableNoCaching("GDK_PIXBUF_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gdk-pixbuf-2.0", "2.10.0", "loaders.cache")); - - OsUtils.SetEnvironmentVariableNoCaching("GTK_IM_MODULE_FILE", Path.Combine(resourcesDataDir, "lib", "gtk-3.0", "3.0.0", "immodules.cache")); - } - - string systemPath = Environment.GetEnvironmentVariable("Path", EnvironmentVariableTarget.Machine); - Environment.SetEnvironmentVariable("Path", $"{Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "bin")};{systemPath}"); - - // Setup base data directory. - AppDataManager.Initialize(CommandLineState.BaseDirPathArg); - - // Initialize the configuration. - ConfigurationState.Initialize(); - - // Initialize the logger system. - LoggerModule.Initialize(); - - // Initialize Discord integration. - DiscordIntegrationModule.Initialize(); - - // Initialize SDL2 driver - SDL2Driver.MainThreadDispatcher = action => - { - Application.Invoke(delegate - { - action(); - }); - }; - - string localConfigurationPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, ReleaseInformation.ConfigName); - string appDataConfigurationPath = Path.Combine(AppDataManager.BaseDirPath, ReleaseInformation.ConfigName); - - // Now load the configuration as the other subsystems are now registered - ConfigurationPath = File.Exists(localConfigurationPath) - ? localConfigurationPath - : File.Exists(appDataConfigurationPath) - ? appDataConfigurationPath - : null; - - if (ConfigurationPath == null) - { - // No configuration, we load the default values and save it to disk - ConfigurationPath = appDataConfigurationPath; - Logger.Notice.Print(LogClass.Application, $"No configuration file found. Saving default configuration to: {ConfigurationPath}"); - - ConfigurationState.Instance.LoadDefault(); - ConfigurationState.Instance.ToFileFormat().SaveConfig(ConfigurationPath); - } - else - { - Logger.Notice.Print(LogClass.Application, $"Loading configuration from: {ConfigurationPath}"); - - if (ConfigurationFileFormat.TryLoad(ConfigurationPath, out ConfigurationFileFormat configurationFileFormat)) - { - ConfigurationState.Instance.Load(configurationFileFormat, ConfigurationPath); - } - else - { - Logger.Warning?.PrintMsg(LogClass.Application, $"Failed to load config! Loading the default config instead.\nFailed config location: {ConfigurationPath}"); - - ConfigurationState.Instance.LoadDefault(); - } - } - - // Check if graphics backend was overridden. - if (CommandLineState.OverrideGraphicsBackend != null) - { - if (CommandLineState.OverrideGraphicsBackend.ToLower() == "opengl") - { - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.OpenGl; - } - else if (CommandLineState.OverrideGraphicsBackend.ToLower() == "vulkan") - { - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = GraphicsBackend.Vulkan; - } - } - - // Check if HideCursor was overridden. - if (CommandLineState.OverrideHideCursor is not null) - { - ConfigurationState.Instance.HideCursor.Value = CommandLineState.OverrideHideCursor!.ToLower() switch - { - "never" => HideCursorMode.Never, - "onidle" => HideCursorMode.OnIdle, - "always" => HideCursorMode.Always, - _ => ConfigurationState.Instance.HideCursor.Value, - }; - } - - // Check if docked mode was overridden. - if (CommandLineState.OverrideDockedMode.HasValue) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = CommandLineState.OverrideDockedMode.Value; - } - - // Logging system information. - PrintSystemInfo(); - - // Enable OGL multithreading on the driver, and some other flags. - BackendThreading threadingMode = ConfigurationState.Instance.Graphics.BackendThreading; - DriverUtilities.InitDriverConfig(threadingMode == BackendThreading.Off); - - // Initialize Gtk. - Application.Init(); - - // Check if keys exists. - bool hasSystemProdKeys = File.Exists(Path.Combine(AppDataManager.KeysDirPath, "prod.keys")); - bool hasCommonProdKeys = AppDataManager.Mode == AppDataManager.LaunchMode.UserProfile && File.Exists(Path.Combine(AppDataManager.KeysDirPathUser, "prod.keys")); - if (!hasSystemProdKeys && !hasCommonProdKeys) - { - UserErrorDialog.CreateUserErrorDialog(UserError.NoKeys); - } - - // Show the main window UI. - MainWindow mainWindow = new(); - mainWindow.Show(); - - // Load the game table if no application was requested by the command line - if (CommandLineState.LaunchPathArg == null) - { - mainWindow.UpdateGameTable(); - } - - if (OperatingSystem.IsLinux()) - { - int currentVmMaxMapCount = LinuxHelper.VmMaxMapCount; - - if (LinuxHelper.VmMaxMapCount < LinuxHelper.RecommendedVmMaxMapCount) - { - Logger.Warning?.Print(LogClass.Application, $"The value of vm.max_map_count is lower than {LinuxHelper.RecommendedVmMaxMapCount}. ({currentVmMaxMapCount})"); - - if (LinuxHelper.PkExecPath is not null) - { - var buttonTexts = new Dictionary() - { - { 0, "Yes, until the next restart" }, - { 1, "Yes, permanently" }, - { 2, "No" }, - }; - - ResponseType response = GtkDialog.CreateCustomDialog( - "Ryujinx - Low limit for memory mappings detected", - $"Would you like to increase the value of vm.max_map_count to {LinuxHelper.RecommendedVmMaxMapCount}?", - "Some games might try to create more memory mappings than currently allowed. " + - "Ryujinx will crash as soon as this limit gets exceeded.", - buttonTexts, - MessageType.Question); - - int rc; - - switch ((int)response) - { - case 0: - rc = LinuxHelper.RunPkExec($"echo {LinuxHelper.RecommendedVmMaxMapCount} > {LinuxHelper.VmMaxMapCountPath}"); - if (rc == 0) - { - Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount} until the next restart."); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Unable to change vm.max_map_count. Process exited with code: {rc}"); - } - break; - case 1: - rc = LinuxHelper.RunPkExec($"echo \"vm.max_map_count = {LinuxHelper.RecommendedVmMaxMapCount}\" > {LinuxHelper.SysCtlConfigPath} && sysctl -p {LinuxHelper.SysCtlConfigPath}"); - if (rc == 0) - { - Logger.Info?.Print(LogClass.Application, $"vm.max_map_count set to {LinuxHelper.VmMaxMapCount}. Written to config: {LinuxHelper.SysCtlConfigPath}"); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Unable to write new value for vm.max_map_count to config. Process exited with code: {rc}"); - } - break; - } - } - else - { - GtkDialog.CreateWarningDialog( - "Max amount of memory mappings is lower than recommended.", - $"The current value of vm.max_map_count ({currentVmMaxMapCount}) is lower than {LinuxHelper.RecommendedVmMaxMapCount}." + - "Some games might try to create more memory mappings than currently allowed. " + - "Ryujinx will crash as soon as this limit gets exceeded.\n\n" + - "You might want to either manually increase the limit or install pkexec, which allows Ryujinx to assist with that."); - } - } - } - - if (CommandLineState.LaunchPathArg != null) - { - if (mainWindow.ApplicationLibrary.TryGetApplicationsFromFile(CommandLineState.LaunchPathArg, out List applications)) - { - ApplicationData applicationData; - - if (CommandLineState.LaunchApplicationId != null) - { - applicationData = applications.Find(application => application.IdString == CommandLineState.LaunchApplicationId); - - if (applicationData != null) - { - mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find requested application id '{CommandLineState.LaunchApplicationId}' in '{CommandLineState.LaunchPathArg}'."); - UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound); - } - } - else - { - applicationData = applications[0]; - mainWindow.RunApplication(applicationData, CommandLineState.StartFullscreenArg); - } - } - else - { - Logger.Error?.Print(LogClass.Application, $"Couldn't find any application in '{CommandLineState.LaunchPathArg}'."); - UserErrorDialog.CreateUserErrorDialog(UserError.ApplicationNotFound); - } - } - - if (ConfigurationState.Instance.CheckUpdatesOnStart.Value && Updater.CanUpdate(false)) - { - Updater.BeginParse(mainWindow, false).ContinueWith(task => - { - Logger.Error?.Print(LogClass.Application, $"Updater Error: {task.Exception}"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - - Application.Run(); - } - - private static void PrintSystemInfo() - { - Logger.Notice.Print(LogClass.Application, $"Ryujinx Version: {Version}"); - SystemInfo.Gather().Print(); - - var enabledLogs = Logger.GetEnabledLevels(); - Logger.Notice.Print(LogClass.Application, $"Logs Enabled: {(enabledLogs.Count == 0 ? "" : string.Join(", ", enabledLogs))}"); - - if (AppDataManager.Mode == AppDataManager.LaunchMode.Custom) - { - Logger.Notice.Print(LogClass.Application, $"Launch Mode: Custom Path {AppDataManager.BaseDirPath}"); - } - else - { - Logger.Notice.Print(LogClass.Application, $"Launch Mode: {AppDataManager.Mode}"); - } - } - - private static void ProcessUnhandledException(Exception ex, bool isTerminating) - { - string message = $"Unhandled exception caught: {ex}"; - - Logger.Error?.PrintMsg(LogClass.Application, message); - - if (Logger.Error == null) - { - Logger.Notice.PrintMsg(LogClass.Application, message); - } - - if (isTerminating) - { - Exit(); - } - } - - public static void Exit() - { - DiscordIntegrationModule.Exit(); - - Logger.Shutdown(); - } - } -} diff --git a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj b/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj deleted file mode 100644 index 722d6080b..000000000 --- a/src/Ryujinx.Gtk3/Ryujinx.Gtk3.csproj +++ /dev/null @@ -1,103 +0,0 @@ - - - - net8.0 - win-x64;osx-x64;linux-x64 - Exe - true - 1.0.0-dirty - $(DefineConstants);$(ExtraDefineConstants) - - true - true - - - - true - false - true - partial - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Always - alsoft.ini - - - Always - THIRDPARTY.md - - - Always - LICENSE.txt - - - - - - Always - - - Always - mime\Ryujinx.xml - - - - - - false - Ryujinx.ico - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/Ryujinx.ico b/src/Ryujinx.Gtk3/Ryujinx.ico deleted file mode 100644 index edf1b93f71fc22179767d13e0ad307a217d440b9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 108122 zcmeHQ2|Sd~8-I75k*Lr?uIM61hjckAm6UEF_brrCDJd(Jq9~PAlujy%4qc8dp%X8$}4bDW67WM!NDK@u2t(Hh=__t5)6JQ%j*7!KnB7x^ujG3?g` z9Cm1h#v(3`(Hsypm)hb5tI%Bpc9gX4mLNz|a&^>_*rzKbRgV%(M~5>atszd9a0B zF7b+{i!-+_vk8|Q#`lRcig*>A|5A9$XtS0$5{I&)@Ed(1Z6#Wg9|ps4a}nnLnP4K0jMYCCyI z+SsIFX*}6A#iq~SZ1%HGnt}thC2Zo9FehG*bm1qTY80!-1hOS9;S@=D{%sex`JlsF z>sV%cCK|400ZkU=dMO=>Eg_HLPfk^_cyB)@{tLF=2*)n#IkUPbuaa}CU{0+~I9AW= z^Q0>I!NX#Oxo@wsD{rV}d-XwBz1S^z=G9Dv^>59!vE#(Tq{^h(j~Xrt##1j5uQC7H!Q((+$F0#yt?>s=as5k zC!a7TynDOn!irQkP3E+XQZ?~A$L_BUtvfs=e9rg%Gq29B&^FDvc17v4tniAHc%6MW z*@?DV>x6USc=KFkCRJ?B9#O6+HKjsYYUSjj1PO5hs}{5Inh`4N+%oe7Lr)0Ke#9Is zvSzr};sqOs8-Cb(Rc2LPT8kT*U%9_hVQx)&dsxx&VulyA<@c`K%PD$!ma z&rh%?F0CbxS}JnYh0pMaP=hCAV^M>%{T!ADF_*1Lx3zR?7dU$STrpB}MP0UO`fRK+UOu0?AU^BAQjGbN>^ zo>cs#t{W#s7)~4(zDAoxKgNk=|1iQJr{r6SW2arYFs^*#ua6pr3zqsB2RrEx3*J(> zVN1bJ111ZTV@gauFSwOvCEw(+9_~`F_2oquL6>(De8}7@L zn-^TES}GMk(VHys^1=UkN!AEG%)|WCte7F+nXwN$jO=C^f@l~9TN+n(&p1P5T zR*x&060V!HN`BR_SDXEAUSeLu{M~DRicBoG(B#;Cv3!$rLl#AanvIVfwUIRY&8Ml- zn$~3ohc>KLRlHy~y4pbN={uhVbL4N#eUPbec0Tiom63}K9TwDom{7`Lw?$GT8=s~S z8Efe1Jb3Z5c?a$g1Mg=*?eR=>WoKp?x?@J@QNh`@%M54E`IW?3`;J@wTvbhc@+>b- zLT356D<8^8d!$C&395_k5pCF=?Yl{G#8VdHX2-JxRkl5&eLcO#?4IayI-oYgT<{wT z7kJ{hMUkM&62%%JcHS8Ug%2GXsuaLI$>|0u?cHkP(%kfT>2UiMBe3|1tT=VisO2_~ zOBK!@o%mBt(Q~yRK0Ky`ZR|$_&g)8d>chCbq?`+49y;Vd!W1;Z*tPbDBtJV-zVPm` z>&5EuzR)G&dO{UfTG}odm>#mEDa48!YP-%RJgI)deKLbh>8_DFaWZ<;JEe%P zJV5N0$;tia*s|rfh}_2n10O8V@+1xk4qAMrGQn+L;RWt{PEctfd@xhY-l*YM2Ky~E zb>S)(wc{ji=J5Pl;5<82GH3|fN1xp|$iUT3MXbBAa`}~GMtt<~s75kA8Cu3Pn8{7W zlW@YCo8+XfNq}tRJ&$u)c{Dk+o`BO4Oh!4c6v0<5KwOb<`HC4~5l%4B*_ZgD`o0bB z0Ys4+aGz6zZCm+u?>_;qcU)mnXBr>^>XR9#y25IjJb^jLl^s!Ej0AymW607Bj z5z(S3T8o3fLINTJPv#&|$Sc)0&4~3UcqVRQcSxMajOl`6!lN$7PBB8yZVD=5%g{WR z2W6unUSx9ka0i^7pW=Mgg*v-KP@LVND$GGV4?1r(8K*tk$1gCYWTXH25&nePcWiVYu(1bGx>u=;@8ZEmT*VBKlKw z&362mgRew%)x1*Q$MFW1J;2J9_U2(?+*lo(d_wJQ_ZdZF1ssxr5fCet7do4H^ExWCFQ>snS@K zD5aLeQ6g@qSgf+VK0|2~?tqqllH^bQu*FOp0)yUa9*A1xanj1a2*o}4%6*rs@tQ8` zchl>cUJyl|227Z*>u==imn5g?8cX&Uiem{dsL9J6CZCylWG3$HLv|;BdB3f@u1lH_ zF9bNQNNBjsuRM8QA~9cU-;+CJM?ug`;^j->w7f6EHs<=5j}$~04vFS4A7pCm>b}M$ zFO_s;d9@G$pN3Be+{d#pRJ*2d+EeF2K4nTwbCwHOmrr;XNn9WL>S@enbpmsZ`nDVc zBRkwrgL#euL~|?I;}PzMltXyaQX48#!-e%5wwuO(R8EvO+|NgO@POR#2F1}gFOG28 z#B?ix$K{B3(b$9rm5Pvn!yHGZe2`rELo)2S(|v=8#L?1NgF4IE2K8x*IkU3;){hHx z-T3SMKiG*_&$+K0tKY8BTI_(I#X@8WaQ@=Km7q6m7{P}SNI0JL?aGX>DN0xYkAjq) zh2xMl%q)4w=0s|Z*2=5&aL|ci33v@JKWw4Lr`MACRcc}@_RPIf(49O7F$k~khxt#gIm^=70EQWkL1}U6hs*%N!xYql@;KLk`mZ$Nr zBF~fBqJQk;F-KeNK}F0`>>&6eezMYF!KKccHYGAR`y_pWuDF=l`v#VakA{DgdTOU> zgp)Wlt&}-nFXXecsnNqhqdz+H+Si-mkIrUMxFCup4Pz=`wpi;sNc4wef@@oQF9FPoF(v*fI5cJpQ-T6HW&Cm6d1&f~HE9xUspD z&${OmJ+y`ybAXzi8E~ys8(rR$N=F#a@C_-kKLIsCzmm?bn=W(rcOhRsN3Sf_+%0`IH=})W_DW zW3rTZpS8#}Z?@|6*`&Q3YZo|QzFay+vXyr@Avi=|2)9`FZM{Z_bb@G-h2E2hD*V9-mpvnbZ+<@V=Cm#OwGaOl>o>Dc zL>-u;PAK>`e+RdVX9hk&k2Tcv3^#V3_$k|VdLB_RQF;DiqW3n^9>;HPyeg`uPPV6a zWlK-d(HrWmHA#)@B(dU+j0pZ%P>A!Uy?LGiv#yJg#frB{dSioJl!)$!oNkUSj)Jzp z41Yv#SHj|qhhJ=*#-0(_s$W;VHd;nPZhpmRiDe;GGd`2T0w&GavxH==X}v0A^0Qe7 zIh7s0?61gseCj|=G@smEo73;A&$ADIX&;fFp%(YqM`d3?=*sWMwq6sx{_X%NbdV|^ zv}iwOO0CJ=3ccB)RWW&GksMBvIdZp$p5-GwIC4XPpd0s;F#bLH*4qykm#7V2z{jWN zJofX<{fC2OMPCpC<`b?rWJo<13uQl(CC9uJyX+}3Y4Y}rBMyV1bA0gyzGNT!TfDPI z)EGxK$JvK>);f=pWb!HHmA^r9D<@|Q@*1V)UHmq6m&$?TTGxN5WblMI{89+obAAH1 z&$O9qmSoHxe(ce_xjrR}!k*ht`gPbn*zR6g*uAPJ%#*Y|U&L=7Z@37`qqH3Vd4m}F z{4DJ%i;H0+?qDHYW6!afTb1WN-tO*`KWER!Qq_lR6Xe$1A?>{(@{%KG*s;WeelhZ+ zY_%{;1x4$bsZ8Uc5a=xs_6@?FlFt+nzIxTmFol$zB^>%A_N3{SjE~mj>^r$P`92BN z);uP--qzP&>a)glp+tl(*AENA@Li;rV^an_FoaJOH=C?QhGi{z(l~sMWlUIQ#;y8! z%mr%v7F8#X?@+L;^na&%)ML`C5Y=~cYVtT|=|R)%JTmawf)V2|IsSE<0v8%E7icWl zzVQ0|WnMCpx@t%FzhrXtsQwyx^Z~}}FB5&Hkd*AY7WaZRcXaqwMHPWMa#e^F7rvPN zx`Vmb#Jyr=leWkQnX@c(D%4lj#oU+sJUh;Z*@j@c@q&bhl9=k569lGTah1l_93D8m zI?{5|_**!w6}lLQM$q8HCLf)L7~gUBIXcxtmH&;&CuQl2;-gr{saD8pJ=fTO-Q92b z0{sh-td}%SZN^PdG_!Hl*uQY5Z0MJ0)-}ifIrMGn)-$>HJq~OV#ToCvE_UNV*{uW+ z`uu#Nm$9a@&q6;eK5UY*?akM0-nBe-(bHBPTIcfe(DQ#{SN)hd$G+)Vc8}KvXFUug zoVN@e@0^~AT`}Dz&7oOyf9KgP_z%YU%mpcAd?tQ{(-5r6O+opZMKY=axQ3p8$}<5#T6y7k61J$j0K$=S(R>=J^E z_iAjC+8D!W0b{>REHt<#dHedjxy51rDNk4e9-Igbj^1oj`IdEqIc|>YHr$Kpix=fv z`otj~h2f5m{2$NEf##mH?=fBk$&7yjHE~dTm$h~qOb?p6a zm%32qOTo%&V*Hhl7YICL(I`&Cuh2aZ8cAjgZ6KdMwP(oGh8%Cmy@^DX>DVUD>z6L$ zMOLu+s~yb=V|QI`_siz{R!3Kka25rRcNWH^S)WL=9SX?Kq_fWJ6=zP-^qlr(OoIQi z=q1Y8H$-Qx=2|(sj?+)*$z(#t6oMJ~h)F)V;LaS*h=sfERlHEYvP5FlcaK5K!kG{9 zWrwU`U5=HZUQ#8nDSa9!@XBV z?BcK!M&iFnV)YN^ToJc(Df1Q?9BW3c)}zW>M|Uq;;Jv_)vk+Fr+d#Z7fuqZ3tVN$H^FNj6qzlF;l#fIv1LxzJhw+c0D?DvV_3b0`zF3gVr(^j9_q;Jy zxwp2ImGOErf5Ck_Mv`{Jf~)5FUU|6YVIm(yw17Q=t1g2)>BtzTo&T_IDXUC9TxBvu z`SrQc6TjV_7N7oO=a67&ozhLa6EA(e^g`qli)G{@l@(*ITRL5y=d?rN+FU~R24*qr zptEY+$(fs4CzCu*}y&gRke`&)<@H;(~}JOHe@mHa2_F z>oK`ua|VAm0~zA=e619{UVq(r{~hb-*E7$)Ci9IpBN{KWNOsBNm~SolO-^ZSo#ssk zH+R#y*ewaXc#t{Qg3khkNo&?#{$5c_cuL?%y>{TISJ+hvjSID zs(c1ffW=TW(#xs{Gp&52Y~mgL#?%0}_vwn`uFLt1kFLGPo3`B%)8u94)xNm$UQ#Z6 znf)^d*(5|Mld{jl`&AC1Ay1Xwl|PnYB6BOSNL^1-_Igw|P6$ z=Gg9=9!m~dwPk#-!cL;g) zBBOi!PY2mF6qk>Z#$r`0*_G{Yzs1{)zQg=W8n;AQbxnNO4^6r(7fh9x@vSY2NX}f1Gc;_JEl3@M9jt*mOr*+$QY(DQK11 zo;=<9_|{G*7WD+}0}AUbCkH{#Roz2w3a7FuPV3ROozf1rDuMT*WnkU=`bF_&QB2Nf znFEV`!VhVj^u-vWv`g|cox_WihpF%2P*^?rVc8Y12=P+Oh_x7!kok2=aSYg)d(CWf0HVe{M|@ zWYa9J|KMC%;d~w_9(-V@3GSG09Eq%(ykWJy(jBRVmNvhx;jN8x?8>6OP&OzX-k)6Sj>1WVC~g;2v=`RCkHDWVO%tD9DA81y_tnlHo-LG2so_ z8?h&fvr- zKJ!yc_+*kl<^a8}jj{fd6Z*vYP<^cGM626ulA4d^hlKDuX{P;(RI0kQOjofYcVqM( z3lA8Z8JXt2QXH+Sd|PAQpmEqbS)A5CyXtSp?Q*%lw+O#Ne5J}-({U>1CFQA6@hk!N zN5wp~*FGf#PhAXSer|h_?W0`q$s~4yyEw-g5&J{W1&br67hmv4vm~^de8ZRh?*0I07d|d&BQ6zw-n>K4 z?ZXbP!@jaYS4>_VY}v!q->Zebicr5hlw@rzq>#vq*TrQpYhP0ja;S@)z1|BVv=@z3 z+Wk87><|C**{^RjIO2}l?&VbGKE71r+vSr6yG|%o1#y?#dY*FJ>}Puz>@dwnmqd50 zde*zmwY-gXm%C!v+K5SqGs`8wKy&Aal`57U>Tlr*Mw^@B_?rny?qG-%2E()Lvau2$ zH~T4GT+6Afv*TQ*0?Q>n&a_c!S2h|1fu(Tyjg^FN_j1O>=j!Qbg@wy%;*X66VBRcD z++e~J8%!9L_A-~8>Ur;0%DY0CUeQq0xs5@`CRlwLR~b&q@PbOQaE=0t`K6mr_+2uI zSSVZd=Ctp=M|Ze~X7V29n$D^=Zv3>PZ&($JdZHGKM zpvruXC2EM&wmO!Xo{6(;2Z4feFgW*PAS|#H28{Ya`tx3G=1@tQ zKwJw^<`C?SHq@#U6-uyA%bwda2?n}-;l){ISQ)Zci-iua)?UiY`lw;v7_jc2orL|9 z^-K+HZVh}k=ZVXih9WcFahq_KqopH!B|_Pf*y`sR@(gEzT;BJ33tnG!^eBb>lLzS& zYdIg8By4s(eDgL&+|EH7X%uc4no_Ld}_kB;gXQs^s>~gZ(h^HU6j=3=|LO^}g zc6G4n?z?#xo1z)|dcF+i%>Iq(8=D}@IG6MxdTyCEoLiY5+uwi2maufS+%aa!3Agv! zN<`QG|Oxy7wHSqd}8OMb9l{b(Ghy?|@|FsDJ# zl_SrvAvg~nzBk%Kg6D{dRjh07Zka11AB)VKdr+6SJul^i{+6hZp-;-Clt};-Fj1kPXemrsRSd^_BYBSGYts>7}y37l6 z!0N)TJh@Pwvsubf)}4!_VpDQz_ST1HHVNnP`te(Ybf!EMw79V{T~J=``-yWy3Hd8x zSd)hFPaYX7MG&>&F~`fk`iKwx=%c)E5HWGulFPfMiwCY#Q1^Jf%TWgv)=A?qY~e0kVcs5#&Jh!^qw$9d@D_xW4>&MBw)$;Z9s%C*8-x=h~{nNM<^{MLH$Kg@<) z26eON?)zFOAyrp-l~)IQFT`SqjIw7cPA{&w=B zSHSMvm%fmVlPe3if<3#|?bfvt4%r)%r9Hmm?fmwrXvNOUR=Y79k9>PCnb)pc*}z)D z9ICY^t28BrGgReFX06b6d%qjnv7bmcY-GhWd2t6rrJ|*N$a6W&@*G<-cbP|#xTxP1 zRW=dcMVKIzR5|%18Iv#0k~VhB-H{#q)LKK8^at)vd=Iop-!2I(QI~PY#C_kJC2#y* z?;x1$xo4Qd5nF7-HCE-Rs>85U+Z+Wh-W_`B6Vo34Opn)pV}JA_J0Vkn>yzv)@>CA; z2!-QS%24!I&)Goqdn$R4XZM)luDZyCzIlaBN%ZBC2V%<&e?;pHsnhd7sk2GkR6Q-S zoXLV!sVbIhHt7sZ`}i3jHPncXCu9=W5jHp#1Yx7+{?ZC@tu+^kpEzWC8s6?&`E!#p zqrLOUrWA*6EHJwn=h)T;oiLnN+L;zc07foTziTr#=^KY79MkwV^(m z=@m!m_%C}0Yau)R2i4k|SY{+W+ zbHdK-2@JbJ^g;Id@1jpwYgkmHZj>lxv%YZV_)zJT9lbp`@h;@UR!{AK5V63Ok<3n< z_T_WErbpj=m;Pu3N@S)_So!zQJTtGA#1m^!mgjzAd59^fswUOSm+aJz`BY^QY#trK zyhbJZz#!uNu!1O$tO=R-PD7rnUOB}gg3IG5(+cxEPQ308J)Vnh!gc!hWn^Pmh!c4a zEgt+qwPK2_l)ntmSxilA3;)^I%Q>X*IV+`^PASVUc^vdxt2I>O#>eP+UVL}A;p{k6 z&y2lg%oJvl@@vKg&zXfjeEcltL(Ny{vzdqblJY0rzW(&b`y8Rd?0fpB$lMom%stG* z=AD66449g##)6P}hI=Q#H30>{5(l&fqUjGjjK4m~0ZqetuC)fFGiwdV4FGaCP_Jp& zkPHaXG$ifQG$5^q_-28BBwVwCi|-Rf7>RT%2Y~OPfDk};`EOlj>L9KRCR|GiKarmi!rAGF@QsMmF3B+&UB=w<#XWuX?* zc?D%aukpLU_$G9opk98mkNBsQgAz#ZFqDBw&=xTI1-;B|XQJ<8{!uxAwt#dUFnzTF znbFbj3`xECOh52XX$!*Pd!W#ZsxhMOL=N;L|7g4sj@kiDg9Z$N@z?h`(2xA1cAy$? zUDKdmjKP23bB|K&XZ}$cD1tWNfR<4;+V8~p>*E}t1OCx>;1iUA6@9Ea>U**R{;&I> zbihAq11bQQVO%KOhh+ebKiL@kH`;%D%l=2{>v!amfd4Ph257G{s)JpyefIZ%UH4Hg z(@FhD}fFS@uz<7WhKnb7*SlUG_gXiY} zcmV^h4A39{`CqOdbdl!)k&!adN0a$uN?%s@( z{`e<-0sdF_q|U$#6JRpH7;poS0r&-|1(16HUmy-m02)sYs6YDSpIiWafDJv7iNFWq zT^L{jxC1B$^aS6n<6!XLy|sg=&(|7get75K@((<70Z^Na+W1@m3DBCip7JyJ|C7ho zymrO!{5Sjq&!Yg=fLK5!pr?4I6%T{|p5>p=UH*YyW;2*da z0PF|k02%;2$#HveG5GIi{(QO5I`S*`gs&i`qE|a-%tDl z-*~`GKtexpkIET?|9;>f@*M-9Z+-xv9?+L{o;ocC|9#9qaE-WU2j~D$e~+4vzP@Jg z-}n5ZIUH2?1N)hKRMr^$_dWl}wu|h0{nh8AZetky_dWj{z`YxQnuq?nX7Jzl{Er0g z(VT66aZa5sga5wge+Hd!kIDgq|Gwwnj=_I-XG#8jj}GVm(L7;?^FP4-RR;h6^4Q(0 z0nGnf2LIh<{onTnF#q`s{{Q8%yH^94|DIaAN8OL_?lq(T*Z2Lu?sC-|?=kpi@XuKP z+jC`r(9!pdj=ueYhv(7U6`H@Gpj~xgPb>y|VsNl78mDDcgEKJ8YxnuC+#JB) z|K4cFyBlX$`R4}i;|GwuxD;9_L9-_G`v?l?T zoh1O8=N_y#DHn$_a2NO|K{*IpZ&EQSGqQ_B(|DZ0f8XQ3iv{>! z58PJ)QnZb$jP=b5+1sGA%RvnO`;C8b;2iBmMSac?z=R*q+TK47eBqrI`^ zT1K^dbxnV9cbV)qUSROwZ~Sut=N^DMz$<_N@ak4M5-jJb*4>P+RQTZ2EvfeIEh!{s{B|$xsfWbWO?> z49yGv`n(AS|NX;1aE@{egG-}^RnQ~5?cJ}7*~nF7{DL;fOYGPYKiMj%BInJwzJ_3{(Fyq;1jj)=xi`r zP=tHC0I2}<4R8f0XUAc)6Ca4paoiE9sB2uRq;Fc>2Trx|*bwDWq#f@x2W;yY!W&tO6S{Pa6SR0rX z+B9PWaOnbd`V}ez&=<(jGyUZT-vGE@WZMQdLftnrw#<=X@ZTnHZE@M1ur~RpmFKO) zfp2X9I(HJKjch_R$8|=Q*?hP*9`u9q+R%1{)K?^U;wfMWCKP4XwnFruR?Yp;I0nJ!Z}zEAVBls4sI~`?|=TY zp5fSlZ!`x?Ig=KR3){2Ydy14&h~hwb&tV6eee(S!NY-bA^_F@uYh)c0|9KmO>;qD z3x=|A9BjkGXmQ+LID>!s;6EiAL-WDs0m%M?)&nTD$D5JmPjSjz5cC1Uq5g|(*z}QR zpMh3f4F2hZf5Z=PZUsO#;2OXtTAZP9tBqe-!441ra{+bG7yJn8L1r78=ePUKXe&Mj z|Gm%uk7qb002bYe)8=%CC_ls=VEgRjtw^)xbBu z`d>7l-%LdOgZ-4$0PO(Wr-^F#UJzldFq^@DkJNeU^3f6hsBgy)SPRf<2WSon*?~|W z5S4?|fOV-vF6~P`+jQ0%)akA_u0U9ipkrKN1pC3B0QWV}2P}d$fe!FZ&;#hU0X?u2 zEr)faEo_C1`rjjEpsv?Hz_$c|(g)lN*EIWdCYBj^sLwm04_E+g0tw2)444D# z(inijKi%<!9U&c&kWq3 z1t2>hvI9+Mi#JLbwC9pw3w{ecl3|(`0NNiybFc4_eaB&6K$QW^1LXmiWL=X|n~pfA zzR%#FF8N3HqKg1D#zf_y#h8tnKXeW2z?GoxCjplvC=0h+-*3;);Gh2Z&y2zFz`1=h z{~6#eZjVC)^L$a519${nqPc)<$DI*v?jvn04hH}9$3Nl;IM)E6HNnMzc7G4mY(pwL zi~;;$Z})pad7r^QUGtCnfxkiFjFzi!UWDnJ7dL{|Gp)lJ z{L?4@t$FG#KZAb;|4;@P`~UuK4N#BG|Mylsrxh=q*8g_x9Y`1WE>f?*>F;X>|BY+V z&^olg1qt>vHSD5QUMb-W{wX=}cP<^)e>mTSWZRKe4E{UHq0Zf>L;k5TvP!2hy!2K6-0KA>pD;GdEcf9KK>|FzKXzYY-6GOX)RDF*)?W>Kde zpdpzd43x4*--A1TJ*zf<&f_z<1&59^l5ch?yze8F({Qw>C4`(0emAARd8ed5(D}a!{4}2< z^|$MHTD%XxLqh%vy!*koekJtvXr61|n%|E6!2LfA{yWO0j_(6E==`@^J;*<@i37l!?bG|w>TSjs=20PfE*`2RCsx*D?9pn(Im%fMASAg!x_dYuC6b>4!D zewXk(0OBI)0TckEA^vvvt#nmh%JaZI`fV*r``h0xYX8>(IyHAe(SaUZO_*y1p2UE+ z#ehX!1@Nv4{LcYb2Gw(Lhr>6X1J0!RtNvX_c>{SZNk{ynbD9Pu|0th=!}g1 zECUSw`=M|E=d6Gw0M!5PZ~pZ_21fhe$K?R>9Q6Ya1JHSi{Z$6g_g@FV;J@$rNBw{) zfX9IT`VI`}@n0WP*5k^sDfp>IX7P1L`?uRnK;J=@E zr%o1lN57$G)emKW!9O(-{d5hyqqEad888E&Zvo}ElzRJ{z~H~1IBzRi;2nJnRs$jc zeK9Uz@ZS~@{TK$kqduV=;0OTm)0=Gpga3ZynpV=lIT{-x`v9^Lodlq>w0q-Qz~G-2 z33Lz+oFh9S>KDue>;$|3^kg|mhWG~T{4X6))axPuKWM%HwFhY45VZ+t{Q&AGqIE=Q zEg4!*h}M*n09`E`sGl(d@Ym}adp#otXaeNlfAtuK`|W`X@MF$Y0mv`h%Fo@(&(_M% z)PbKm0Th2{{H4utHa@RJCb#el|LLcQ(D*!+z!X2JIaP{34Z|STCjBvZp3u?{kGHsA z-O69m%AeNCPi*BUwDNeYvQM?ekJmgYT*{~|KYE1d7s$IpVpC|)bf2O zKrPZMY3+Azb^i}PTg&&ITl>9Q-2dI446yns+)Ccoex+9Tom=_IUGuZGdcL)vyVZTA zR(|J}e)yiXNFP2=|N5b2g?9d5KWg*4?*F&_ZF;|vb)}|j^uM+L-}b!u8B}YOny-;V z=)j-W>ON9{`h6r2wI3x&?MGWesr(bbs|+9jaF_`Ehd<@U{{!|Pth?2QHJ50A46Soh zVQ)O?72HSrCsx2ZTOL?j+h=Pmdpl!6FZ~Ymr_L9&hbR&9K^yX*SJssGw!W?7@1p*c zynsFPq*H(%+e_10uD_*_E0StAiTN++4|zbk-CKKXx|y$?eys=iM`fT0z6o-jP81#lI}HMwAK(Xw118dd z1cZ0#_q4krM-SDcUE`w+(6MknwI{c1FGAfx~MlL zj|Q~h}h6{ioKWyVq^%59Nv(AOhf_?svi2y|5R&VXU@MH79jAt?M4_ z|Nhbc>w&V5@{E@LTyXC>U?1SOJyzSeiXZmEr)nA17-mM&()!=#q&Eqi>7=MSuA1U7VIt?QY0}SUuCy4(q@G*MBnT zj@Hb~P6vAm`m@>e0fVx?7}|g=_$_L2O1{u?2e|&I4M+fN1W>mDV?qB6IL}y(mfn=` zfuuj^I0zs_1=Rir1K{_`al%(lZ5GlqDG`BkF9~hnD<}si^i7L}0U}K(5;3y)Ihdk5 zatE0H$d2#>kVl1bxUNP1@Js9Rzz&xWb9DLOR)cOe=$Z-zkSDtato|_=6X+=eKzl7I zSP$1_0PUEY%2N%^^W+W83a7(&&O1(2lX7uq@_Cp zqjE6N^rtSn)YqW96W}G_cU?ELO2;*|0d+yp9=6#A{)hBe2Hi^lg4Fo}ZGau{pKf4Y z(B|2n{~`T{f$l|s<<$B^KVS~@11j}Ri`x8si~oxL&<{X;Lp0BHfLedl_CxD&-CB)f2@BAj5Fl_kM(~H`u}n6=>JLi zuLRv!K>w;W{ir`i-Hvtlnx5M~v~T1bj4}S`SE0=KkL!yXCQ!$`~ z24F6U{0R^Ou!6Qu2y99%#ux4Af4PVBm!Xx$--ZL1gFvrwv;g{wK^^A>8v+~roR#3Ly?zN}L191-a4L*l7JeMVIcl#qj>rfuuJ@R*)O*b=VB32Xsew z^bWG=O`*g&&|Hw68-Uj6bXR}W{`Xq_?~D8d`NaTn-OPht{r>fl{-`g2@&J{CuGWPC zuRkS^L3dOaPHT!KCbu-ch0BUzB1le(%T^uJbiX>Y zZMWW}DZGAjOSdsUx}hg}kFEJV?q*lG*;Q(Gots=7xzPn`TDxf|pW&}D|Eb(Ye!$bx* z@XXlOadg$+P5Dt7T0xx_$bw`y0n`9Gk{#WrH~G<8VkA>%WTz|nL1r}Pf4vK`)0_Mv zAbVHxp5Ej~?P6!k4dpqyDgPk*jsY&eb5F<*aiDL(`8=m6^;2g6#|{p(H2Xzl@{Gx?Q4b|L@r?+cCAm;Gc6ed`@cpaz&dCy3K?`DKO4w<1@NL7 zpTRBt8ad*niPewSFwgcD`id360jyuIdxHRL84uEp{IHx5?x&0xHlaXp)5#|NI*NOCZ{!{Xi;)3j`FNL_6LkWXCw}<@qYikb?y~&Sc z2HCv<+bQxx`#A%~ORv_Omi^urK)FW`^8a{-L+y?@0G)-8unq9=d-PCAW2(xd!HE|4AB0sPT5`hz|koaxZ;4fZUJXbNfiPsj30gZ%FRjrk3G5O5&>b2uk- z;Q)~z?K9T~{I+qy8n*>-oU}kJx{x^Q5!aU1)#OK=%1<#S8h5&TZ&yyF+=dr`n%|paaZlk>I;Uu^-9R|6mi?Ibtkdz-ckiZUQOn&(RIq7LY+ zffnb8P~z)r7vwOgn$ zBb`C^@qh;aG;d02Yua+@Req!cvZ<*7&I8aoyE;H?9nhL;q3-b9lkdP%Au=Y6VT~?# zvkN1pMnAYDXSffo7saiIJIG(s?4~ul#8xh$*-zWeRzmR*u#DEOa8r0@vx_k``CGfC zUQOXO%`O4gmYq33+gisYbS-?P9Mqe+m1?+1KJJL#-MZ9 zP`}L`aJU)h8{Fb-Im$b2xt-7-rT>)rU5K9(fX@19Yn?>}gtxE{QNP>zx|8XrP~L;M zkzFth(7G+9_WF|HFf%E9w!LEgw8&L^e>oNWwdB7dNX}gW+Cn40DhQzMDxIY zAV-Tl=&kf2O*9vW_8DvdEO*&`n+I(7&M*(81oNLfa5iG&dVpR`AFUTe^RVb_-dAp} zfnJb4tiNse2g<@RSeLLFT2T z48Q?}!5oq*lmk=B{XR;c1=7EW(uB1De4zW8zDysb1?i);s4OrCg7P4cat2Rd=--?^ zY6~0H)-@|->g)6y6K>;QC-qOFyb3zB)#g)&{cis$@3iN3GW{!*IQk)d7!UmWyK?=I z{u!9_`u)v>^wIih>ilkf-BbBr3Ea4W?ktqNM=qqD+Clo2aK9`4-w}|G2Ne#&wI!r0 z2JIAe(v8;;H(FQGT4!oMO236|{f|igA^_qR0ieBr)M-**qx5@q+z9a_TQ4mcy2O8X zvjWmb`GB}c1yGmCa`5wYH{SN%Ba^FP<5xAVt{G{<)i@%6&>F*b;0QblIKoH(#@>XH zBXI;lU%1AaTy!l2_kXhSziF^9gOu`qy*281fcgRs6zDyJp>#~0HA(PEC7v}ey{hUMAF9lC>~3Ab`&5V zKq=SA4%wpLjN+j2J{pg2qv-j}|-BkKbFmqW02NV$ve4~F*_Wj$j)1nU>}K^&{0FE|O}pm0ZV zQU0|he`Ef`z17ff90Bhubv1p&8N5F}If@q#@=JE-eG~`fkH-53?a5Dh7rAY`{{zbJ zP$h9S h0UK7aRDdghsTr6u*5VKK1Q96rnV!Hk*hF0c{{!KrZ8-n{ diff --git a/src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs b/src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs deleted file mode 100644 index cb8103cae..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/ErrorAppletDialog.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common.Configuration; -using System.Reflection; - -namespace Ryujinx.UI.Applet -{ - internal class ErrorAppletDialog : MessageDialog - { - public ErrorAppletDialog(Window parentWindow, DialogFlags dialogFlags, MessageType messageType, string[] buttons) : base(parentWindow, dialogFlags, messageType, ButtonsType.None, null) - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"); - - int responseId = 0; - - if (buttons != null) - { - foreach (string buttonText in buttons) - { - AddButton(buttonText, responseId); - responseId++; - } - } - else - { - AddButton("OK", 0); - } - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs b/src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs deleted file mode 100644 index 0e560b789..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/GtkDynamicTextInputHandler.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Gtk; -using Ryujinx.HLE.UI; -using Ryujinx.Input.GTK3; -using Ryujinx.UI.Widgets; -using System.Threading; - -namespace Ryujinx.UI.Applet -{ - /// - /// Class that forwards key events to a GTK Entry so they can be processed into text. - /// - internal class GtkDynamicTextInputHandler : IDynamicTextInputHandler - { - private readonly Window _parent; - private readonly OffscreenWindow _inputToTextWindow = new(); - private readonly RawInputToTextEntry _inputToTextEntry = new(); - - private bool _canProcessInput; - - public event DynamicTextChangedHandler TextChangedEvent; - public event KeyPressedHandler KeyPressedEvent; - public event KeyReleasedHandler KeyReleasedEvent; - - public bool TextProcessingEnabled - { - get - { - return Volatile.Read(ref _canProcessInput); - } - - set - { - Volatile.Write(ref _canProcessInput, value); - } - } - - public GtkDynamicTextInputHandler(Window parent) - { - _parent = parent; - _parent.KeyPressEvent += HandleKeyPressEvent; - _parent.KeyReleaseEvent += HandleKeyReleaseEvent; - - _inputToTextWindow.Add(_inputToTextEntry); - - _inputToTextEntry.TruncateMultiline = true; - - // Start with input processing turned off so the text box won't accumulate text - // if the user is playing on the keyboard. - _canProcessInput = false; - } - - [GLib.ConnectBefore()] - private void HandleKeyPressEvent(object o, KeyPressEventArgs args) - { - var key = (Ryujinx.Common.Configuration.Hid.Key)GTK3MappingHelper.ToInputKey(args.Event.Key); - - if (!(KeyPressedEvent?.Invoke(key)).GetValueOrDefault(true)) - { - return; - } - - if (_canProcessInput) - { - _inputToTextEntry.SendKeyPressEvent(o, args); - _inputToTextEntry.GetSelectionBounds(out int selectionStart, out int selectionEnd); - TextChangedEvent?.Invoke(_inputToTextEntry.Text, selectionStart, selectionEnd, _inputToTextEntry.OverwriteMode); - } - } - - [GLib.ConnectBefore()] - private void HandleKeyReleaseEvent(object o, KeyReleaseEventArgs args) - { - var key = (Ryujinx.Common.Configuration.Hid.Key)GTK3MappingHelper.ToInputKey(args.Event.Key); - - if (!(KeyReleasedEvent?.Invoke(key)).GetValueOrDefault(true)) - { - return; - } - - if (_canProcessInput) - { - // TODO (caian): This solution may have problems if the pause is sent after a key press - // and before a key release. But for now GTK Entry does not seem to use release events. - _inputToTextEntry.SendKeyReleaseEvent(o, args); - _inputToTextEntry.GetSelectionBounds(out int selectionStart, out int selectionEnd); - TextChangedEvent?.Invoke(_inputToTextEntry.Text, selectionStart, selectionEnd, _inputToTextEntry.OverwriteMode); - } - } - - public void SetText(string text, int cursorBegin) - { - _inputToTextEntry.Text = text; - _inputToTextEntry.Position = cursorBegin; - } - - public void SetText(string text, int cursorBegin, int cursorEnd) - { - _inputToTextEntry.Text = text; - _inputToTextEntry.SelectRegion(cursorBegin, cursorEnd); - } - - public void Dispose() - { - _parent.KeyPressEvent -= HandleKeyPressEvent; - _parent.KeyReleaseEvent -= HandleKeyReleaseEvent; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs b/src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs deleted file mode 100644 index b3f509a09..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUIHandler.cs +++ /dev/null @@ -1,203 +0,0 @@ -using Gtk; -using Ryujinx.HLE.HOS.Applets; -using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; -using Ryujinx.HLE.UI; -using Ryujinx.UI.Widgets; -using System; -using System.Threading; - -namespace Ryujinx.UI.Applet -{ - internal class GtkHostUIHandler : IHostUIHandler - { - private readonly Window _parent; - - public IHostUITheme HostUITheme { get; } - - public GtkHostUIHandler(Window parent) - { - _parent = parent; - - HostUITheme = new GtkHostUITheme(parent); - } - - public bool DisplayMessageDialog(ControllerAppletUIArgs args) - { - string playerCount = args.PlayerCountMin == args.PlayerCountMax ? $"exactly {args.PlayerCountMin}" : $"{args.PlayerCountMin}-{args.PlayerCountMax}"; - - string message = $"Application requests {playerCount} player(s) with:\n\n" - + $"TYPES: {args.SupportedStyles}\n\n" - + $"PLAYERS: {string.Join(", ", args.SupportedPlayers)}\n\n" - + (args.IsDocked ? "Docked mode set. Handheld is also invalid.\n\n" : "") - + "Please reconfigure Input now and then press OK."; - - return DisplayMessageDialog("Controller Applet", message); - } - - public bool DisplayMessageDialog(string title, string message) - { - ManualResetEvent dialogCloseEvent = new(false); - - bool okPressed = false; - - Application.Invoke(delegate - { - MessageDialog msgDialog = null; - - try - { - msgDialog = new MessageDialog(_parent, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) - { - Title = title, - Text = message, - UseMarkup = true, - }; - - msgDialog.SetDefaultSize(400, 0); - - msgDialog.Response += (object o, ResponseArgs args) => - { - if (args.ResponseId == ResponseType.Ok) - { - okPressed = true; - } - - dialogCloseEvent.Set(); - msgDialog?.Dispose(); - }; - - msgDialog.Show(); - } - catch (Exception ex) - { - GtkDialog.CreateErrorDialog($"Error displaying Message Dialog: {ex}"); - - dialogCloseEvent.Set(); - } - }); - - dialogCloseEvent.WaitOne(); - - return okPressed; - } - - public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText) - { - ManualResetEvent dialogCloseEvent = new(false); - - bool okPressed = false; - bool error = false; - string inputText = args.InitialText ?? ""; - - Application.Invoke(delegate - { - try - { - var swkbdDialog = new SwkbdAppletDialog(_parent) - { - Title = "Software Keyboard", - Text = args.HeaderText, - SecondaryText = args.SubtitleText, - }; - - swkbdDialog.InputEntry.Text = inputText; - swkbdDialog.InputEntry.PlaceholderText = args.GuideText; - swkbdDialog.OkButton.Label = args.SubmitText; - - swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax); - swkbdDialog.SetInputValidation(args.KeyboardMode); - - ((MainWindow)_parent).RendererWidget.NpadManager.BlockInputUpdates(); - - if (swkbdDialog.Run() == (int)ResponseType.Ok) - { - inputText = swkbdDialog.InputEntry.Text; - okPressed = true; - } - - swkbdDialog.Dispose(); - } - catch (Exception ex) - { - error = true; - - GtkDialog.CreateErrorDialog($"Error displaying Software Keyboard: {ex}"); - } - finally - { - dialogCloseEvent.Set(); - } - }); - - dialogCloseEvent.WaitOne(); - ((MainWindow)_parent).RendererWidget.NpadManager.UnblockInputUpdates(); - - userText = error ? null : inputText; - - return error || okPressed; - } - - public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value) - { - device.Configuration.UserChannelPersistence.ExecuteProgram(kind, value); - ((MainWindow)_parent).RendererWidget?.Exit(); - } - - public bool DisplayErrorAppletDialog(string title, string message, string[] buttons) - { - ManualResetEvent dialogCloseEvent = new(false); - - bool showDetails = false; - - Application.Invoke(delegate - { - try - { - ErrorAppletDialog msgDialog = new(_parent, DialogFlags.DestroyWithParent, MessageType.Error, buttons) - { - Title = title, - Text = message, - UseMarkup = true, - WindowPosition = WindowPosition.CenterAlways, - }; - - msgDialog.SetDefaultSize(400, 0); - - msgDialog.Response += (object o, ResponseArgs args) => - { - if (buttons != null) - { - if (buttons.Length > 1) - { - if (args.ResponseId != (ResponseType)(buttons.Length - 1)) - { - showDetails = true; - } - } - } - - dialogCloseEvent.Set(); - msgDialog?.Dispose(); - }; - - msgDialog.Show(); - } - catch (Exception ex) - { - GtkDialog.CreateErrorDialog($"Error displaying ErrorApplet Dialog: {ex}"); - - dialogCloseEvent.Set(); - } - }); - - dialogCloseEvent.WaitOne(); - - return showDetails; - } - - public IDynamicTextInputHandler CreateDynamicTextInputHandler() - { - return new GtkDynamicTextInputHandler(_parent); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs b/src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs deleted file mode 100644 index 52d1123bb..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/GtkHostUITheme.cs +++ /dev/null @@ -1,90 +0,0 @@ -using Gtk; -using Ryujinx.HLE.UI; -using System.Diagnostics; - -namespace Ryujinx.UI.Applet -{ - internal class GtkHostUITheme : IHostUITheme - { - private const int RenderSurfaceWidth = 32; - private const int RenderSurfaceHeight = 32; - - public string FontFamily { get; private set; } - - public ThemeColor DefaultBackgroundColor { get; } - public ThemeColor DefaultForegroundColor { get; } - public ThemeColor DefaultBorderColor { get; } - public ThemeColor SelectionBackgroundColor { get; } - public ThemeColor SelectionForegroundColor { get; } - - public GtkHostUITheme(Window parent) - { - Entry entry = new(); - entry.SetStateFlags(StateFlags.Selected, true); - - // Get the font and some colors directly from GTK. - FontFamily = entry.PangoContext.FontDescription.Family; - - // Get foreground colors from the style context. - - var defaultForegroundColor = entry.StyleContext.GetColor(StateFlags.Normal); - var selectedForegroundColor = entry.StyleContext.GetColor(StateFlags.Selected); - - DefaultForegroundColor = new ThemeColor((float)defaultForegroundColor.Alpha, (float)defaultForegroundColor.Red, (float)defaultForegroundColor.Green, (float)defaultForegroundColor.Blue); - SelectionForegroundColor = new ThemeColor((float)selectedForegroundColor.Alpha, (float)selectedForegroundColor.Red, (float)selectedForegroundColor.Green, (float)selectedForegroundColor.Blue); - - ListBoxRow row = new(); - row.SetStateFlags(StateFlags.Selected, true); - - // Request the main thread to render some UI elements to an image to get an approximation for the color. - // NOTE (caian): This will only take the color of the top-left corner of the background, which may be incorrect - // if someone provides a custom style with a gradient or image. - - using (var surface = new Cairo.ImageSurface(Cairo.Format.Argb32, RenderSurfaceWidth, RenderSurfaceHeight)) - using (var context = new Cairo.Context(surface)) - { - context.SetSourceRGBA(1, 1, 1, 1); - context.Rectangle(0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - context.Fill(); - - // The background color must be from the main Window because entry uses a different color. - parent.StyleContext.RenderBackground(context, 0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - - DefaultBackgroundColor = ToThemeColor(surface.Data); - - context.SetSourceRGBA(1, 1, 1, 1); - context.Rectangle(0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - context.Fill(); - - // Use the background color of the list box row when selected as the text box frame color because they are the - // same in the default theme. - row.StyleContext.RenderBackground(context, 0, 0, RenderSurfaceWidth, RenderSurfaceHeight); - - DefaultBorderColor = ToThemeColor(surface.Data); - } - - // Use the border color as the text selection color. - SelectionBackgroundColor = DefaultBorderColor; - } - - private static ThemeColor ToThemeColor(byte[] data) - { - Debug.Assert(data.Length == 4 * RenderSurfaceWidth * RenderSurfaceHeight); - - // Take the center-bottom pixel of the surface. - int position = 4 * (RenderSurfaceWidth * (RenderSurfaceHeight - 1) + RenderSurfaceWidth / 2); - - if (position + 4 > data.Length) - { - return new ThemeColor(1, 0, 0, 0); - } - - float a = data[position + 3] / 255.0f; - float r = data[position + 2] / 255.0f; - float g = data[position + 1] / 255.0f; - float b = data[position + 0] / 255.0f; - - return new ThemeColor(a, r, g, b); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs b/src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs deleted file mode 100644 index 8045da91e..000000000 --- a/src/Ryujinx.Gtk3/UI/Applet/SwkbdAppletDialog.cs +++ /dev/null @@ -1,127 +0,0 @@ -using Gtk; -using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; -using System; -using System.Linq; - -namespace Ryujinx.UI.Applet -{ - public class SwkbdAppletDialog : MessageDialog - { - private int _inputMin; - private int _inputMax; -#pragma warning disable IDE0052 // Remove unread private member - private KeyboardMode _mode; -#pragma warning restore IDE0052 - - private string _validationInfoText = ""; - - private Predicate _checkLength = _ => true; - private Predicate _checkInput = _ => true; - - private readonly Label _validationInfo; - - public Entry InputEntry { get; } - public Button OkButton { get; } - public Button CancelButton { get; } - - public SwkbdAppletDialog(Window parent) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.None, null) - { - SetDefaultSize(300, 0); - - _validationInfo = new Label() - { - Visible = false, - }; - - InputEntry = new Entry() - { - Visible = true, - }; - - InputEntry.Activated += OnInputActivated; - InputEntry.Changed += OnInputChanged; - - OkButton = (Button)AddButton("OK", ResponseType.Ok); - CancelButton = (Button)AddButton("Cancel", ResponseType.Cancel); - - ((Box)MessageArea).PackEnd(_validationInfo, true, true, 0); - ((Box)MessageArea).PackEnd(InputEntry, true, true, 4); - } - - private void ApplyValidationInfo() - { - _validationInfo.Visible = !string.IsNullOrEmpty(_validationInfoText); - _validationInfo.Markup = _validationInfoText; - } - - public void SetInputLengthValidation(int min, int max) - { - _inputMin = Math.Min(min, max); - _inputMax = Math.Max(min, max); - - _validationInfo.Visible = false; - - if (_inputMin <= 0 && _inputMax == int.MaxValue) // Disable. - { - _validationInfo.Visible = false; - - _checkLength = _ => true; - } - else if (_inputMin > 0 && _inputMax == int.MaxValue) - { - _validationInfoText = $"Must be at least {_inputMin} characters long. "; - - _checkLength = length => _inputMin <= length; - } - else - { - _validationInfoText = $"Must be {_inputMin}-{_inputMax} characters long. "; - - _checkLength = length => _inputMin <= length && length <= _inputMax; - } - - ApplyValidationInfo(); - OnInputChanged(this, EventArgs.Empty); - } - - public void SetInputValidation(KeyboardMode mode) - { - _mode = mode; - - switch (mode) - { - case KeyboardMode.Numeric: - _validationInfoText += "Must be 0-9 or '.' only."; - _checkInput = text => text.All(NumericCharacterValidation.IsNumeric); - break; - case KeyboardMode.Alphabet: - _validationInfoText += "Must be non CJK-characters only."; - _checkInput = text => text.All(value => !CJKCharacterValidation.IsCJK(value)); - break; - case KeyboardMode.ASCII: - _validationInfoText += "Must be ASCII text only."; - _checkInput = text => text.All(char.IsAscii); - break; - default: - _checkInput = _ => true; - break; - } - - ApplyValidationInfo(); - OnInputChanged(this, EventArgs.Empty); - } - - private void OnInputActivated(object sender, EventArgs e) - { - if (OkButton.IsSensitive) - { - Respond(ResponseType.Ok); - } - } - - private void OnInputChanged(object sender, EventArgs e) - { - OkButton.Sensitive = _checkLength(InputEntry.Text.Length) && _checkInput(InputEntry.Text); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs deleted file mode 100644 index 5a8ca96a1..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/ButtonHelper.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Input; -using System; -using System.Collections.Generic; -using Key = Ryujinx.Common.Configuration.Hid.Key; -using StickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; - -namespace Ryujinx.UI.Helper -{ - public static class ButtonHelper - { - private static readonly Dictionary _keysMap = new() - { - { Key.Unknown, "Unknown" }, - { Key.ShiftLeft, "ShiftLeft" }, - { Key.ShiftRight, "ShiftRight" }, - { Key.ControlLeft, "CtrlLeft" }, - { Key.ControlRight, "CtrlRight" }, - { Key.AltLeft, OperatingSystem.IsMacOS() ? "OptLeft" : "AltLeft" }, - { Key.AltRight, OperatingSystem.IsMacOS() ? "OptRight" : "AltRight" }, - { Key.WinLeft, OperatingSystem.IsMacOS() ? "CmdLeft" : "WinLeft" }, - { Key.WinRight, OperatingSystem.IsMacOS() ? "CmdRight" : "WinRight" }, - { Key.Up, "Up" }, - { Key.Down, "Down" }, - { Key.Left, "Left" }, - { Key.Right, "Right" }, - { Key.Enter, "Enter" }, - { Key.Escape, "Escape" }, - { Key.Space, "Space" }, - { Key.Tab, "Tab" }, - { Key.BackSpace, "Backspace" }, - { Key.Insert, "Insert" }, - { Key.Delete, "Delete" }, - { Key.PageUp, "PageUp" }, - { Key.PageDown, "PageDown" }, - { Key.Home, "Home" }, - { Key.End, "End" }, - { Key.CapsLock, "CapsLock" }, - { Key.ScrollLock, "ScrollLock" }, - { Key.PrintScreen, "PrintScreen" }, - { Key.Pause, "Pause" }, - { Key.NumLock, "NumLock" }, - { Key.Clear, "Clear" }, - { Key.Keypad0, "Keypad0" }, - { Key.Keypad1, "Keypad1" }, - { Key.Keypad2, "Keypad2" }, - { Key.Keypad3, "Keypad3" }, - { Key.Keypad4, "Keypad4" }, - { Key.Keypad5, "Keypad5" }, - { Key.Keypad6, "Keypad6" }, - { Key.Keypad7, "Keypad7" }, - { Key.Keypad8, "Keypad8" }, - { Key.Keypad9, "Keypad9" }, - { Key.KeypadDivide, "KeypadDivide" }, - { Key.KeypadMultiply, "KeypadMultiply" }, - { Key.KeypadSubtract, "KeypadSubtract" }, - { Key.KeypadAdd, "KeypadAdd" }, - { Key.KeypadDecimal, "KeypadDecimal" }, - { Key.KeypadEnter, "KeypadEnter" }, - { Key.Number0, "0" }, - { Key.Number1, "1" }, - { Key.Number2, "2" }, - { Key.Number3, "3" }, - { Key.Number4, "4" }, - { Key.Number5, "5" }, - { Key.Number6, "6" }, - { Key.Number7, "7" }, - { Key.Number8, "8" }, - { Key.Number9, "9" }, - { Key.Tilde, "~" }, - { Key.Grave, "`" }, - { Key.Minus, "-" }, - { Key.Plus, "+" }, - { Key.BracketLeft, "[" }, - { Key.BracketRight, "]" }, - { Key.Semicolon, ";" }, - { Key.Quote, "'" }, - { Key.Comma, "," }, - { Key.Period, "." }, - { Key.Slash, "/" }, - { Key.BackSlash, "\\" }, - { Key.Unbound, "Unbound" }, - }; - - private static readonly Dictionary _gamepadInputIdMap = new() - { - { GamepadInputId.LeftStick, "LeftStick" }, - { GamepadInputId.RightStick, "RightStick" }, - { GamepadInputId.LeftShoulder, "LeftShoulder" }, - { GamepadInputId.RightShoulder, "RightShoulder" }, - { GamepadInputId.LeftTrigger, "LeftTrigger" }, - { GamepadInputId.RightTrigger, "RightTrigger" }, - { GamepadInputId.DpadUp, "DpadUp" }, - { GamepadInputId.DpadDown, "DpadDown" }, - { GamepadInputId.DpadLeft, "DpadLeft" }, - { GamepadInputId.DpadRight, "DpadRight" }, - { GamepadInputId.Minus, "Minus" }, - { GamepadInputId.Plus, "Plus" }, - { GamepadInputId.Guide, "Guide" }, - { GamepadInputId.Misc1, "Misc1" }, - { GamepadInputId.Paddle1, "Paddle1" }, - { GamepadInputId.Paddle2, "Paddle2" }, - { GamepadInputId.Paddle3, "Paddle3" }, - { GamepadInputId.Paddle4, "Paddle4" }, - { GamepadInputId.Touchpad, "Touchpad" }, - { GamepadInputId.SingleLeftTrigger0, "SingleLeftTrigger0" }, - { GamepadInputId.SingleRightTrigger0, "SingleRightTrigger0" }, - { GamepadInputId.SingleLeftTrigger1, "SingleLeftTrigger1" }, - { GamepadInputId.SingleRightTrigger1, "SingleRightTrigger1" }, - { GamepadInputId.Unbound, "Unbound" }, - }; - - private static readonly Dictionary _stickInputIdMap = new() - { - { StickInputId.Left, "StickLeft" }, - { StickInputId.Right, "StickRight" }, - { StickInputId.Unbound, "Unbound" }, - }; - - public static string ToString(Button button) - { - string keyString = ""; - - switch (button.Type) - { - case ButtonType.Key: - var key = button.AsHidType(); - - if (!_keysMap.TryGetValue(button.AsHidType(), out keyString)) - { - keyString = key.ToString(); - } - - break; - case ButtonType.GamepadButtonInputId: - var gamepadButton = button.AsHidType(); - - if (!_gamepadInputIdMap.TryGetValue(button.AsHidType(), out keyString)) - { - keyString = gamepadButton.ToString(); - } - - break; - case ButtonType.StickId: - var stickInput = button.AsHidType(); - - if (!_stickInputIdMap.TryGetValue(button.AsHidType(), out keyString)) - { - keyString = stickInput.ToString(); - } - - break; - } - - return keyString; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs deleted file mode 100644 index c2c32d3ae..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/MetalHelper.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Gdk; -using System; -using System.Runtime.InteropServices; -using System.Runtime.Versioning; - -namespace Ryujinx.UI.Helper -{ - public delegate void UpdateBoundsCallbackDelegate(Window window); - - [SupportedOSPlatform("macos")] - static partial class MetalHelper - { - private const string LibObjCImport = "/usr/lib/libobjc.A.dylib"; - - private readonly struct Selector - { - public readonly IntPtr NativePtr; - - public unsafe Selector(string value) - { - int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); - byte* data = stackalloc byte[size]; - - fixed (char* pValue = value) - { - System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); - } - - NativePtr = sel_registerName(data); - } - - public static implicit operator Selector(string value) => new(value); - } - - private static unsafe IntPtr GetClass(string value) - { - int size = System.Text.Encoding.UTF8.GetMaxByteCount(value.Length); - byte* data = stackalloc byte[size]; - - fixed (char* pValue = value) - { - System.Text.Encoding.UTF8.GetBytes(pValue, value.Length, data, size); - } - - return objc_getClass(data); - } - - private struct NsPoint - { - public double X; - public double Y; - - public NsPoint(double x, double y) - { - X = x; - Y = y; - } - } - - private struct NsRect - { - public NsPoint Pos; - public NsPoint Size; - - public NsRect(double x, double y, double width, double height) - { - Pos = new NsPoint(x, y); - Size = new NsPoint(width, height); - } - } - - public static IntPtr GetMetalLayer(Display display, Window window, out IntPtr nsView, out UpdateBoundsCallbackDelegate updateBounds) - { - nsView = gdk_quartz_window_get_nsview(window.Handle); - - // Create a new CAMetalLayer. - IntPtr layerClass = GetClass("CAMetalLayer"); - IntPtr metalLayer = IntPtr_objc_msgSend(layerClass, "alloc"); - objc_msgSend(metalLayer, "init"); - - // Create a child NSView to render into. - IntPtr nsViewClass = GetClass("NSView"); - IntPtr child = IntPtr_objc_msgSend(nsViewClass, "alloc"); - objc_msgSend(child, "init", new NsRect()); - - // Add it as a child. - objc_msgSend(nsView, "addSubview:", child); - - // Make its renderer our metal layer. - objc_msgSend(child, "setWantsLayer:", (byte)1); - objc_msgSend(child, "setLayer:", metalLayer); - objc_msgSend(metalLayer, "setContentsScale:", (double)display.GetMonitorAtWindow(window).ScaleFactor); - - // Set the frame position/location. - updateBounds = (Window window) => - { - window.GetPosition(out int x, out int y); - int width = window.Width; - int height = window.Height; - objc_msgSend(child, "setFrame:", new NsRect(x, y, width, height)); - }; - - updateBounds(window); - - return metalLayer; - } - - [LibraryImport(LibObjCImport)] - private static unsafe partial IntPtr sel_registerName(byte* data); - - [LibraryImport(LibObjCImport)] - private static unsafe partial IntPtr objc_getClass(byte* data); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, byte value); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, IntPtr value); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, NsRect point); - - [LibraryImport(LibObjCImport)] - private static partial void objc_msgSend(IntPtr receiver, Selector selector, double value); - - [LibraryImport(LibObjCImport, EntryPoint = "objc_msgSend")] - private static partial IntPtr IntPtr_objc_msgSend(IntPtr receiver, Selector selector); - - [LibraryImport("libgdk-3.0.dylib")] - private static partial IntPtr gdk_quartz_window_get_nsview(IntPtr gdkWindow); - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs deleted file mode 100644 index 3e3fbeaae..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/SortHelper.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common.Helper; -using System; - -namespace Ryujinx.UI.Helper -{ - static class SortHelper - { - public static int TimePlayedSort(ITreeModel model, TreeIter a, TreeIter b) - { - TimeSpan aTimeSpan = ValueFormatUtils.ParseTimeSpan(model.GetValue(a, 5).ToString()); - TimeSpan bTimeSpan = ValueFormatUtils.ParseTimeSpan(model.GetValue(b, 5).ToString()); - - return TimeSpan.Compare(aTimeSpan, bTimeSpan); - } - - public static int LastPlayedSort(ITreeModel model, TreeIter a, TreeIter b) - { - DateTime aDateTime = ValueFormatUtils.ParseDateTime(model.GetValue(a, 6).ToString()); - DateTime bDateTime = ValueFormatUtils.ParseDateTime(model.GetValue(b, 6).ToString()); - - return DateTime.Compare(aDateTime, bDateTime); - } - - public static int FileSizeSort(ITreeModel model, TreeIter a, TreeIter b) - { - long aSize = ValueFormatUtils.ParseFileSize(model.GetValue(a, 8).ToString()); - long bSize = ValueFormatUtils.ParseFileSize(model.GetValue(b, 8).ToString()); - - return aSize.CompareTo(bSize); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs b/src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs deleted file mode 100644 index e1fed1c4d..000000000 --- a/src/Ryujinx.Gtk3/UI/Helper/ThemeHelper.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Configuration; -using System.IO; - -namespace Ryujinx.UI.Helper -{ - static class ThemeHelper - { - public static void ApplyTheme() - { - if (!ConfigurationState.Instance.UI.EnableCustomTheme) - { - return; - } - - if (File.Exists(ConfigurationState.Instance.UI.CustomThemePath) && (Path.GetExtension(ConfigurationState.Instance.UI.CustomThemePath) == ".css")) - { - CssProvider cssProvider = new(); - - cssProvider.LoadFromPath(ConfigurationState.Instance.UI.CustomThemePath); - - StyleContext.AddProviderForScreen(Gdk.Screen.Default, cssProvider, 800); - } - else - { - Logger.Warning?.Print(LogClass.Application, $"The \"custom_theme_path\" section in \"{ReleaseInformation.ConfigName}\" contains an invalid path: \"{ConfigurationState.Instance.UI.CustomThemePath}\"."); - - ConfigurationState.Instance.UI.CustomThemePath.Value = ""; - ConfigurationState.Instance.UI.EnableCustomTheme.Value = false; - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.cs b/src/Ryujinx.Gtk3/UI/MainWindow.cs deleted file mode 100644 index b10dfe3f9..000000000 --- a/src/Ryujinx.Gtk3/UI/MainWindow.cs +++ /dev/null @@ -1,1993 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Common.Keys; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Audio.Backends.Dummy; -using Ryujinx.Audio.Backends.OpenAL; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Audio.Backends.SoundIo; -using Ryujinx.Audio.Integration; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.Logging; -using Ryujinx.Common.SystemInterop; -using Ryujinx.Cpu; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.HOS.SystemState; -using Ryujinx.Input.GTK3; -using Ryujinx.Input.HLE; -using Ryujinx.Input.SDL2; -using Ryujinx.Modules; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Applet; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Helper; -using Ryujinx.UI.Widgets; -using Ryujinx.UI.Windows; -using Silk.NET.Vulkan; -using SPB.Graphics.Vulkan; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; -using GUI = Gtk.Builder.ObjectAttribute; -using ShaderCacheLoadingState = Ryujinx.Graphics.Gpu.Shader.ShaderCacheState; - -namespace Ryujinx.UI -{ - public class MainWindow : Window - { - private readonly VirtualFileSystem _virtualFileSystem; - private readonly ContentManager _contentManager; - private readonly AccountManager _accountManager; - private readonly LibHacHorizonManager _libHacHorizonManager; - - private UserChannelPersistence _userChannelPersistence; - - private HLE.Switch _emulationContext; - - private WindowsMultimediaTimerResolution _windowsMultimediaTimerResolution; - - private readonly GtkHostUIHandler _uiHandler; - private readonly AutoResetEvent _deviceExitStatus; - private readonly ListStore _tableStore; - - private bool _updatingGameTable; - private bool _gameLoaded; - private bool _ending; - - private ApplicationData _currentApplicationData = null; - - private string _lastScannedAmiiboId = ""; - private bool _lastScannedAmiiboShowAll = false; - - public readonly ApplicationLibrary ApplicationLibrary; - public RendererWidgetBase RendererWidget; - public InputManager InputManager; - - public bool IsFocused; - -#pragma warning disable CS0169, CS0649, IDE0044, IDE0051 // Field is never assigned to, Add readonly modifier, Remove unused private member - - [GUI] public MenuItem ExitMenuItem; - [GUI] public MenuItem UpdateMenuItem; - [GUI] MenuBar _menuBar; - [GUI] Box _footerBox; - [GUI] Box _statusBar; - [GUI] MenuItem _optionMenu; - [GUI] MenuItem _manageUserProfiles; - [GUI] MenuItem _fileMenu; - [GUI] MenuItem _loadApplicationFile; - [GUI] MenuItem _loadApplicationFolder; - [GUI] MenuItem _appletMenu; - [GUI] MenuItem _actionMenu; - [GUI] MenuItem _pauseEmulation; - [GUI] MenuItem _resumeEmulation; - [GUI] MenuItem _stopEmulation; - [GUI] MenuItem _simulateWakeUpMessage; - [GUI] MenuItem _scanAmiibo; - [GUI] MenuItem _takeScreenshot; - [GUI] MenuItem _hideUI; - [GUI] MenuItem _fullScreen; - [GUI] CheckMenuItem _startFullScreen; - [GUI] CheckMenuItem _showConsole; - [GUI] CheckMenuItem _favToggle; - [GUI] MenuItem _firmwareInstallDirectory; - [GUI] MenuItem _firmwareInstallFile; - [GUI] MenuItem _fileTypesSubMenu; - [GUI] Label _fifoStatus; - [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] CheckMenuItem _nspShown; - [GUI] CheckMenuItem _pfs0Shown; - [GUI] CheckMenuItem _xciShown; - [GUI] CheckMenuItem _ncaShown; - [GUI] CheckMenuItem _nroShown; - [GUI] CheckMenuItem _nsoShown; - [GUI] Label _gpuBackend; - [GUI] Label _dockedMode; - [GUI] Label _aspectRatio; - [GUI] Label _gameStatus; - [GUI] TreeView _gameTable; - [GUI] TreeSelection _gameTableSelection; - [GUI] ScrolledWindow _gameTableWindow; - [GUI] Label _gpuName; - [GUI] Label _progressLabel; - [GUI] Label _firmwareVersionLabel; - [GUI] Gtk.ProgressBar _progressBar; - [GUI] Box _viewBox; - [GUI] Label _vSyncStatus; - [GUI] Label _volumeStatus; - [GUI] Box _listStatusBox; - [GUI] Label _loadingStatusLabel; - [GUI] Gtk.ProgressBar _loadingStatusBar; - -#pragma warning restore CS0649, IDE0044, CS0169, IDE0051 - - public MainWindow() : this(new Builder("Ryujinx.Gtk3.UI.MainWindow.glade")) { } - - private MainWindow(Builder builder) : base(builder.GetRawOwnedObject("_mainWin")) - { - builder.Autoconnect(this); - - // Apply custom theme if needed. - ThemeHelper.ApplyTheme(); - - SetWindowSizePosition(); - - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - Title = $"Ryujinx {Program.Version}"; - - // Hide emulation context status bar. - _statusBar.Hide(); - - // 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); - - _contentManager = new ContentManager(_virtualFileSystem); - _accountManager = new AccountManager(_libHacHorizonManager.RyujinxClient, CommandLineState.Profile); - _userChannelPersistence = new UserChannelPersistence(); - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - // Instantiate GUI objects. - ApplicationLibrary = new ApplicationLibrary(_virtualFileSystem, checkLevel) - { - DesiredLanguage = ConfigurationState.Instance.System.Language, - }; - _uiHandler = new GtkHostUIHandler(this); - _deviceExitStatus = new AutoResetEvent(false); - - WindowStateEvent += WindowStateEvent_Changed; - DeleteEvent += Window_Close; - FocusInEvent += MainWindow_FocusInEvent; - FocusOutEvent += MainWindow_FocusOutEvent; - - ApplicationLibrary.ApplicationAdded += Application_Added; - ApplicationLibrary.ApplicationCountUpdated += ApplicationCount_Updated; - - _fileMenu.StateChanged += FileMenu_StateChanged; - _actionMenu.StateChanged += ActionMenu_StateChanged; - _optionMenu.StateChanged += OptionMenu_StateChanged; - - _gameTable.ButtonReleaseEvent += Row_Clicked; - _fullScreen.Activated += FullScreen_Toggled; - - RendererWidgetBase.StatusUpdatedEvent += Update_StatusBar; - - ConfigurationState.Instance.System.IgnoreMissingServices.Event += UpdateIgnoreMissingServicesState; - ConfigurationState.Instance.Graphics.AspectRatio.Event += UpdateAspectRatioState; - ConfigurationState.Instance.System.EnableDockedMode.Event += UpdateDockedModeState; - ConfigurationState.Instance.System.AudioVolume.Event += UpdateAudioVolumeState; - - ConfigurationState.Instance.Multiplayer.Mode.Event += UpdateMultiplayerMode; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Event += UpdateMultiplayerLanInterfaceId; - - if (ConfigurationState.Instance.UI.StartFullscreen) - { - _startFullScreen.Active = true; - } - - _showConsole.Active = ConfigurationState.Instance.UI.ShowConsole.Value; - _showConsole.Visible = ConsoleHelper.SetConsoleWindowStateSupported; - - _actionMenu.Sensitive = false; - _pauseEmulation.Sensitive = false; - _resumeEmulation.Sensitive = false; - - _nspShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value; - _pfs0Shown.Active = ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value; - _xciShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value; - _ncaShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value; - _nroShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value; - _nsoShown.Active = ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value; - - _nspShown.Toggled += NSP_Shown_Toggled; - _pfs0Shown.Toggled += PFS0_Shown_Toggled; - _xciShown.Toggled += XCI_Shown_Toggled; - _ncaShown.Toggled += NCA_Shown_Toggled; - _nroShown.Toggled += NRO_Shown_Toggled; - _nsoShown.Toggled += NSO_Shown_Toggled; - - _fileTypesSubMenu.Visible = FileAssociationHelper.IsTypeAssociationSupported; - - 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; - } - - _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; - - _gameTable.Model = _tableStore = new ListStore( - typeof(bool), - typeof(Gdk.Pixbuf), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(string), - typeof(BlitStruct)); - - _tableStore.SetSortFunc(5, SortHelper.TimePlayedSort); - _tableStore.SetSortFunc(6, SortHelper.LastPlayedSort); - _tableStore.SetSortFunc(8, SortHelper.FileSizeSort); - - int columnId = ConfigurationState.Instance.UI.ColumnSort.SortColumnId; - bool ascending = ConfigurationState.Instance.UI.ColumnSort.SortAscending; - - _tableStore.SetSortColumnId(columnId, ascending ? SortType.Ascending : SortType.Descending); - - _gameTable.EnableSearch = true; - _gameTable.SearchColumn = 2; - _gameTable.SearchEqualFunc = (model, col, key, iter) => !((string)model.GetValue(iter, col)).Contains(key, StringComparison.InvariantCultureIgnoreCase); - - _hideUI.Label = _hideUI.Label.Replace("SHOWUIKEY", ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI.ToString()); - - UpdateColumns(); - - ConfigurationState.Instance.UI.GameDirs.Event += (sender, args) => - { - if (args.OldValue != args.NewValue) - { - UpdateGameTable(); - } - }; - - Task.Run(RefreshFirmwareLabel); - - InputManager = new InputManager(new GTK3KeyboardDriver(this), new SDL2GamepadDriver()); - } - - private void UpdateMultiplayerLanInterfaceId(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.MultiplayerLanInterfaceId = args.NewValue; - } - } - - private void UpdateMultiplayerMode(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.MultiplayerMode = args.NewValue; - } - } - - private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.IgnoreMissingServices = args.NewValue; - } - } - - private void UpdateAspectRatioState(object sender, ReactiveEventArgs args) - { - if (_emulationContext != null) - { - _emulationContext.Configuration.AspectRatio = args.NewValue; - } - } - - private void UpdateDockedModeState(object sender, ReactiveEventArgs e) - { - _emulationContext?.System.ChangeDockedModeState(e.NewValue); - } - - private void UpdateAudioVolumeState(object sender, ReactiveEventArgs e) - { - _emulationContext?.SetVolume(e.NewValue); - } - - private void WindowStateEvent_Changed(object o, WindowStateEventArgs args) - { - _fullScreen.Label = args.Event.NewWindowState.HasFlag(Gdk.WindowState.Fullscreen) ? "Exit Fullscreen" : "Enter Fullscreen"; - } - - private void MainWindow_FocusOutEvent(object o, FocusOutEventArgs args) - { - IsFocused = false; - } - - private void MainWindow_FocusInEvent(object o, FocusInEventArgs args) - { - IsFocused = true; - } - - private void UpdateColumns() - { - foreach (TreeViewColumn column in _gameTable.Columns) - { - _gameTable.RemoveColumn(column); - } - - CellRendererToggle favToggle = new(); - favToggle.Toggled += FavToggle_Toggled; - - 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); - } - - foreach (TreeViewColumn column in _gameTable.Columns) - { - 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; - } - } - } - - protected override void OnDestroyed() - { - InputManager.Dispose(); - } - - private void InitializeSwitchInstance() - { - _virtualFileSystem.ReloadKeySet(); - - IRenderer renderer; - - if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) - { - string preferredGpu = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - renderer = new Graphics.Vulkan.VulkanRenderer(Vk.GetApi(), CreateVulkanSurface, VulkanHelper.GetRequiredInstanceExtensions, preferredGpu); - } - else - { - renderer = new Graphics.OpenGL.OpenGLRenderer(); - } - - 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}"); - - IHardwareDeviceDriver deviceDriver = new DummyHardwareDeviceDriver(); - - if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SDL2) - { - if (SDL2HardwareDeviceDriver.IsSupported) - { - 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, trying to fall back to SoundIO."); - - 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."); - } - } - } - } - else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.SoundIo) - { - if (SoundIoHardwareDeviceDriver.IsSupported) - { - deviceDriver = new SoundIoHardwareDeviceDriver(); - } - else - { - 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."); - } - } - } - } - else if (ConfigurationState.Instance.System.AudioBackend.Value == AudioBackend.OpenAl) - { - if (OpenALHardwareDeviceDriver.IsSupported) - { - deviceDriver = new OpenALHardwareDeviceDriver(); - } - else - { - Logger.Warning?.Print(LogClass.Audio, "OpenAL 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 SoundIO."); - - 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."); - } - } - } - } - - var memoryConfiguration = ConfigurationState.Instance.System.ExpandRam.Value - ? HLE.MemoryConfiguration.MemoryConfiguration8GiB - : HLE.MemoryConfiguration.MemoryConfiguration4GiB; - - IntegrityCheckLevel fsIntegrityCheckLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks ? IntegrityCheckLevel.ErrorOnInvalid : IntegrityCheckLevel.None; - - HLE.HLEConfiguration configuration = new(_virtualFileSystem, - _libHacHorizonManager, - _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, - ConfigurationState.Instance.System.EnableInternetAccess, - fsIntegrityCheckLevel, - ConfigurationState.Instance.System.FsGlobalAccessLogMode, - ConfigurationState.Instance.System.SystemTimeOffset, - ConfigurationState.Instance.System.TimeZone, - ConfigurationState.Instance.System.MemoryManagerMode, - ConfigurationState.Instance.System.IgnoreMissingServices, - ConfigurationState.Instance.Graphics.AspectRatio, - ConfigurationState.Instance.System.AudioVolume, - ConfigurationState.Instance.System.UseHypervisor, - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, - ConfigurationState.Instance.Multiplayer.Mode); - - _emulationContext = new HLE.Switch(configuration); - } - - private SurfaceKHR CreateVulkanSurface(Instance instance, Vk vk) - { - return new SurfaceKHR((ulong)((VulkanRenderer)RendererWidget).CreateWindowSurface(instance.Handle)); - } - - private void SetupProgressUIHandlers() - { - if (_emulationContext.Processes.ActiveApplication.DiskCacheLoadState != null) - { - _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged -= ProgressHandler; - _emulationContext.Processes.ActiveApplication.DiskCacheLoadState.StateChanged += ProgressHandler; - } - - _emulationContext.Gpu.ShaderCacheStateChanged -= ProgressHandler; - _emulationContext.Gpu.ShaderCacheStateChanged += ProgressHandler; - } - - private void ProgressHandler(T state, int current, int total) where T : Enum - { - bool visible; - string label; - - switch (state) - { - case LoadState ptcState: - visible = ptcState != LoadState.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)}"); - } - - Application.Invoke(delegate - { - _loadingStatusLabel.Text = label; - _loadingStatusBar.Fraction = total > 0 ? (double)current / total : 0; - _loadingStatusBar.Visible = visible; - _loadingStatusLabel.Visible = visible; - }); - } - - public void UpdateGameTable() - { - if (_updatingGameTable || _gameLoaded) - { - return; - } - - _updatingGameTable = true; - - _tableStore.Clear(); - - Thread applicationLibraryThread = new(() => - { - ApplicationLibrary.DesiredLanguage = ConfigurationState.Instance.System.Language; - ApplicationLibrary.LoadApplications(ConfigurationState.Instance.UI.GameDirs); - - _updatingGameTable = false; - }) - { - Name = "GUI.ApplicationLibraryThread", - IsBackground = true, - }; - applicationLibraryThread.Start(); - } - - [Conditional("RELEASE")] - public void PerformanceCheck() - { - if (ConfigurationState.Instance.Logger.EnableTrace.Value) - { - MessageDialog debugWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) - { - Title = "Ryujinx - Warning", - 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?", - }; - - if (debugWarningDialog.Run() == (int)ResponseType.Yes) - { - ConfigurationState.Instance.Logger.EnableTrace.Value = false; - SaveConfig(); - } - - debugWarningDialog.Dispose(); - } - - if (!string.IsNullOrWhiteSpace(ConfigurationState.Instance.Graphics.ShadersDumpPath.Value)) - { - MessageDialog shadersDumpWarningDialog = new(this, DialogFlags.Modal, MessageType.Warning, ButtonsType.YesNo, null) - { - 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?", - }; - - if (shadersDumpWarningDialog.Run() == (int)ResponseType.Yes) - { - ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = ""; - SaveConfig(); - } - - shadersDumpWarningDialog.Dispose(); - } - } - - private bool LoadApplication(string path, ulong applicationId, bool isFirmwareTitle) - { - SystemVersion firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - if (!SetupValidator.CanStartApplication(_contentManager, path, out UserError userError)) - { - if (SetupValidator.CanFixStartApplication(_contentManager, path, userError, out firmwareVersion)) - { - string message = $"Would you like to install the firmware embedded in this game? (Firmware {firmwareVersion.VersionString})"; - - ResponseType responseDialog = (ResponseType)GtkDialog.CreateConfirmationDialog("No Firmware Installed", message).Run(); - - if (responseDialog != ResponseType.Yes || !SetupValidator.TryFixStartApplication(_contentManager, path, userError, out _)) - { - UserErrorDialog.CreateUserErrorDialog(userError); - - return false; - } - - // Tell the user that we installed a firmware for them. - - firmwareVersion = _contentManager.GetCurrentFirmwareVersion(); - - RefreshFirmwareLabel(); - - 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); - } - else - { - UserErrorDialog.CreateUserErrorDialog(userError); - - return false; - } - } - - Logger.Notice.Print(LogClass.Application, $"Using Firmware Version: {firmwareVersion?.VersionString}"); - - if (isFirmwareTitle) - { - Logger.Info?.Print(LogClass.Application, "Loading as Firmware Title (NCA)."); - - return _emulationContext.LoadNca(path); - } - - if (Directory.Exists(path)) - { - string[] romFsFiles = Directory.GetFiles(path, "*.istorage"); - - if (romFsFiles.Length == 0) - { - romFsFiles = Directory.GetFiles(path, "*.romfs"); - } - - if (romFsFiles.Length > 0) - { - Logger.Info?.Print(LogClass.Application, "Loading as cart with RomFS."); - - return _emulationContext.LoadCart(path, romFsFiles[0]); - } - - Logger.Info?.Print(LogClass.Application, "Loading as cart WITHOUT RomFS."); - - return _emulationContext.LoadCart(path); - } - - if (File.Exists(path)) - { - switch (System.IO.Path.GetExtension(path).ToLowerInvariant()) - { - case ".xci": - Logger.Info?.Print(LogClass.Application, "Loading as XCI."); - - return _emulationContext.LoadXci(path, applicationId); - case ".nca": - Logger.Info?.Print(LogClass.Application, "Loading as NCA."); - - return _emulationContext.LoadNca(path); - case ".nsp": - case ".pfs0": - Logger.Info?.Print(LogClass.Application, "Loading as NSP."); - - return _emulationContext.LoadNsp(path, applicationId); - default: - Logger.Info?.Print(LogClass.Application, "Loading as Homebrew."); - try - { - return _emulationContext.LoadProgram(path); - } - catch (ArgumentOutOfRangeException) - { - Logger.Error?.Print(LogClass.Application, "The specified file is not supported by Ryujinx."); - - return false; - } - } - } - - Logger.Warning?.Print(LogClass.Application, "Please specify a valid XCI/NCA/NSP/PFS0/NRO file."); - - return false; - } - - public void RunApplication(ApplicationData application, bool startFullscreen = false) - { - if (_gameLoaded) - { - GtkDialog.CreateInfoDialog("A game has already been loaded", "Please stop emulation or close the emulator before launching another game."); - } - else - { - PerformanceCheck(); - - Logger.RestartTime(); - - RendererWidget = CreateRendererWidget(); - - SwitchToRenderWidget(startFullscreen); - - InitializeSwitchInstance(); - - UpdateGraphicsConfig(); - - bool isFirmwareTitle = false; - - if (application.Path.StartsWith("@SystemContent")) - { - application.Path = VirtualFileSystem.SwitchPathToSystemPath(application.Path); - - isFirmwareTitle = true; - } - - if (!LoadApplication(application.Path, application.Id, isFirmwareTitle)) - { - _emulationContext.Dispose(); - SwitchToGameTable(); - - return; - } - - SetupProgressUIHandlers(); - - _currentApplicationData = application; - - _deviceExitStatus.Reset(); - - Thread windowThread = new(CreateGameWindow) - { - Name = "GUI.WindowThread", - }; - - windowThread.Start(); - - _gameLoaded = true; - _actionMenu.Sensitive = true; - UpdateMenuItem.Sensitive = false; - - _lastScannedAmiiboId = ""; - - _firmwareInstallFile.Sensitive = false; - _firmwareInstallDirectory.Sensitive = false; - - DiscordIntegrationModule.SwitchToPlayingState(_emulationContext.Processes.ActiveApplication.ProgramIdText, - _emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString()); - - ApplicationLibrary.LoadAndSaveMetaData(_emulationContext.Processes.ActiveApplication.ProgramIdText, appMetadata => - { - appMetadata.UpdatePreGame(); - }); - } - } - - private RendererWidgetBase CreateRendererWidget() - { - if (ConfigurationState.Instance.Graphics.GraphicsBackend == GraphicsBackend.Vulkan) - { - return new VulkanRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); - } - else - { - return new OpenGLRenderer(InputManager, ConfigurationState.Instance.Logger.GraphicsDebugLevel); - } - } - - private void SwitchToRenderWidget(bool startFullscreen = false) - { - _viewBox.Remove(_gameTableWindow); - RendererWidget.Expand = true; - _viewBox.Child = RendererWidget; - - RendererWidget.ShowAll(); - EditFooterForGameRenderer(); - - if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - ToggleExtraWidgets(false); - } - else if (startFullscreen || ConfigurationState.Instance.UI.StartFullscreen.Value) - { - FullScreen_Toggled(null, null); - } - } - - private void SwitchToGameTable() - { - if (Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - ToggleExtraWidgets(true); - } - - RendererWidget.Exit(); - - if (RendererWidget.Window != Window && RendererWidget.Window != null) - { - RendererWidget.Window.Dispose(); - } - - RendererWidget.Dispose(); - - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution?.Dispose(); - _windowsMultimediaTimerResolution = null; - } - - DisplaySleep.Restore(); - - _viewBox.Remove(RendererWidget); - _viewBox.Add(_gameTableWindow); - - _gameTableWindow.Expand = true; - - Window.Title = $"Ryujinx {Program.Version}"; - - _emulationContext = null; - _gameLoaded = false; - RendererWidget = null; - - DiscordIntegrationModule.SwitchToMainMenu(); - - RecreateFooterForMenu(); - - UpdateColumns(); - UpdateGameTable(); - - RefreshFirmwareLabel(); - HandleRelaunch(); - } - - private void CreateGameWindow() - { - if (OperatingSystem.IsWindows()) - { - _windowsMultimediaTimerResolution = new WindowsMultimediaTimerResolution(1); - } - - DisplaySleep.Prevent(); - - RendererWidget.Initialize(_emulationContext); - - RendererWidget.WaitEvent.WaitOne(); - - RendererWidget.Start(); - - _emulationContext.Dispose(); - _deviceExitStatus.Set(); - - // NOTE: Everything that is here will not be executed when you close the UI. - Application.Invoke(delegate - { - SwitchToGameTable(); - }); - } - - private void RecreateFooterForMenu() - { - _listStatusBox.Show(); - _statusBar.Hide(); - } - - private void EditFooterForGameRenderer() - { - _listStatusBox.Hide(); - _statusBar.Show(); - } - - public void ToggleExtraWidgets(bool show) - { - if (RendererWidget != null) - { - if (show) - { - _menuBar.ShowAll(); - _footerBox.Show(); - _statusBar.Show(); - } - else - { - _menuBar.Hide(); - _footerBox.Hide(); - } - } - } - - private void UpdateGameMetadata(string titleId) - { - if (_gameLoaded) - { - ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => - { - appMetadata.UpdatePostGame(); - }); - } - } - - public static void UpdateGraphicsConfig() - { - int resScale = ConfigurationState.Instance.Graphics.ResScale; - float resScaleCustom = ConfigurationState.Instance.Graphics.ResScaleCustom; - - Graphics.Gpu.GraphicsConfig.ResScale = (resScale == -1) ? resScaleCustom : resScale; - Graphics.Gpu.GraphicsConfig.MaxAnisotropy = ConfigurationState.Instance.Graphics.MaxAnisotropy; - Graphics.Gpu.GraphicsConfig.ShadersDumpPath = ConfigurationState.Instance.Graphics.ShadersDumpPath; - Graphics.Gpu.GraphicsConfig.EnableShaderCache = ConfigurationState.Instance.Graphics.EnableShaderCache; - Graphics.Gpu.GraphicsConfig.EnableTextureRecompression = ConfigurationState.Instance.Graphics.EnableTextureRecompression; - Graphics.Gpu.GraphicsConfig.EnableMacroHLE = ConfigurationState.Instance.Graphics.EnableMacroHLE; - } - - public void UpdateInternetAccess() - { - if (_gameLoaded) - { - _emulationContext.Configuration.EnableInternetAccess = ConfigurationState.Instance.System.EnableInternetAccess.Value; - } - } - - public static void SaveConfig() - { - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - } - - private void End() - { - if (_ending) - { - return; - } - - _ending = true; - - if (_emulationContext != null) - { - UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText); - - if (RendererWidget != null) - { - // We tell the widget that we are exiting. - RendererWidget.Exit(); - - // Wait for the other thread to dispose the HLE context before exiting. - _deviceExitStatus.WaitOne(); - RendererWidget.Dispose(); - } - } - - Dispose(); - - Program.Exit(); - Application.Quit(); - } - - // - // Events - // - private void Application_Added(object sender, ApplicationAddedEventArgs args) - { - Application.Invoke(delegate - { - _tableStore.AppendValues( - args.AppData.Favorite, - new Gdk.Pixbuf(args.AppData.Icon, 75, 75), - $"{args.AppData.Name}\n{args.AppData.IdString.ToUpper()}", - args.AppData.Developer, - args.AppData.Version, - args.AppData.TimePlayedString, - args.AppData.LastPlayedString, - args.AppData.FileExtension, - args.AppData.FileSizeString, - args.AppData.Path, - args.AppData.ControlHolder); - }); - } - - private void ApplicationCount_Updated(object sender, ApplicationCountUpdatedEventArgs args) - { - Application.Invoke(delegate - { - _progressLabel.Text = $"{args.NumAppsLoaded}/{args.NumAppsFound} Games Loaded"; - float barValue = 0; - - if (args.NumAppsFound != 0) - { - barValue = (float)args.NumAppsLoaded / args.NumAppsFound; - } - - _progressBar.Fraction = barValue; - - // Reset the vertical scrollbar to the top when titles finish loading - if (args.NumAppsLoaded == args.NumAppsFound) - { - _gameTableWindow.Vadjustment.Value = 0; - } - }); - } - - private void Update_StatusBar(object sender, StatusUpdatedEventArgs args) - { - Application.Invoke(delegate - { - _gameStatus.Text = args.GameStatus; - _fifoStatus.Text = args.FifoStatus; - _gpuName.Text = args.GpuName; - _dockedMode.Text = args.DockedMode; - _aspectRatio.Text = args.AspectRatio; - _gpuBackend.Text = args.GpuBackend; - _volumeStatus.Text = GetVolumeLabelText(args.Volume); - - 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)); - } - }); - } - - private void FavToggle_Toggled(object sender, ToggledArgs args) - { - _tableStore.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - - string titleId = _tableStore.GetValue(treeIter, 2).ToString().Split("\n")[1].ToLower(); - bool newToggleValue = !(bool)_tableStore.GetValue(treeIter, 0); - - _tableStore.SetValue(treeIter, 0, newToggleValue); - - ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => - { - appMetadata.Favorite = newToggleValue; - }); - } - - 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(); - } - - private void Row_Activated(object sender, RowActivatedArgs args) - { - _gameTableSelection.GetSelected(out TreeIter treeIter); - - ApplicationData application = new() - { - Favorite = (bool)_tableStore.GetValue(treeIter, 0), - Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0], - Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber), - Developer = (string)_tableStore.GetValue(treeIter, 3), - Version = (string)_tableStore.GetValue(treeIter, 4), - TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)), - LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)), - FileExtension = (string)_tableStore.GetValue(treeIter, 7), - FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)), - Path = (string)_tableStore.GetValue(treeIter, 9), - ControlHolder = (BlitStruct)_tableStore.GetValue(treeIter, 10), - }; - - RunApplication(application); - } - - private void VSyncStatus_Clicked(object sender, ButtonReleaseEventArgs args) - { - _emulationContext.EnableDeviceVsync = !_emulationContext.EnableDeviceVsync; - - Logger.Info?.Print(LogClass.Application, $"VSync toggled to: {_emulationContext.EnableDeviceVsync}"); - } - - private void DockedMode_Clicked(object sender, ButtonReleaseEventArgs args) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = !ConfigurationState.Instance.System.EnableDockedMode.Value; - } - - private static 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); - } - } - } - - private void AspectRatio_Clicked(object sender, ButtonReleaseEventArgs args) - { - AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value; - - ConfigurationState.Instance.Graphics.AspectRatio.Value = ((int)aspectRatio + 1) > Enum.GetNames().Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1; - } - - private void Row_Clicked(object sender, ButtonReleaseEventArgs args) - { - if (args.Event.Button != 3 /* Right Click */) - { - return; - } - - _gameTableSelection.GetSelected(out TreeIter treeIter); - - if (treeIter.UserData == IntPtr.Zero) - { - return; - } - - ApplicationData application = new() - { - Favorite = (bool)_tableStore.GetValue(treeIter, 0), - Name = ((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[0], - Id = ulong.Parse(((string)_tableStore.GetValue(treeIter, 2)).Split('\n')[1], NumberStyles.HexNumber), - Developer = (string)_tableStore.GetValue(treeIter, 3), - Version = (string)_tableStore.GetValue(treeIter, 4), - TimePlayed = ValueFormatUtils.ParseTimeSpan((string)_tableStore.GetValue(treeIter, 5)), - LastPlayed = ValueFormatUtils.ParseDateTime((string)_tableStore.GetValue(treeIter, 6)), - FileExtension = (string)_tableStore.GetValue(treeIter, 7), - FileSize = ValueFormatUtils.ParseFileSize((string)_tableStore.GetValue(treeIter, 8)), - Path = (string)_tableStore.GetValue(treeIter, 9), - ControlHolder = (BlitStruct)_tableStore.GetValue(treeIter, 10), - }; - - _ = new GameTableContextMenu(this, _virtualFileSystem, _accountManager, _libHacHorizonManager.RyujinxClient, application); - } - - private void Load_Application_File(object sender, EventArgs args) - { - using FileChooserNative fileChooser = new("Choose the file to open", this, FileChooserAction.Open, "Open", "Cancel"); - - FileFilter filter = new() - { - Name = "Switch Executables", - }; - filter.AddPattern("*.xci"); - filter.AddPattern("*.nsp"); - filter.AddPattern("*.pfs0"); - filter.AddPattern("*.nca"); - filter.AddPattern("*.nro"); - filter.AddPattern("*.nso"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - if (ApplicationLibrary.TryGetApplicationsFromFile(fileChooser.Filename, - out List applications)) - { - RunApplication(applications[0]); - } - else - { - GtkDialog.CreateErrorDialog("No applications found in selected file."); - } - } - } - - private void Load_Application_Folder(object sender, EventArgs args) - { - using FileChooserNative fileChooser = new("Choose the folder to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - ApplicationData applicationData = new() - { - Name = System.IO.Path.GetFileNameWithoutExtension(fileChooser.Filename), - Path = fileChooser.Filename, - }; - - RunApplication(applicationData); - } - } - - 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) - { - string contentPath = _contentManager.GetInstalledContentPath(0x0100000000001009, StorageId.BuiltInSystem, NcaContentType.Program); - - ApplicationData applicationData = new() - { - Name = "miiEdit", - Id = 0x0100000000001009ul, - Path = contentPath, - }; - - RunApplication(applicationData); - } - - private void Open_Ryu_Folder(object sender, EventArgs args) - { - OpenHelper.OpenFolder(AppDataManager.BaseDirPath); - } - - private void OpenLogsFolder_Pressed(object sender, EventArgs args) - { - string logPath = AppDataManager.GetOrCreateLogsDir(); - if (!string.IsNullOrEmpty(logPath)) - { - OpenHelper.OpenFolder(logPath); - } - } - - private void Exit_Pressed(object sender, EventArgs args) - { - if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - SaveWindowSizePosition(); - End(); - } - } - - private void Window_Close(object sender, DeleteEventArgs args) - { - if (!_gameLoaded || !ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - SaveWindowSizePosition(); - End(); - } - else - { - args.RetVal = true; - } - } - - private void SetWindowSizePosition() - { - DefaultWidth = ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth; - DefaultHeight = ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight; - - Move(ConfigurationState.Instance.UI.WindowStartup.WindowPositionX, ConfigurationState.Instance.UI.WindowStartup.WindowPositionY); - - if (ConfigurationState.Instance.UI.WindowStartup.WindowMaximized) - { - Maximize(); - } - } - - private void SaveWindowSizePosition() - { - GetSize(out int windowWidth, out int windowHeight); - GetPosition(out int windowXPos, out int windowYPos); - - ConfigurationState.Instance.UI.WindowStartup.WindowMaximized.Value = IsMaximized; - ConfigurationState.Instance.UI.WindowStartup.WindowSizeWidth.Value = windowWidth; - ConfigurationState.Instance.UI.WindowStartup.WindowSizeHeight.Value = windowHeight; - ConfigurationState.Instance.UI.WindowStartup.WindowPositionX.Value = windowXPos; - ConfigurationState.Instance.UI.WindowStartup.WindowPositionY.Value = windowYPos; - - SaveConfig(); - } - - private void StopEmulation_Pressed(object sender, EventArgs args) - { - if (_emulationContext != null) - { - UpdateGameMetadata(_emulationContext.Processes.ActiveApplication.ProgramIdText); - } - - _pauseEmulation.Sensitive = false; - _resumeEmulation.Sensitive = false; - UpdateMenuItem.Sensitive = true; - RendererWidget?.Exit(); - } - - private void PauseEmulation_Pressed(object sender, EventArgs args) - { - _pauseEmulation.Sensitive = false; - _resumeEmulation.Sensitive = true; - _emulationContext.System.TogglePauseEmulation(true); - Title = TitleHelper.ActiveApplicationTitle(_emulationContext.Processes.ActiveApplication, Program.Version, "Paused"); - Logger.Info?.Print(LogClass.Emulation, "Emulation was paused"); - } - - private void ResumeEmulation_Pressed(object sender, EventArgs args) - { - _pauseEmulation.Sensitive = true; - _resumeEmulation.Sensitive = false; - _emulationContext.System.TogglePauseEmulation(false); - Title = TitleHelper.ActiveApplicationTitle(_emulationContext.Processes.ActiveApplication, Program.Version); - Logger.Info?.Print(LogClass.Emulation, "Emulation was resumed"); - } - - public void ActivatePauseMenu() - { - _pauseEmulation.Sensitive = true; - _resumeEmulation.Sensitive = false; - } - - public void TogglePause() - { - _pauseEmulation.Sensitive ^= true; - _resumeEmulation.Sensitive ^= true; - _emulationContext.System.TogglePauseEmulation(_resumeEmulation.Sensitive); - } - - private void Installer_File_Pressed(object o, EventArgs args) - { - FileChooserNative fileChooser = new("Choose the firmware file to open", this, FileChooserAction.Open, "Open", "Cancel"); - - FileFilter filter = new() - { - Name = "Switch Firmware Files", - }; - filter.AddPattern("*.zip"); - filter.AddPattern("*.xci"); - - fileChooser.AddFilter(filter); - - HandleInstallerDialog(fileChooser); - } - - private void Installer_Directory_Pressed(object o, EventArgs args) - { - FileChooserNative directoryChooser = new("Choose the firmware directory to open", this, FileChooserAction.SelectFolder, "Open", "Cancel"); - - HandleInstallerDialog(directoryChooser); - } - - private void HandleInstallerDialog(FileChooserNative fileChooser) - { - if (fileChooser.Run() == (int)ResponseType.Accept) - { - try - { - string filename = fileChooser.Filename; - - fileChooser.Dispose(); - - SystemVersion firmwareVersion = _contentManager.VerifyFirmwarePackage(filename); - - if (firmwareVersion is null) - { - GtkDialog.CreateErrorDialog($"A valid system firmware was not found in {filename}."); - - return; - } - - string dialogTitle = $"Install Firmware {firmwareVersion.VersionString}"; - - SystemVersion currentVersion = _contentManager.GetCurrentFirmwareVersion(); - - string dialogMessage = $"System version {firmwareVersion.VersionString} will be installed."; - - if (currentVersion != null) - { - dialogMessage += $"\n\nThis will replace the current system version {currentVersion.VersionString}. "; - } - - dialogMessage += "\n\nDo you want to continue?"; - - ResponseType responseInstallDialog = (ResponseType)GtkDialog.CreateConfirmationDialog(dialogTitle, dialogMessage).Run(); - - MessageDialog waitingDialog = GtkDialog.CreateWaitingDialog(dialogTitle, "Installing firmware..."); - - if (responseInstallDialog == ResponseType.Yes) - { - Logger.Info?.Print(LogClass.Application, $"Installing firmware {firmwareVersion.VersionString}"); - - Thread thread = new(() => - { - Application.Invoke(delegate - { - waitingDialog.Run(); - - }); - - try - { - _contentManager.InstallFirmware(filename); - - Application.Invoke(delegate - { - waitingDialog.Dispose(); - - string message = $"System version {firmwareVersion.VersionString} successfully installed."; - - GtkDialog.CreateInfoDialog(dialogTitle, message); - Logger.Info?.Print(LogClass.Application, message); - - // Purge Applet Cache. - - DirectoryInfo miiEditorCacheFolder = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, "0100000000001009", "cache")); - - if (miiEditorCacheFolder.Exists) - { - miiEditorCacheFolder.Delete(true); - } - }); - } - catch (Exception ex) - { - Application.Invoke(delegate - { - waitingDialog.Dispose(); - - GtkDialog.CreateErrorDialog(ex.Message); - }); - } - finally - { - RefreshFirmwareLabel(); - } - }) - { - Name = "GUI.FirmwareInstallerThread", - }; - thread.Start(); - } - } - catch (MissingKeyException ex) - { - Logger.Error?.Print(LogClass.Application, ex.ToString()); - UserErrorDialog.CreateUserErrorDialog(UserError.FirmwareParsingFailed); - } - catch (Exception ex) - { - GtkDialog.CreateErrorDialog(ex.Message); - } - } - else - { - fileChooser.Dispose(); - } - } - - private void RefreshFirmwareLabel() - { - SystemVersion currentFirmware = _contentManager.GetCurrentFirmwareVersion(); - - Application.Invoke(delegate - { - _firmwareVersionLabel.Text = currentFirmware != null ? currentFirmware.VersionString : "0.0.0"; - }); - } - - private void InstallFileTypes_Pressed(object sender, EventArgs e) - { - if (FileAssociationHelper.Install()) - { - GtkDialog.CreateInfoDialog("Install file types", "File types successfully installed!"); - } - else - { - GtkDialog.CreateErrorDialog("Failed to install file types."); - } - } - - private void UninstallFileTypes_Pressed(object sender, EventArgs e) - { - if (FileAssociationHelper.Uninstall()) - { - GtkDialog.CreateInfoDialog("Uninstall file types", "File types successfully uninstalled!"); - } - else - { - GtkDialog.CreateErrorDialog("Failed to uninstall file types."); - } - } - - private void HandleRelaunch() - { - if (_userChannelPersistence.PreviousIndex != -1 && _userChannelPersistence.ShouldRestart) - { - _userChannelPersistence.ShouldRestart = false; - - RunApplication(_currentApplicationData); - } - else - { - // otherwise, clear state. - _userChannelPersistence = new UserChannelPersistence(); - _currentApplicationData = null; - _actionMenu.Sensitive = false; - _firmwareInstallFile.Sensitive = true; - _firmwareInstallDirectory.Sensitive = true; - } - } - - private void FullScreen_Toggled(object sender, EventArgs args) - { - if (!Window.State.HasFlag(Gdk.WindowState.Fullscreen)) - { - Fullscreen(); - - ToggleExtraWidgets(false); - } - else - { - Unfullscreen(); - - ToggleExtraWidgets(true); - } - } - - private void StartFullScreen_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.StartFullscreen.Value = _startFullScreen.Active; - - SaveConfig(); - } - - private void ShowConsole_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShowConsole.Value = _showConsole.Active; - - SaveConfig(); - } - - private void OptionMenu_StateChanged(object o, StateChangedArgs args) - { - _manageUserProfiles.Sensitive = _emulationContext == null; - } - - private void Settings_Pressed(object sender, EventArgs args) - { - SettingsWindow settingsWindow = new(this, _virtualFileSystem, _contentManager); - - settingsWindow.SetSizeRequest((int)(settingsWindow.DefaultWidth * Program.WindowScaleFactor), (int)(settingsWindow.DefaultHeight * Program.WindowScaleFactor)); - settingsWindow.Show(); - } - - private void HideUI_Pressed(object sender, EventArgs args) - { - ToggleExtraWidgets(false); - } - - private void ManageCheats_Pressed(object sender, EventArgs args) - { - var window = new CheatWindow( - _virtualFileSystem, - _emulationContext.Processes.ActiveApplication.ProgramId, - _emulationContext.Processes.ActiveApplication.ApplicationControlProperties - .Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(), - _currentApplicationData.Path); - - window.Destroyed += CheatWindow_Destroyed; - window.Show(); - } - - private void CheatWindow_Destroyed(object sender, EventArgs e) - { - _emulationContext.EnableCheats(); - (sender as CheatWindow).Destroyed -= CheatWindow_Destroyed; - } - - private void ManageUserProfiles_Pressed(object sender, EventArgs args) - { - UserProfilesManagerWindow userProfilesManagerWindow = new(_accountManager, _contentManager, _virtualFileSystem); - - userProfilesManagerWindow.SetSizeRequest((int)(userProfilesManagerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(userProfilesManagerWindow.DefaultHeight * Program.WindowScaleFactor)); - userProfilesManagerWindow.Show(); - } - - private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args) - { - _emulationContext?.System.SimulateWakeUpMessage(); - } - - private void ActionMenu_StateChanged(object o, StateChangedArgs args) - { - _scanAmiibo.Sensitive = _emulationContext != null && _emulationContext.System.SearchingForAmiibo(out int _); - _takeScreenshot.Sensitive = _emulationContext != null; - } - - private void Scan_Amiibo(object sender, EventArgs args) - { - if (_emulationContext.System.SearchingForAmiibo(out int deviceId)) - { - AmiiboWindow amiiboWindow = new() - { - LastScannedAmiiboShowAll = _lastScannedAmiiboShowAll, - LastScannedAmiiboId = _lastScannedAmiiboId, - DeviceId = deviceId, - TitleId = _emulationContext.Processes.ActiveApplication.ProgramIdText.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."); - } - } - - private void Take_Screenshot(object sender, EventArgs args) - { - if (_emulationContext != null && RendererWidget != null) - { - RendererWidget.ScreenshotRequested = true; - } - } - - 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); - } - } - - private void Update_Pressed(object sender, EventArgs args) - { - if (Updater.CanUpdate(true)) - { - Updater.BeginParse(this, true).ContinueWith(task => - { - Logger.Error?.Print(LogClass.Application, $"Updater error: {task.Exception}"); - }, TaskContinuationOptions.OnlyOnFaulted); - } - } - - private void About_Pressed(object sender, EventArgs args) - { - AboutWindow aboutWindow = new(); - - aboutWindow.SetSizeRequest((int)(aboutWindow.DefaultWidth * Program.WindowScaleFactor), (int)(aboutWindow.DefaultHeight * Program.WindowScaleFactor)); - aboutWindow.Show(); - } - - private void Fav_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.FavColumn.Value = _favToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Icon_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.IconColumn.Value = _iconToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void App_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.AppColumn.Value = _appToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Developer_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.DevColumn.Value = _developerToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Version_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.VersionColumn.Value = _versionToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void TimePlayed_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.TimePlayedColumn.Value = _timePlayedToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void LastPlayed_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.LastPlayedColumn.Value = _lastPlayedToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void FileExt_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.FileExtColumn.Value = _fileExtToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void FileSize_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.FileSizeColumn.Value = _fileSizeToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void Path_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.GuiColumns.PathColumn.Value = _pathToggle.Active; - - SaveConfig(); - UpdateColumns(); - } - - private void NSP_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NSP.Value = _nspShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void PFS0_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.PFS0.Value = _pfs0Shown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void XCI_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.XCI.Value = _xciShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void NCA_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NCA.Value = _ncaShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void NRO_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NRO.Value = _nroShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void NSO_Shown_Toggled(object sender, EventArgs args) - { - ConfigurationState.Instance.UI.ShownFileTypes.NSO.Value = _nsoShown.Active; - - SaveConfig(); - UpdateGameTable(); - } - - private void RefreshList_Pressed(object sender, ButtonReleaseEventArgs args) - { - UpdateGameTable(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.glade b/src/Ryujinx.Gtk3/UI/MainWindow.glade deleted file mode 100644 index d1b6872a9..000000000 --- a/src/Ryujinx.Gtk3/UI/MainWindow.glade +++ /dev/null @@ -1,1006 +0,0 @@ - - - - - - False - Ryujinx - center - - - True - False - vertical - - - True - False - - - True - False - File - True - - - True - False - - - True - False - Open a file explorer to choose a Switch compatible file to load - Load Application from File - True - - - - - - True - False - Open a file explorer to choose a Switch compatible, unpacked application to load - Load Unpacked Game - True - - - - - - True - False - Load Applet - True - - - True - False - - - True - False - Open Mii Editor Applet in Standalone mode - Mii Editor - True - - - - - - - - - - True - False - - - - - True - False - Open Ryujinx filesystem folder - Open Ryujinx Folder - True - - - - - - True - False - Opens the folder where logs are written to. - Open Logs Folder - True - - - - - - True - False - - - - - True - False - Exit Ryujinx - Exit - True - - - - - - - - - - True - False - Options - True - - - True - False - - - True - False - Enter Fullscreen - True - - - - - True - False - Start Games in Fullscreen Mode - True - - - - - - True - False - Show Log Console - True - - - - - - True - False - - - - - True - False - Select which GUI columns to enable - Enable GUI Columns - True - - - True - False - - - True - False - Enable or Disable Favorite Games Column in the game list - Enable Favorite Games Column - True - - - - - True - False - Enable or Disable Icon Column in the game list - Enable Icon Column - True - - - - - True - False - Enable or Disable Title Name/ID Column in the game list - Enable Title Name/ID Column - True - - - - - True - False - Enable or Disable Developer Column in the game list - Enable Developer Column - True - - - - - True - False - Enable or Disable Version Column in the game list - Enable Version Column - True - - - - - True - False - Enable or Disable Time Played Column in the game list - Enable Time Played Column - True - - - - - True - False - Enable or Disable Last Played Column in the game list - Enable Last Played Column - True - - - - - True - False - Enable or Disable file extension column in the game list - Enable File Ext Column - True - - - - - True - False - Enable or Disable File Size Column in the game list - Enable File Size Column - True - - - - - True - False - Enable or Disable Path Column in the game list - Enable Path Column - True - - - - - - - - - True - False - Select which file types to show - Show File Types - True - - - True - False - - - True - False - Shows .NSP files in the games list - .NSP - True - - - - - True - False - Shows .PFS0 files in the games list - .PFS0 - True - - - - - True - False - Shows .XCI files in the games list - .XCI - True - - - - - True - False - Shows .NCA files in the games list - .NCA - True - - - - - True - False - Shows .NRO files in the games list - .NRO - True - - - - - True - False - Shows .NSO files in the games list - .NSO - True - - - - - - - - - True - False - - - - - True - False - Open settings window - Settings - True - - - - - - True - False - Open User Profiles Manager window - Manage User Profiles - True - - - - - - - - - - True - False - Actions - True - - - True - False - - - True - False - Pause emulation - Pause Emulation - True - - - - - - True - False - Resume emulation - Resume Emulation - True - - - - - - True - False - Stop emulation of the current game and return to game selection - Stop Emulation - True - - - - - - True - False - - - - - True - False - Simulate a Wake-up Message - Simulate Wake-up Message - True - - - - - - True - False - Scan an Amiibo - Scan an Amiibo - True - - - - - - True - False - Take a screenshot - Take Screenshot - - - - - - True - False - Hide UI (SHOWUIKEY to show) - True - - - - - - True - False - Manage Cheats - - - - - - - - - - True - False - Tools - True - - - True - False - - - True - False - Install Firmware - True - - - True - False - - - True - False - Install a firmware from XCI or ZIP - True - - - - - - True - False - Install a firmware from a directory - True - - - - - - - - - - True - False - Manage file types - True - - - True - False - - - True - False - Install file types - - - - - - True - False - Uninstall file types - - - - - - - - - - - - - - True - False - Help - True - - - True - False - - - True - False - Check for updates to Ryujinx - Check for Updates - True - - - - - - True - False - - - - - True - False - Open about window - About - True - - - - - - - - - - False - True - 0 - - - - - True - False - vertical - - - True - False - vertical - - - True - True - in - - - True - True - True - True - - - - - - - - - True - True - 0 - - - - - True - True - 0 - - - - - 19 - True - False - - - True - False - - - True - False - 5 - - - - RefreshList - True - False - gtk-refresh - - - - - False - False - 0 - - - - - True - False - 10 - 5 - 2 - 2 - 0/0 Games Loaded - - - False - True - 1 - - - - - 200 - True - False - start - 10 - 5 - 6 - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - - - True - False - - - - True - False - start - 5 - 5 - VSync - - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - True - False - - - - True - False - start - 5 - 5 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - - True - False - - - - True - False - start - 5 - 5 - - - - - False - True - 4 - - - - - True - False - - - False - True - 5 - - - - - True - False - - - - True - False - start - 5 - 5 - - - - - False - True - 6 - - - - - True - False - - - False - True - 7 - - - - - True - False - start - 5 - 5 - - - False - True - 8 - - - - - True - False - - - False - True - 9 - - - - - True - False - start - 5 - 5 - - - False - True - 10 - - - - - True - False - - - False - True - 11 - - - - - True - False - start - 5 - 5 - - - False - True - 12 - - - - - True - False - - - False - True - 13 - - - - - True - False - start - 5 - 5 - - - True - True - 14 - - - - - True - True - 1 - - - - - True - False - 5 - - - True - False - System Version - - - False - True - 0 - - - - - 50 - True - False - 5 - 5 - - - False - True - end - 1 - - - - - False - True - end - 4 - - - - - False - 5 - 5 - 0/0 - - - False - True - 11 - - - - - 200 - False - 5 - 5 - 6 - - - False - True - 12 - - - - - False - True - 1 - - - - - True - True - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs b/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs deleted file mode 100644 index 1fdabc754..000000000 --- a/src/Ryujinx.Gtk3/UI/OpenGLRenderer.cs +++ /dev/null @@ -1,142 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Input.HLE; -using SPB.Graphics; -using SPB.Graphics.Exceptions; -using SPB.Graphics.OpenGL; -using SPB.Platform; -using SPB.Platform.GLX; -using SPB.Platform.WGL; -using SPB.Windowing; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.UI -{ - public partial class OpenGLRenderer : RendererWidgetBase - { - private readonly GraphicsDebugLevel _glLogLevel; - - private bool _initializedOpenGL; - - private OpenGLContextBase _openGLContext; - private SwappableNativeWindowBase _nativeWindow; - - public OpenGLRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) - { - _glLogLevel = glLogLevel; - } - - protected override bool OnDrawn(Cairo.Context cr) - { - if (!_initializedOpenGL) - { - IntializeOpenGL(); - } - - return true; - } - - private void IntializeOpenGL() - { - _nativeWindow = RetrieveNativeWindow(); - - Window.EnsureNative(); - - _openGLContext = PlatformHelper.CreateOpenGLContext(GetGraphicsMode(), 3, 3, _glLogLevel == GraphicsDebugLevel.None ? OpenGLContextFlags.Compat : OpenGLContextFlags.Compat | OpenGLContextFlags.Debug); - _openGLContext.Initialize(_nativeWindow); - _openGLContext.MakeCurrent(_nativeWindow); - - // Release the GL exclusivity that SPB gave us as we aren't going to use it in GTK Thread. - _openGLContext.MakeCurrent(null); - - WaitEvent.Set(); - - _initializedOpenGL = true; - } - - private SwappableNativeWindowBase RetrieveNativeWindow() - { - if (OperatingSystem.IsWindows()) - { - IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); - - return new WGLWindow(new NativeHandle(windowHandle)); - } - else if (OperatingSystem.IsLinux()) - { - IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); - IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - - return new GLXWindow(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); - } - - throw new NotImplementedException(); - } - - [LibraryImport("libgdk-3-0.dll")] - private static partial IntPtr gdk_win32_window_get_handle(IntPtr d); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); - - private static FramebufferFormat GetGraphicsMode() - { - return Environment.OSVersion.Platform == PlatformID.Unix ? new FramebufferFormat(new ColorFormat(8, 8, 8, 0), 16, 0, ColorFormat.Zero, 0, 2, false) : FramebufferFormat.Default; - } - - public override void InitializeRenderer() - { - // First take exclusivity on the OpenGL context. - ((Graphics.OpenGL.OpenGLRenderer)Renderer).InitializeBackgroundContext(SPBOpenGLContext.CreateBackgroundContext(_openGLContext)); - - _openGLContext.MakeCurrent(_nativeWindow); - - GL.ClearColor(0, 0, 0, 1.0f); - GL.Clear(ClearBufferMask.ColorBufferBit); - SwapBuffers(); - } - - public override void SwapBuffers() - { - _nativeWindow.SwapBuffers(); - } - - protected override string GetGpuBackendName() - { - return "OpenGL"; - } - - protected override void Dispose(bool disposing) - { - // Try to bind the OpenGL context before calling the shutdown event. - try - { - _openGLContext?.MakeCurrent(_nativeWindow); - } - catch (ContextException e) - { - Logger.Warning?.Print(LogClass.UI, $"Failed to bind OpenGL context: {e}"); - } - - Device?.DisposeGpu(); - NpadManager.Dispose(); - - // Unbind context and destroy everything. - try - { - _openGLContext?.MakeCurrent(null); - } - catch (ContextException e) - { - Logger.Warning?.Print(LogClass.UI, $"Failed to unbind OpenGL context: {e}"); - } - - _openGLContext?.Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs b/src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs deleted file mode 100644 index 1224ccfe0..000000000 --- a/src/Ryujinx.Gtk3/UI/OpenToolkitBindingsContext.cs +++ /dev/null @@ -1,20 +0,0 @@ -using SPB.Graphics; -using System; - -namespace Ryujinx.UI -{ - public class OpenToolkitBindingsContext : OpenTK.IBindingsContext - { - private readonly IBindingsContext _bindingContext; - - public OpenToolkitBindingsContext(IBindingsContext bindingsContext) - { - _bindingContext = bindingsContext; - } - - public IntPtr GetProcAddress(string procName) - { - return _bindingContext.GetProcAddress(procName); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs b/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs deleted file mode 100644 index 12139e87d..000000000 --- a/src/Ryujinx.Gtk3/UI/RendererWidgetBase.cs +++ /dev/null @@ -1,813 +0,0 @@ -using Gdk; -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.Graphics.GAL; -using Ryujinx.Graphics.GAL.Multithreading; -using Ryujinx.Graphics.Gpu; -using Ryujinx.Input; -using Ryujinx.Input.GTK3; -using Ryujinx.Input.HLE; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Widgets; -using SkiaSharp; -using System; -using System.Diagnostics; -using System.IO; -using System.Runtime.InteropServices; -using System.Threading; -using System.Threading.Tasks; -using Key = Ryujinx.Input.Key; -using ScalingFilter = Ryujinx.Graphics.GAL.ScalingFilter; -using Switch = Ryujinx.HLE.Switch; - -namespace Ryujinx.UI -{ - public abstract class RendererWidgetBase : DrawingArea - { - private const int SwitchPanelWidth = 1280; - private const int SwitchPanelHeight = 720; - private const int TargetFps = 60; - private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping. - private const float VolumeDelta = 0.05f; - - public ManualResetEvent WaitEvent { get; set; } - public NpadManager NpadManager { get; } - public TouchScreenManager TouchScreenManager { get; } - public Switch Device { get; private set; } - public IRenderer Renderer { get; private set; } - - public bool ScreenshotRequested { get; set; } - protected int WindowWidth { get; private set; } - protected int WindowHeight { get; private set; } - - public static event EventHandler StatusUpdatedEvent; - - private bool _isActive; - private bool _isStopped; - - private bool _toggleFullscreen; - private bool _toggleDockedMode; - - private readonly long _ticksPerFrame; - - private long _ticks = 0; - private float _newVolume; - - private readonly Stopwatch _chrono; - - private KeyboardHotkeyState _prevHotkeyState; - - private readonly ManualResetEvent _exitEvent; - private readonly ManualResetEvent _gpuDoneEvent; - - private readonly CancellationTokenSource _gpuCancellationTokenSource; - - // Hide Cursor - const int CursorHideIdleTime = 5; // seconds - private static readonly Cursor _invisibleCursor = new(Display.Default, CursorType.BlankCursor); - private long _lastCursorMoveTime; - private HideCursorMode _hideCursorMode; - private readonly InputManager _inputManager; - private readonly IKeyboard _keyboardInterface; - private readonly GraphicsDebugLevel _glLogLevel; - private string _gpuBackendName; - private string _gpuDriverName; - private bool _isMouseInClient; - - public RendererWidgetBase(InputManager inputManager, GraphicsDebugLevel glLogLevel) - { - var mouseDriver = new GTK3MouseDriver(this); - - _inputManager = inputManager; - _inputManager.SetMouseDriver(mouseDriver); - NpadManager = _inputManager.CreateNpadManager(); - TouchScreenManager = _inputManager.CreateTouchScreenManager(); - _keyboardInterface = (IKeyboard)_inputManager.KeyboardDriver.GetGamepad("0"); - - WaitEvent = new ManualResetEvent(false); - - _glLogLevel = glLogLevel; - - Destroyed += Renderer_Destroyed; - - _chrono = new Stopwatch(); - - _ticksPerFrame = Stopwatch.Frequency / TargetFps; - - AddEvents((int)(EventMask.ButtonPressMask - | EventMask.ButtonReleaseMask - | EventMask.PointerMotionMask - | EventMask.ScrollMask - | EventMask.EnterNotifyMask - | EventMask.LeaveNotifyMask - | EventMask.KeyPressMask - | EventMask.KeyReleaseMask)); - - _exitEvent = new ManualResetEvent(false); - _gpuDoneEvent = new ManualResetEvent(false); - - _gpuCancellationTokenSource = new CancellationTokenSource(); - - _hideCursorMode = ConfigurationState.Instance.HideCursor; - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - - ConfigurationState.Instance.HideCursor.Event += HideCursorStateChanged; - ConfigurationState.Instance.Graphics.AntiAliasing.Event += UpdateAnriAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event += UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event += UpdateScalingFilterLevel; - } - - private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs e) - { - Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - private void UpdateScalingFilter(object sender, ReactiveEventArgs e) - { - Renderer.Window.SetScalingFilter((ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - } - - public abstract void InitializeRenderer(); - - public abstract void SwapBuffers(); - - protected abstract string GetGpuBackendName(); - - private string GetGpuDriverName() - { - return Renderer.GetHardwareInfo().GpuDriver; - } - - private void HideCursorStateChanged(object sender, ReactiveEventArgs state) - { - Application.Invoke(delegate - { - _hideCursorMode = state.NewValue; - - switch (_hideCursorMode) - { - case HideCursorMode.Never: - Window.Cursor = null; - break; - case HideCursorMode.OnIdle: - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - break; - case HideCursorMode.Always: - Window.Cursor = _invisibleCursor; - break; - default: - throw new ArgumentOutOfRangeException(nameof(state)); - } - }); - } - - private void Renderer_Destroyed(object sender, EventArgs e) - { - ConfigurationState.Instance.HideCursor.Event -= HideCursorStateChanged; - ConfigurationState.Instance.Graphics.AntiAliasing.Event -= UpdateAnriAliasing; - ConfigurationState.Instance.Graphics.ScalingFilter.Event -= UpdateScalingFilter; - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Event -= UpdateScalingFilterLevel; - - NpadManager.Dispose(); - Dispose(); - } - - private void UpdateAnriAliasing(object sender, ReactiveEventArgs e) - { - Renderer?.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)e.NewValue); - } - - protected override bool OnMotionNotifyEvent(EventMotion evnt) - { - if (_hideCursorMode == HideCursorMode.OnIdle) - { - _lastCursorMoveTime = Stopwatch.GetTimestamp(); - } - - if (ConfigurationState.Instance.Hid.EnableMouse) - { - Window.Cursor = _invisibleCursor; - } - - _isMouseInClient = true; - - return false; - } - - protected override bool OnEnterNotifyEvent(EventCrossing evnt) - { - Window.Cursor = ConfigurationState.Instance.Hid.EnableMouse ? _invisibleCursor : null; - - _isMouseInClient = true; - - return base.OnEnterNotifyEvent(evnt); - } - - protected override bool OnLeaveNotifyEvent(EventCrossing evnt) - { - Window.Cursor = null; - - _isMouseInClient = false; - - return base.OnLeaveNotifyEvent(evnt); - } - - protected override void OnGetPreferredHeight(out int minimumHeight, out int naturalHeight) - { - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - // If the monitor is at least 1080p, use the Switch panel size as minimal size. - if (monitor.Geometry.Height >= 1080) - { - minimumHeight = SwitchPanelHeight; - } - // Otherwise, we default minimal size to 480p 16:9. - else - { - minimumHeight = 480; - } - - naturalHeight = minimumHeight; - } - - protected override void OnGetPreferredWidth(out int minimumWidth, out int naturalWidth) - { - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - // If the monitor is at least 1080p, use the Switch panel size as minimal size. - if (monitor.Geometry.Height >= 1080) - { - minimumWidth = SwitchPanelWidth; - } - // Otherwise, we default minimal size to 480p 16:9. - else - { - minimumWidth = 854; - } - - naturalWidth = minimumWidth; - } - - protected override bool OnConfigureEvent(EventConfigure evnt) - { - bool result = base.OnConfigureEvent(evnt); - - Gdk.Monitor monitor = Display.GetMonitorAtWindow(Window); - - WindowWidth = evnt.Width * monitor.ScaleFactor; - WindowHeight = evnt.Height * monitor.ScaleFactor; - - Renderer?.Window?.SetSize(WindowWidth, WindowHeight); - - return result; - } - - private void HandleScreenState(KeyboardStateSnapshot keyboard) - { - bool toggleFullscreen = keyboard.IsPressed(Key.F11) - || ((keyboard.IsPressed(Key.AltLeft) - || keyboard.IsPressed(Key.AltRight)) - && keyboard.IsPressed(Key.Enter)) - || keyboard.IsPressed(Key.Escape); - - bool fullScreenToggled = ParentWindow.State.HasFlag(WindowState.Fullscreen); - - if (toggleFullscreen != _toggleFullscreen) - { - if (toggleFullscreen) - { - if (fullScreenToggled) - { - ParentWindow.Unfullscreen(); - (Toplevel as MainWindow)?.ToggleExtraWidgets(true); - } - else - { - if (keyboard.IsPressed(Key.Escape)) - { - if (!ConfigurationState.Instance.ShowConfirmExit || GtkDialog.CreateExitDialog()) - { - Exit(); - } - } - else - { - ParentWindow.Fullscreen(); - (Toplevel as MainWindow)?.ToggleExtraWidgets(false); - } - } - } - } - - _toggleFullscreen = toggleFullscreen; - - bool toggleDockedMode = keyboard.IsPressed(Key.F9); - - if (toggleDockedMode != _toggleDockedMode) - { - if (toggleDockedMode) - { - ConfigurationState.Instance.System.EnableDockedMode.Value = - !ConfigurationState.Instance.System.EnableDockedMode.Value; - } - } - - _toggleDockedMode = toggleDockedMode; - - if (_isMouseInClient) - { - if (ConfigurationState.Instance.Hid.EnableMouse.Value) - { - Window.Cursor = _invisibleCursor; - } - else - { - switch (_hideCursorMode) - { - case HideCursorMode.OnIdle: - long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime; - Window.Cursor = (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency) ? _invisibleCursor : null; - break; - case HideCursorMode.Always: - Window.Cursor = _invisibleCursor; - break; - case HideCursorMode.Never: - Window.Cursor = null; - break; - } - } - } - } - - public void Initialize(Switch device) - { - Device = device; - - IRenderer renderer = Device.Gpu.Renderer; - - if (renderer is ThreadedRenderer tr) - { - renderer = tr.BaseRenderer; - } - - Renderer = renderer; - Renderer?.Window?.SetSize(WindowWidth, WindowHeight); - - if (Renderer != null) - { - Renderer.ScreenCaptured += Renderer_ScreenCaptured; - } - - NpadManager.Initialize(device, ConfigurationState.Instance.Hid.InputConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - TouchScreenManager.Initialize(device); - } - - private unsafe void Renderer_ScreenCaptured(object sender, ScreenCaptureImageInfo e) - { - if (e.Data.Length > 0 && e.Height > 0 && e.Width > 0) - { - Task.Run(() => - { - lock (this) - { - string applicationName = Device.Processes.ActiveApplication.Name; - string sanitizedApplicationName = FileSystemUtils.SanitizeFileName(applicationName); - DateTime currentTime = DateTime.Now; - - string filename = $"{sanitizedApplicationName}_{currentTime.Year}-{currentTime.Month:D2}-{currentTime.Day:D2}_{currentTime.Hour:D2}-{currentTime.Minute:D2}-{currentTime.Second:D2}.png"; - - string directory = AppDataManager.Mode switch - { - AppDataManager.LaunchMode.Portable or AppDataManager.LaunchMode.Custom => System.IO.Path.Combine(AppDataManager.BaseDirPath, "screenshots"), - _ => System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures), "Ryujinx"), - }; - - string path = System.IO.Path.Combine(directory, filename); - - try - { - Directory.CreateDirectory(directory); - } - catch (Exception ex) - { - Logger.Error?.Print(LogClass.Application, $"Failed to create directory at path {directory}. Error : {ex.GetType().Name}", "Screenshot"); - - return; - } - - var colorType = e.IsBgra ? SKColorType.Bgra8888 : SKColorType.Rgba8888; - using var image = new SKBitmap(new SKImageInfo(e.Width, e.Height, colorType, SKAlphaType.Premul)); - - Marshal.Copy(e.Data, 0, image.GetPixels(), e.Data.Length); - using var surface = SKSurface.Create(image.Info); - var canvas = surface.Canvas; - - if (e.FlipX || e.FlipY) - { - canvas.Clear(SKColors.Transparent); - - float scaleX = e.FlipX ? -1 : 1; - float scaleY = e.FlipY ? -1 : 1; - - var matrix = SKMatrix.CreateScale(scaleX, scaleY, image.Width / 2f, image.Height / 2f); - - canvas.SetMatrix(matrix); - } - canvas.DrawBitmap(image, new SKPoint()); - - surface.Flush(); - using var snapshot = surface.Snapshot(); - using var encoded = snapshot.Encode(SKEncodedImageFormat.Png, 80); - using var file = File.OpenWrite(path); - encoded.SaveTo(file); - - image.Dispose(); - - Logger.Notice.Print(LogClass.Application, $"Screenshot saved to {path}", "Screenshot"); - } - }); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Screenshot is empty. Size : {e.Data.Length} bytes. Resolution : {e.Width}x{e.Height}", "Screenshot"); - } - } - - public void Render() - { - Gtk.Window parent = Toplevel as Gtk.Window; - parent.Present(); - - InitializeRenderer(); - - Device.Gpu.Renderer.Initialize(_glLogLevel); - - Renderer.Window.SetAntiAliasing((Graphics.GAL.AntiAliasing)ConfigurationState.Instance.Graphics.AntiAliasing.Value); - Renderer.Window.SetScalingFilter((Graphics.GAL.ScalingFilter)ConfigurationState.Instance.Graphics.ScalingFilter.Value); - Renderer.Window.SetScalingFilterLevel(ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value); - - _gpuBackendName = GetGpuBackendName(); - _gpuDriverName = GetGpuDriverName(); - - Device.Gpu.Renderer.RunLoop(() => - { - Device.Gpu.SetGpuThread(); - Device.Gpu.InitializeShaderCache(_gpuCancellationTokenSource.Token); - - Renderer.Window.ChangeVSyncMode(Device.EnableDeviceVsync); - - (Toplevel as MainWindow)?.ActivatePauseMenu(); - - while (_isActive) - { - if (_isStopped) - { - return; - } - - _ticks += _chrono.ElapsedTicks; - - _chrono.Restart(); - - if (Device.WaitFifo()) - { - Device.Statistics.RecordFifoStart(); - Device.ProcessFrame(); - Device.Statistics.RecordFifoEnd(); - } - - while (Device.ConsumeFrameAvailable()) - { - Device.PresentFrame(SwapBuffers); - } - - if (_ticks >= _ticksPerFrame) - { - string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? "Docked" : "Handheld"; - float scale = GraphicsConfig.ResScale; - if (scale != 1) - { - dockedMode += $" ({scale}x)"; - } - - StatusUpdatedEvent?.Invoke(this, new StatusUpdatedEventArgs( - Device.EnableDeviceVsync, - Device.GetVolume(), - _gpuBackendName, - dockedMode, - ConfigurationState.Instance.Graphics.AspectRatio.Value.ToText(), - $"Game: {Device.Statistics.GetGameFrameRate():00.00} FPS ({Device.Statistics.GetGameFrameTime():00.00} ms)", - $"FIFO: {Device.Statistics.GetFifoPercent():0.00} %", - $"GPU: {_gpuDriverName}")); - - _ticks = Math.Min(_ticks - _ticksPerFrame, _ticksPerFrame); - } - } - - // Make sure all commands in the run loop are fully executed before leaving the loop. - if (Device.Gpu.Renderer is ThreadedRenderer threaded) - { - threaded.FlushThreadedCommands(); - } - - _gpuDoneEvent.Set(); - }); - } - - public void Start() - { - _chrono.Restart(); - - _isActive = true; - - Gtk.Window parent = Toplevel as Gtk.Window; - - Application.Invoke(delegate - { - parent.Present(); - - var activeProcess = Device.Processes.ActiveApplication; - - parent.Title = TitleHelper.ActiveApplicationTitle(activeProcess, Program.Version); - }); - - Thread renderLoopThread = new(Render) - { - Name = "GUI.RenderLoop", - }; - renderLoopThread.Start(); - - Thread nvidiaStutterWorkaround = null; - if (Renderer is Graphics.OpenGL.OpenGLRenderer) - { - nvidiaStutterWorkaround = new Thread(NvidiaStutterWorkaround) - { - Name = "GUI.NvidiaStutterWorkaround", - }; - nvidiaStutterWorkaround.Start(); - } - - MainLoop(); - - // NOTE: The render loop is allowed to stay alive until the renderer itself is disposed, as it may handle resource dispose. - // We only need to wait for all commands submitted during the main gpu loop to be processed. - _gpuDoneEvent.WaitOne(); - _gpuDoneEvent.Dispose(); - nvidiaStutterWorkaround?.Join(); - - Exit(); - } - - public void Exit() - { - TouchScreenManager?.Dispose(); - NpadManager?.Dispose(); - - if (_isStopped) - { - return; - } - - _gpuCancellationTokenSource.Cancel(); - - _isStopped = true; - - if (_isActive) - { - _isActive = false; - - _exitEvent.WaitOne(); - _exitEvent.Dispose(); - } - } - - private void NvidiaStutterWorkaround() - { - while (_isActive) - { - // When NVIDIA Threaded Optimization is on, the driver will snapshot all threads in the system whenever the application creates any new ones. - // The ThreadPool has something called a "GateThread" which terminates itself after some inactivity. - // However, it immediately starts up again, since the rules regarding when to terminate and when to start differ. - // This creates a new thread every second or so. - // The main problem with this is that the thread snapshot can take 70ms, is on the OpenGL thread and will delay rendering any graphics. - // This is a little over budget on a frame time of 16ms, so creates a large stutter. - // The solution is to keep the ThreadPool active so that it never has a reason to terminate the GateThread. - - // TODO: This should be removed when the issue with the GateThread is resolved. - - ThreadPool.QueueUserWorkItem((state) => { }); - Thread.Sleep(300); - } - } - - public void MainLoop() - { - while (_isActive) - { - UpdateFrame(); - - // Polling becomes expensive if it's not slept - Thread.Sleep(1); - } - - _exitEvent.Set(); - } - - private bool UpdateFrame() - { - if (!_isActive) - { - return true; - } - - if (_isStopped) - { - return false; - } - - if ((Toplevel as MainWindow).IsFocused) - { - Application.Invoke(delegate - { - KeyboardStateSnapshot keyboard = _keyboardInterface.GetKeyboardStateSnapshot(); - - HandleScreenState(keyboard); - - if (keyboard.IsPressed(Key.Delete)) - { - if (!ParentWindow.State.HasFlag(WindowState.Fullscreen)) - { - Device.Processes.ActiveApplication.DiskCacheLoadState?.Cancel(); - } - } - }); - } - - NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - - if ((Toplevel as MainWindow).IsFocused) - { - KeyboardHotkeyState currentHotkeyState = GetHotkeyState(); - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleVSync)) - { - Device.EnableDeviceVsync = !Device.EnableDeviceVsync; - } - - if ((currentHotkeyState.HasFlag(KeyboardHotkeyState.Screenshot) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.Screenshot)) || ScreenshotRequested) - { - ScreenshotRequested = false; - - Renderer.Screenshot(); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ShowUI) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ShowUI)) - { - (Toplevel as MainWindow).ToggleExtraWidgets(true); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.Pause) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.Pause)) - { - (Toplevel as MainWindow)?.TogglePause(); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ToggleMute) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ToggleMute)) - { - if (Device.IsAudioMuted()) - { - Device.SetVolume(ConfigurationState.Instance.System.AudioVolume); - } - else - { - Device.SetVolume(0); - } - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleUp) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleUp)) - { - GraphicsConfig.ResScale = GraphicsConfig.ResScale % MaxResolutionScale + 1; - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleDown) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.ResScaleDown)) - { - GraphicsConfig.ResScale = - (MaxResolutionScale + GraphicsConfig.ResScale - 2) % MaxResolutionScale + 1; - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeUp)) - { - _newVolume = MathF.Round((Device.GetVolume() + VolumeDelta), 2); - Device.SetVolume(_newVolume); - } - - if (currentHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown) && - !_prevHotkeyState.HasFlag(KeyboardHotkeyState.VolumeDown)) - { - _newVolume = MathF.Round((Device.GetVolume() - VolumeDelta), 2); - Device.SetVolume(_newVolume); - } - - _prevHotkeyState = currentHotkeyState; - } - - // Touchscreen - bool hasTouch = false; - - // Get screen touch position - if ((Toplevel as MainWindow).IsFocused && !ConfigurationState.Instance.Hid.EnableMouse) - { - hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as GTK3MouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()); - } - - if (!hasTouch) - { - TouchScreenManager.Update(false); - } - - Device.Hid.DebugPad.Update(); - - return true; - } - - [Flags] - private enum KeyboardHotkeyState - { - None = 0, - ToggleVSync = 1 << 0, - Screenshot = 1 << 1, - ShowUI = 1 << 2, - Pause = 1 << 3, - ToggleMute = 1 << 4, - ResScaleUp = 1 << 5, - ResScaleDown = 1 << 6, - VolumeUp = 1 << 7, - VolumeDown = 1 << 8, - } - - private KeyboardHotkeyState GetHotkeyState() - { - KeyboardHotkeyState state = KeyboardHotkeyState.None; - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleVsync)) - { - state |= KeyboardHotkeyState.ToggleVSync; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Screenshot)) - { - state |= KeyboardHotkeyState.Screenshot; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ShowUI)) - { - state |= KeyboardHotkeyState.ShowUI; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.Pause)) - { - state |= KeyboardHotkeyState.Pause; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ToggleMute)) - { - state |= KeyboardHotkeyState.ToggleMute; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleUp)) - { - state |= KeyboardHotkeyState.ResScaleUp; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.ResScaleDown)) - { - state |= KeyboardHotkeyState.ResScaleDown; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeUp)) - { - state |= KeyboardHotkeyState.VolumeUp; - } - - if (_keyboardInterface.IsPressed((Key)ConfigurationState.Instance.Hid.Hotkeys.Value.VolumeDown)) - { - state |= KeyboardHotkeyState.VolumeDown; - } - - return state; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs b/src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs deleted file mode 100644 index 97feb4345..000000000 --- a/src/Ryujinx.Gtk3/UI/SPBOpenGLContext.cs +++ /dev/null @@ -1,49 +0,0 @@ -using OpenTK.Graphics.OpenGL; -using Ryujinx.Graphics.OpenGL; -using SPB.Graphics; -using SPB.Graphics.OpenGL; -using SPB.Platform; -using SPB.Windowing; - -namespace Ryujinx.UI -{ - class SPBOpenGLContext : IOpenGLContext - { - private readonly OpenGLContextBase _context; - private readonly NativeWindowBase _window; - - private SPBOpenGLContext(OpenGLContextBase context, NativeWindowBase window) - { - _context = context; - _window = window; - } - - public void Dispose() - { - _context.Dispose(); - _window.Dispose(); - } - - public void MakeCurrent() - { - _context.MakeCurrent(_window); - } - - public bool HasContext() => _context.IsCurrent; - - public static SPBOpenGLContext CreateBackgroundContext(OpenGLContextBase sharedContext) - { - OpenGLContextBase context = PlatformHelper.CreateOpenGLContext(FramebufferFormat.Default, 3, 3, OpenGLContextFlags.Compat, true, sharedContext); - NativeWindowBase window = PlatformHelper.CreateOpenGLWindow(FramebufferFormat.Default, 0, 0, 100, 100); - - context.Initialize(window); - context.MakeCurrent(window); - - GL.LoadBindings(new OpenToolkitBindingsContext(context)); - - context.MakeCurrent(null); - - return new SPBOpenGLContext(context, window); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs b/src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs deleted file mode 100644 index db467ebfc..000000000 --- a/src/Ryujinx.Gtk3/UI/StatusUpdatedEventArgs.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace Ryujinx.UI -{ - public class StatusUpdatedEventArgs : EventArgs - { - public bool VSyncEnabled; - public float Volume; - public string DockedMode; - public string AspectRatio; - public string GameStatus; - public string FifoStatus; - public string GpuName; - public string GpuBackend; - - public StatusUpdatedEventArgs(bool vSyncEnabled, float volume, string gpuBackend, string dockedMode, string aspectRatio, string gameStatus, string fifoStatus, string gpuName) - { - VSyncEnabled = vSyncEnabled; - Volume = volume; - GpuBackend = gpuBackend; - DockedMode = dockedMode; - AspectRatio = aspectRatio; - GameStatus = gameStatus; - FifoStatus = fifoStatus; - GpuName = gpuName; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/VulkanRenderer.cs b/src/Ryujinx.Gtk3/UI/VulkanRenderer.cs deleted file mode 100644 index eefc56999..000000000 --- a/src/Ryujinx.Gtk3/UI/VulkanRenderer.cs +++ /dev/null @@ -1,93 +0,0 @@ -using Gdk; -using Ryujinx.Common.Configuration; -using Ryujinx.Input.HLE; -using Ryujinx.UI.Helper; -using SPB.Graphics.Vulkan; -using SPB.Platform.Metal; -using SPB.Platform.Win32; -using SPB.Platform.X11; -using SPB.Windowing; -using System; -using System.Runtime.InteropServices; - -namespace Ryujinx.UI -{ - public partial class VulkanRenderer : RendererWidgetBase - { - public NativeWindowBase NativeWindow { get; private set; } - private UpdateBoundsCallbackDelegate _updateBoundsCallback; - - public VulkanRenderer(InputManager inputManager, GraphicsDebugLevel glLogLevel) : base(inputManager, glLogLevel) { } - - private NativeWindowBase RetrieveNativeWindow() - { - if (OperatingSystem.IsWindows()) - { - IntPtr windowHandle = gdk_win32_window_get_handle(Window.Handle); - - return new SimpleWin32Window(new NativeHandle(windowHandle)); - } - else if (OperatingSystem.IsLinux()) - { - IntPtr displayHandle = gdk_x11_display_get_xdisplay(Display.Handle); - IntPtr windowHandle = gdk_x11_window_get_xid(Window.Handle); - - return new SimpleX11Window(new NativeHandle(displayHandle), new NativeHandle(windowHandle)); - } - else if (OperatingSystem.IsMacOS()) - { - IntPtr metalLayer = MetalHelper.GetMetalLayer(Display, Window, out IntPtr nsView, out _updateBoundsCallback); - - return new SimpleMetalWindow(new NativeHandle(nsView), new NativeHandle(metalLayer)); - } - - throw new NotImplementedException(); - } - - [LibraryImport("libgdk-3-0.dll")] - private static partial IntPtr gdk_win32_window_get_handle(IntPtr d); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_display_get_xdisplay(IntPtr gdkDisplay); - - [LibraryImport("libgdk-3.so.0")] - private static partial IntPtr gdk_x11_window_get_xid(IntPtr gdkWindow); - - protected override bool OnConfigureEvent(EventConfigure evnt) - { - if (NativeWindow == null) - { - NativeWindow = RetrieveNativeWindow(); - - WaitEvent.Set(); - } - - bool result = base.OnConfigureEvent(evnt); - - _updateBoundsCallback?.Invoke(Window); - - return result; - } - - public unsafe IntPtr CreateWindowSurface(IntPtr instance) - { - return VulkanHelper.CreateWindowSurface(instance, NativeWindow); - } - - public override void InitializeRenderer() { } - - public override void SwapBuffers() { } - - protected override string GetGpuBackendName() - { - return "Vulkan"; - } - - protected override void Dispose(bool disposing) - { - Device?.DisposeGpu(); - - NpadManager.Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs b/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs deleted file mode 100644 index 8ee1cd2f3..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.Designer.cs +++ /dev/null @@ -1,233 +0,0 @@ -using Gtk; -using System; - -namespace Ryujinx.UI.Widgets -{ - public partial class GameTableContextMenu : Menu - { - private MenuItem _openSaveUserDirMenuItem; - private MenuItem _openSaveDeviceDirMenuItem; - private MenuItem _openSaveBcatDirMenuItem; - private MenuItem _manageTitleUpdatesMenuItem; - private MenuItem _manageDlcMenuItem; - private MenuItem _manageCheatMenuItem; - private MenuItem _openTitleModDirMenuItem; - private MenuItem _openTitleSdModDirMenuItem; - private Menu _extractSubMenu; - private MenuItem _extractMenuItem; - private MenuItem _extractRomFsMenuItem; - private MenuItem _extractExeFsMenuItem; - private MenuItem _extractLogoMenuItem; - private Menu _manageSubMenu; - private MenuItem _manageCacheMenuItem; - private MenuItem _purgePtcCacheMenuItem; - private MenuItem _purgeShaderCacheMenuItem; - private MenuItem _openPtcDirMenuItem; - private MenuItem _openShaderCacheDirMenuItem; - private MenuItem _createShortcutMenuItem; - - private void InitializeComponent() - { - // - // _openSaveUserDirMenuItem - // - _openSaveUserDirMenuItem = new MenuItem("Open User Save Directory") - { - TooltipText = "Open the directory which contains Application's User Saves.", - }; - _openSaveUserDirMenuItem.Activated += OpenSaveUserDir_Clicked; - - // - // _openSaveDeviceDirMenuItem - // - _openSaveDeviceDirMenuItem = new MenuItem("Open Device Save Directory") - { - TooltipText = "Open the directory which contains Application's Device Saves.", - }; - _openSaveDeviceDirMenuItem.Activated += OpenSaveDeviceDir_Clicked; - - // - // _openSaveBcatDirMenuItem - // - _openSaveBcatDirMenuItem = new MenuItem("Open BCAT Save Directory") - { - TooltipText = "Open the directory which contains Application's BCAT Saves.", - }; - _openSaveBcatDirMenuItem.Activated += OpenSaveBcatDir_Clicked; - - // - // _manageTitleUpdatesMenuItem - // - _manageTitleUpdatesMenuItem = new MenuItem("Manage Title Updates") - { - TooltipText = "Open the Title Update management window", - }; - _manageTitleUpdatesMenuItem.Activated += ManageTitleUpdates_Clicked; - - // - // _manageDlcMenuItem - // - _manageDlcMenuItem = new MenuItem("Manage DLC") - { - TooltipText = "Open the DLC management window", - }; - _manageDlcMenuItem.Activated += ManageDlc_Clicked; - - // - // _manageCheatMenuItem - // - _manageCheatMenuItem = new MenuItem("Manage Cheats") - { - TooltipText = "Open the Cheat management window", - }; - _manageCheatMenuItem.Activated += ManageCheats_Clicked; - - // - // _openTitleModDirMenuItem - // - _openTitleModDirMenuItem = new MenuItem("Open Mods Directory") - { - TooltipText = "Open the directory which contains Application's Mods.", - }; - _openTitleModDirMenuItem.Activated += OpenTitleModDir_Clicked; - - // - // _openTitleSdModDirMenuItem - // - _openTitleSdModDirMenuItem = new MenuItem("Open Atmosphere Mods Directory") - { - TooltipText = "Open the alternative SD card atmosphere directory which contains the Application's Mods.", - }; - _openTitleSdModDirMenuItem.Activated += OpenTitleSdModDir_Clicked; - - // - // _extractSubMenu - // - _extractSubMenu = new Menu(); - - // - // _extractMenuItem - // - _extractMenuItem = new MenuItem("Extract Data") - { - Submenu = _extractSubMenu - }; - - // - // _extractRomFsMenuItem - // - _extractRomFsMenuItem = new MenuItem("RomFS") - { - TooltipText = "Extract the RomFS section from Application's current config (including updates).", - }; - _extractRomFsMenuItem.Activated += ExtractRomFs_Clicked; - - // - // _extractExeFsMenuItem - // - _extractExeFsMenuItem = new MenuItem("ExeFS") - { - TooltipText = "Extract the ExeFS section from Application's current config (including updates).", - }; - _extractExeFsMenuItem.Activated += ExtractExeFs_Clicked; - - // - // _extractLogoMenuItem - // - _extractLogoMenuItem = new MenuItem("Logo") - { - TooltipText = "Extract the Logo section from Application's current config (including updates).", - }; - _extractLogoMenuItem.Activated += ExtractLogo_Clicked; - - // - // _manageSubMenu - // - _manageSubMenu = new Menu(); - - // - // _manageCacheMenuItem - // - _manageCacheMenuItem = new MenuItem("Cache Management") - { - Submenu = _manageSubMenu, - }; - - // - // _purgePtcCacheMenuItem - // - _purgePtcCacheMenuItem = new MenuItem("Queue PPTC Rebuild") - { - TooltipText = "Trigger PPTC to rebuild at boot time on the next game launch.", - }; - _purgePtcCacheMenuItem.Activated += PurgePtcCache_Clicked; - - // - // _purgeShaderCacheMenuItem - // - _purgeShaderCacheMenuItem = new MenuItem("Purge Shader Cache") - { - TooltipText = "Delete the Application's shader cache.", - }; - _purgeShaderCacheMenuItem.Activated += PurgeShaderCache_Clicked; - - // - // _openPtcDirMenuItem - // - _openPtcDirMenuItem = new MenuItem("Open PPTC Directory") - { - TooltipText = "Open the directory which contains the Application's PPTC cache.", - }; - _openPtcDirMenuItem.Activated += OpenPtcDir_Clicked; - - // - // _openShaderCacheDirMenuItem - // - _openShaderCacheDirMenuItem = new MenuItem("Open Shader Cache Directory") - { - TooltipText = "Open the directory which contains the Application's shader cache.", - }; - _openShaderCacheDirMenuItem.Activated += OpenShaderCacheDir_Clicked; - - // - // _createShortcutMenuItem - // - _createShortcutMenuItem = new MenuItem("Create Application Shortcut") - { - TooltipText = OperatingSystem.IsMacOS() ? "Create a shortcut in macOS's Applications folder that launches the selected Application" : "Create a Desktop Shortcut that launches the selected Application." - }; - _createShortcutMenuItem.Activated += CreateShortcut_Clicked; - - ShowComponent(); - } - - private void ShowComponent() - { - _extractSubMenu.Append(_extractExeFsMenuItem); - _extractSubMenu.Append(_extractRomFsMenuItem); - _extractSubMenu.Append(_extractLogoMenuItem); - - _manageSubMenu.Append(_purgePtcCacheMenuItem); - _manageSubMenu.Append(_purgeShaderCacheMenuItem); - _manageSubMenu.Append(_openPtcDirMenuItem); - _manageSubMenu.Append(_openShaderCacheDirMenuItem); - - Add(_createShortcutMenuItem); - Add(new SeparatorMenuItem()); - Add(_openSaveUserDirMenuItem); - Add(_openSaveDeviceDirMenuItem); - Add(_openSaveBcatDirMenuItem); - Add(new SeparatorMenuItem()); - Add(_manageTitleUpdatesMenuItem); - Add(_manageDlcMenuItem); - Add(_manageCheatMenuItem); - Add(_openTitleModDirMenuItem); - Add(_openTitleSdModDirMenuItem); - Add(new SeparatorMenuItem()); - Add(_manageCacheMenuItem); - Add(_extractMenuItem); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs b/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs deleted file mode 100644 index a3e3d4c8c..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GameTableContextMenu.cs +++ /dev/null @@ -1,634 +0,0 @@ -using Gtk; -using LibHac; -using LibHac.Account; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Fs.Shim; -using LibHac.FsSystem; -using LibHac.Ns; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Helper; -using Ryujinx.UI.Windows; -using System; -using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Threading; - -namespace Ryujinx.UI.Widgets -{ - public partial class GameTableContextMenu : Menu - { - private readonly MainWindow _parent; - private readonly VirtualFileSystem _virtualFileSystem; - private readonly AccountManager _accountManager; - private readonly HorizonClient _horizonClient; - - private readonly ApplicationData _applicationData; - - private MessageDialog _dialog; - private bool _cancel; - - public GameTableContextMenu(MainWindow parent, VirtualFileSystem virtualFileSystem, AccountManager accountManager, HorizonClient horizonClient, ApplicationData applicationData) - { - _parent = parent; - - InitializeComponent(); - - _virtualFileSystem = virtualFileSystem; - _accountManager = accountManager; - _horizonClient = horizonClient; - _applicationData = applicationData; - - if (!_applicationData.ControlHolder.ByteSpan.IsZeros()) - { - _openSaveUserDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.UserAccountSaveDataSize > 0; - _openSaveDeviceDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.DeviceSaveDataSize > 0; - _openSaveBcatDirMenuItem.Sensitive = _applicationData.ControlHolder.Value.BcatDeliveryCacheStorageSize > 0; - } - else - { - _openSaveUserDirMenuItem.Sensitive = false; - _openSaveDeviceDirMenuItem.Sensitive = false; - _openSaveBcatDirMenuItem.Sensitive = false; - } - - string fileExt = System.IO.Path.GetExtension(_applicationData.Path).ToLower(); - bool hasNca = fileExt == ".nca" || fileExt == ".nsp" || fileExt == ".pfs0" || fileExt == ".xci"; - - _extractRomFsMenuItem.Sensitive = hasNca; - _extractExeFsMenuItem.Sensitive = hasNca; - _extractLogoMenuItem.Sensitive = hasNca; - - _createShortcutMenuItem.Sensitive = !ReleaseInformation.IsFlatHubBuild; - - PopupAtPointer(null); - } - - private bool TryFindSaveData(string titleName, ulong titleId, BlitStruct controlHolder, in SaveDataFilter filter, out ulong saveDataId) - { - saveDataId = default; - - Result result = _horizonClient.Fs.FindSaveDataWithFilter(out SaveDataInfo saveDataInfo, SaveDataSpaceId.User, in filter); - - if (ResultFs.TargetNotFound.Includes(result)) - { - ref ApplicationControlProperty control = ref controlHolder.Value; - - Logger.Info?.Print(LogClass.Application, $"Creating save directory for Title: {titleName} [{titleId:x16}]"); - - if (Utilities.IsZeros(controlHolder.ByteSpan)) - { - // If the current application doesn't have a loaded control property, create a dummy one - // and set the savedata sizes so a user savedata will be created. - control = ref new BlitStruct(1).Value; - - // The set sizes don't actually matter as long as they're non-zero because we use directory savedata. - control.UserAccountSaveDataSize = 0x4000; - control.UserAccountSaveDataJournalSize = 0x4000; - - Logger.Warning?.Print(LogClass.Application, "No control file was found for this game. Using a dummy one instead. This may cause inaccuracies in some games."); - } - - Uid user = new((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); - - result = _horizonClient.Fs.EnsureApplicationSaveData(out _, new LibHac.Ncm.ApplicationId(titleId), in control, in user); - - if (result.IsFailure()) - { - GtkDialog.CreateErrorDialog($"There was an error creating the specified savedata: {result.ToStringWithName()}"); - - return false; - } - - // Try to find the savedata again after creating it - result = _horizonClient.Fs.FindSaveDataWithFilter(out saveDataInfo, SaveDataSpaceId.User, in filter); - } - - if (result.IsSuccess()) - { - saveDataId = saveDataInfo.SaveDataId; - - return true; - } - - GtkDialog.CreateErrorDialog($"There was an error finding the specified savedata: {result.ToStringWithName()}"); - - return false; - } - - private void OpenSaveDir(in SaveDataFilter saveDataFilter) - { - if (!TryFindSaveData(_applicationData.Name, _applicationData.Id, _applicationData.ControlHolder, in saveDataFilter, out ulong saveDataId)) - { - return; - } - - string saveRootPath = System.IO.Path.Combine(VirtualFileSystem.GetNandPath(), $"user/save/{saveDataId:x16}"); - - if (!Directory.Exists(saveRootPath)) - { - // Inconsistent state. Create the directory - Directory.CreateDirectory(saveRootPath); - } - - string committedPath = System.IO.Path.Combine(saveRootPath, "0"); - string workingPath = System.IO.Path.Combine(saveRootPath, "1"); - - // If the committed directory exists, that path will be loaded the next time the savedata is mounted - if (Directory.Exists(committedPath)) - { - OpenHelper.OpenFolder(committedPath); - } - else - { - // If the working directory exists and the committed directory doesn't, - // the working directory will be loaded the next time the savedata is mounted - if (!Directory.Exists(workingPath)) - { - Directory.CreateDirectory(workingPath); - } - - OpenHelper.OpenFolder(workingPath); - } - } - - private void ExtractSection(NcaSectionType ncaSectionType, int programIndex = 0) - { - FileChooserNative fileChooser = new("Choose the folder to extract into", _parent, FileChooserAction.SelectFolder, "Extract", "Cancel"); - - ResponseType response = (ResponseType)fileChooser.Run(); - string destination = fileChooser.Filename; - - fileChooser.Dispose(); - - if (response == ResponseType.Accept) - { - Thread extractorThread = new(() => - { - Gtk.Application.Invoke(delegate - { - _dialog = new MessageDialog(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Cancel, null) - { - Title = "Ryujinx - NCA Section Extractor", - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.Gtk3.UI.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = $"Extracting {ncaSectionType} section from {System.IO.Path.GetFileName(_applicationData.Path)}...", - WindowPosition = WindowPosition.Center, - }; - - int dialogResponse = _dialog.Run(); - if (dialogResponse == (int)ResponseType.Cancel || dialogResponse == (int)ResponseType.DeleteEvent) - { - _cancel = true; - _dialog.Dispose(); - } - }); - - using FileStream file = new(_applicationData.Path, FileMode.Open, FileAccess.Read); - - Nca mainNca = null; - Nca patchNca = null; - - if ((System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nsp") || - (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".pfs0") || - (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".xci")) - { - IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(_applicationData.Path, _virtualFileSystem); - - foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = new(_virtualFileSystem.KeySet, ncaFile.Release().AsStorage()); - - if (nca.Header.ContentType == NcaContentType.Program) - { - int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program); - - if (nca.SectionExists(NcaSectionType.Data) && nca.Header.GetFsHeader(dataIndex).IsPatchSection()) - { - patchNca = nca; - } - else - { - mainNca = nca; - } - } - } - } - else if (System.IO.Path.GetExtension(_applicationData.Path).ToLower() == ".nca") - { - mainNca = new Nca(_virtualFileSystem.KeySet, file.AsStorage()); - } - - if (mainNca == null) - { - Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA is not present in the selected file."); - - Gtk.Application.Invoke(delegate - { - GtkDialog.CreateErrorDialog("Extraction failure. The main NCA is not present in the selected file."); - }); - - return; - } - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - (Nca updatePatchNca, _) = mainNca.GetUpdateData(_virtualFileSystem, checkLevel, programIndex, out _); - - if (updatePatchNca != null) - { - patchNca = updatePatchNca; - } - - int index = Nca.GetSectionIndexFromType(ncaSectionType, mainNca.Header.ContentType); - - bool sectionExistsInPatch = false; - - if (patchNca != null) - { - sectionExistsInPatch = patchNca.CanOpenSection(index); - } - - IFileSystem ncaFileSystem = sectionExistsInPatch ? mainNca.OpenFileSystemWithPatch(patchNca, index, IntegrityCheckLevel.ErrorOnInvalid) - : mainNca.OpenFileSystem(index, IntegrityCheckLevel.ErrorOnInvalid); - - FileSystemClient fsClient = _horizonClient.Fs; - - string source = DateTime.Now.ToFileTime().ToString()[10..]; - string output = DateTime.Now.ToFileTime().ToString()[10..]; - - using var uniqueSourceFs = new UniqueRef(ncaFileSystem); - using var uniqueOutputFs = new UniqueRef(new LocalFileSystem(destination)); - - fsClient.Register(source.ToU8Span(), ref uniqueSourceFs.Ref); - fsClient.Register(output.ToU8Span(), ref uniqueOutputFs.Ref); - - (Result? resultCode, bool canceled) = CopyDirectory(fsClient, $"{source}:/", $"{output}:/"); - - if (!canceled) - { - if (resultCode.Value.IsFailure()) - { - Logger.Error?.Print(LogClass.Application, $"LibHac returned error code: {resultCode.Value.ErrorCode}"); - - Gtk.Application.Invoke(delegate - { - _dialog?.Dispose(); - - GtkDialog.CreateErrorDialog("Extraction failed. Read the log file for further information."); - }); - } - else if (resultCode.Value.IsSuccess()) - { - Gtk.Application.Invoke(delegate - { - _dialog?.Dispose(); - - MessageDialog dialog = new(null, DialogFlags.DestroyWithParent, MessageType.Info, ButtonsType.Ok, null) - { - Title = "Ryujinx - NCA Section Extractor", - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"), - SecondaryText = "Extraction completed successfully.", - WindowPosition = WindowPosition.Center, - }; - - dialog.Run(); - dialog.Dispose(); - }); - } - } - - fsClient.Unmount(source.ToU8Span()); - fsClient.Unmount(output.ToU8Span()); - }) - { - Name = "GUI.NcaSectionExtractorThread", - IsBackground = true, - }; - extractorThread.Start(); - } - } - - private (Result? result, bool canceled) CopyDirectory(FileSystemClient fs, string sourcePath, string destPath) - { - Result rc = fs.OpenDirectory(out DirectoryHandle sourceHandle, sourcePath.ToU8Span(), OpenDirectoryMode.All); - if (rc.IsFailure()) - { - return (rc, false); - } - - using (sourceHandle) - { - foreach (DirectoryEntryEx entry in fs.EnumerateEntries(sourcePath, "*", SearchOptions.Default)) - { - if (_cancel) - { - return (null, true); - } - - string subSrcPath = PathTools.Normalize(PathTools.Combine(sourcePath, entry.Name)); - string subDstPath = PathTools.Normalize(PathTools.Combine(destPath, entry.Name)); - - if (entry.Type == DirectoryEntryType.Directory) - { - fs.EnsureDirectoryExists(subDstPath); - - (Result? result, bool canceled) = CopyDirectory(fs, subSrcPath, subDstPath); - if (canceled || result.Value.IsFailure()) - { - return (result, canceled); - } - } - - if (entry.Type == DirectoryEntryType.File) - { - fs.CreateOrOverwriteFile(subDstPath, entry.Size); - - rc = CopyFile(fs, subSrcPath, subDstPath); - if (rc.IsFailure()) - { - return (rc, false); - } - } - } - } - - return (Result.Success, false); - } - - public static Result CopyFile(FileSystemClient fs, string sourcePath, string destPath) - { - Result rc = fs.OpenFile(out FileHandle sourceHandle, sourcePath.ToU8Span(), OpenMode.Read); - if (rc.IsFailure()) - { - return rc; - } - - using (sourceHandle) - { - rc = fs.OpenFile(out FileHandle destHandle, destPath.ToU8Span(), OpenMode.Write | OpenMode.AllowAppend); - if (rc.IsFailure()) - { - return rc; - } - - using (destHandle) - { - const int MaxBufferSize = 1024 * 1024; - - rc = fs.GetFileSize(out long fileSize, sourceHandle); - if (rc.IsFailure()) - { - return rc; - } - - int bufferSize = (int)Math.Min(MaxBufferSize, fileSize); - - byte[] buffer = ArrayPool.Shared.Rent(bufferSize); - try - { - for (long offset = 0; offset < fileSize; offset += bufferSize) - { - int toRead = (int)Math.Min(fileSize - offset, bufferSize); - Span buf = buffer.AsSpan(0, toRead); - - rc = fs.ReadFile(out long _, sourceHandle, offset, buf); - if (rc.IsFailure()) - { - return rc; - } - - rc = fs.WriteFile(destHandle, offset, buf, WriteOption.None); - if (rc.IsFailure()) - { - return rc; - } - } - } - finally - { - ArrayPool.Shared.Return(buffer); - } - - rc = fs.FlushFile(destHandle); - if (rc.IsFailure()) - { - return rc; - } - } - } - - return Result.Success; - } - - // - // Events - // - private void OpenSaveUserDir_Clicked(object sender, EventArgs args) - { - var userId = new LibHac.Fs.UserId((ulong)_accountManager.LastOpenedUser.UserId.High, (ulong)_accountManager.LastOpenedUser.UserId.Low); - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, saveType: default, userId, saveDataId: default, index: default); - - OpenSaveDir(in saveDataFilter); - } - - private void OpenSaveDeviceDir_Clicked(object sender, EventArgs args) - { - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Device, userId: default, saveDataId: default, index: default); - - OpenSaveDir(in saveDataFilter); - } - - private void OpenSaveBcatDir_Clicked(object sender, EventArgs args) - { - var saveDataFilter = SaveDataFilter.Make(_applicationData.Id, SaveDataType.Bcat, userId: default, saveDataId: default, index: default); - - OpenSaveDir(in saveDataFilter); - } - - private void ManageTitleUpdates_Clicked(object sender, EventArgs args) - { - new TitleUpdateWindow(_parent, _virtualFileSystem, _applicationData).Show(); - } - - private void ManageDlc_Clicked(object sender, EventArgs args) - { - new DlcWindow(_virtualFileSystem, _applicationData.IdBaseString, _applicationData).Show(); - } - - private void ManageCheats_Clicked(object sender, EventArgs args) - { - new CheatWindow(_virtualFileSystem, _applicationData.Id, _applicationData.Name, _applicationData.Path).Show(); - } - - private void OpenTitleModDir_Clicked(object sender, EventArgs args) - { - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, _applicationData.IdString); - - OpenHelper.OpenFolder(titleModsPath); - } - - private void OpenTitleSdModDir_Clicked(object sender, EventArgs args) - { - string sdModsBasePath = ModLoader.GetSdModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(sdModsBasePath, _applicationData.IdString); - - OpenHelper.OpenFolder(titleModsPath); - } - - private void ExtractRomFs_Clicked(object sender, EventArgs args) - { - ExtractSection(NcaSectionType.Data); - } - - private void ExtractExeFs_Clicked(object sender, EventArgs args) - { - ExtractSection(NcaSectionType.Code); - } - - private void ExtractLogo_Clicked(object sender, EventArgs args) - { - ExtractSection(NcaSectionType.Logo); - } - - private void OpenPtcDir_Clicked(object sender, EventArgs args) - { - string ptcDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu"); - - string mainPath = System.IO.Path.Combine(ptcDir, "0"); - string backupPath = System.IO.Path.Combine(ptcDir, "1"); - - if (!Directory.Exists(ptcDir)) - { - Directory.CreateDirectory(ptcDir); - Directory.CreateDirectory(mainPath); - Directory.CreateDirectory(backupPath); - } - - OpenHelper.OpenFolder(ptcDir); - } - - private void OpenShaderCacheDir_Clicked(object sender, EventArgs args) - { - string shaderCacheDir = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader"); - - if (!Directory.Exists(shaderCacheDir)) - { - Directory.CreateDirectory(shaderCacheDir); - } - - OpenHelper.OpenFolder(shaderCacheDir); - } - - private void PurgePtcCache_Clicked(object sender, EventArgs args) - { - DirectoryInfo mainDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "0")); - DirectoryInfo backupDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "cpu", "1")); - - MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to queue a PPTC rebuild on the next boot of:\n\n{_applicationData.Name}\n\nAre you sure you want to proceed?"); - - List cacheFiles = new(); - - if (mainDir.Exists) - { - cacheFiles.AddRange(mainDir.EnumerateFiles("*.cache")); - } - - if (backupDir.Exists) - { - cacheFiles.AddRange(backupDir.EnumerateFiles("*.cache")); - } - - if (cacheFiles.Count > 0 && warningDialog.Run() == (int)ResponseType.Yes) - { - foreach (FileInfo file in cacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - GtkDialog.CreateErrorDialog($"Error purging PPTC cache {file.Name}: {e}"); - } - } - } - - warningDialog.Dispose(); - } - - private void PurgeShaderCache_Clicked(object sender, EventArgs args) - { - DirectoryInfo shaderCacheDir = new(System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationData.IdString, "cache", "shader")); - - using MessageDialog warningDialog = GtkDialog.CreateConfirmationDialog("Warning", $"You are about to delete the shader cache for :\n\n{_applicationData.Name}\n\nAre you sure you want to proceed?"); - - List oldCacheDirectories = new(); - List newCacheFiles = new(); - - if (shaderCacheDir.Exists) - { - oldCacheDirectories.AddRange(shaderCacheDir.EnumerateDirectories("*")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.toc")); - newCacheFiles.AddRange(shaderCacheDir.GetFiles("*.data")); - } - - if ((oldCacheDirectories.Count > 0 || newCacheFiles.Count > 0) && warningDialog.Run() == (int)ResponseType.Yes) - { - foreach (DirectoryInfo directory in oldCacheDirectories) - { - try - { - directory.Delete(true); - } - catch (Exception e) - { - GtkDialog.CreateErrorDialog($"Error purging shader cache at {directory.Name}: {e}"); - } - } - - foreach (FileInfo file in newCacheFiles) - { - try - { - file.Delete(); - } - catch (Exception e) - { - GtkDialog.CreateErrorDialog($"Error purging shader cache at {file.Name}: {e}"); - } - } - } - } - - private void CreateShortcut_Clicked(object sender, EventArgs args) - { - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - byte[] appIcon = new ApplicationLibrary(_virtualFileSystem, checkLevel).GetApplicationIcon(_applicationData.Path, ConfigurationState.Instance.System.Language, _applicationData.Id); - ShortcutHelper.CreateAppShortcut(_applicationData.Path, _applicationData.Name, _applicationData.IdString, appIcon); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs deleted file mode 100644 index 567f9ad67..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GtkDialog.cs +++ /dev/null @@ -1,114 +0,0 @@ -using Gtk; -using Ryujinx.Common.Logging; -using Ryujinx.UI.Common.Configuration; -using System.Collections.Generic; -using System.Reflection; - -namespace Ryujinx.UI.Widgets -{ - internal class GtkDialog : MessageDialog - { - private static bool _isChoiceDialogOpen; - - private GtkDialog(string title, string mainText, string secondaryText, MessageType messageType = MessageType.Other, ButtonsType buttonsType = ButtonsType.Ok) - : base(null, DialogFlags.Modal, messageType, buttonsType, null) - { - Title = title; - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - Text = mainText; - SecondaryText = secondaryText; - WindowPosition = WindowPosition.Center; - SecondaryUseMarkup = true; - - Response += GtkDialog_Response; - - SetSizeRequest(200, 20); - } - - private void GtkDialog_Response(object sender, ResponseArgs args) - { - Dispose(); - } - - internal static void CreateInfoDialog(string mainText, string secondaryText) - { - new GtkDialog("Ryujinx - Info", mainText, secondaryText, MessageType.Info).Run(); - } - - internal static void CreateUpdaterInfoDialog(string mainText, string secondaryText) - { - new GtkDialog("Ryujinx - Updater", mainText, secondaryText, MessageType.Info).Run(); - } - - internal static MessageDialog CreateWaitingDialog(string mainText, string secondaryText) - { - return new GtkDialog("Ryujinx - Waiting", mainText, secondaryText, MessageType.Info, ButtonsType.None); - } - - internal static void CreateWarningDialog(string mainText, string secondaryText) - { - new GtkDialog("Ryujinx - Warning", mainText, secondaryText, MessageType.Warning).Run(); - } - - internal static void CreateErrorDialog(string errorMessage) - { - Logger.Error?.Print(LogClass.Application, errorMessage); - - new GtkDialog("Ryujinx - Error", "Ryujinx has encountered an error", errorMessage, MessageType.Error).Run(); - } - - internal static MessageDialog CreateConfirmationDialog(string mainText, string secondaryText = "") - { - return new GtkDialog("Ryujinx - Confirmation", mainText, secondaryText, MessageType.Question, ButtonsType.YesNo); - } - - internal static bool CreateChoiceDialog(string title, string mainText, string secondaryText) - { - if (_isChoiceDialogOpen) - { - return false; - } - - _isChoiceDialogOpen = true; - - ResponseType response = (ResponseType)new GtkDialog(title, mainText, secondaryText, MessageType.Question, ButtonsType.YesNo).Run(); - - _isChoiceDialogOpen = false; - - return response == ResponseType.Yes; - } - - internal static ResponseType CreateCustomDialog(string title, string mainText, string secondaryText, Dictionary buttons, MessageType messageType = MessageType.Other) - { - GtkDialog gtkDialog = new(title, mainText, secondaryText, messageType, ButtonsType.None); - - foreach (var button in buttons) - { - gtkDialog.AddButton(button.Value, button.Key); - } - - return (ResponseType)gtkDialog.Run(); - } - - internal static string CreateInputDialog(Window parent, string title, string mainText, uint inputMax) - { - GtkInputDialog gtkDialog = new(parent, title, mainText, inputMax); - ResponseType response = (ResponseType)gtkDialog.Run(); - string responseText = gtkDialog.InputEntry.Text.TrimEnd(); - - gtkDialog.Dispose(); - - if (response == ResponseType.Ok) - { - return responseText; - } - - return ""; - } - - internal static bool CreateExitDialog() - { - return CreateChoiceDialog("Ryujinx - Exit", "Are you sure you want to close Ryujinx?", "All unsaved data will be lost!"); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs deleted file mode 100644 index fd85248b7..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/GtkInputDialog.cs +++ /dev/null @@ -1,37 +0,0 @@ -using Gtk; - -namespace Ryujinx.UI.Widgets -{ - public class GtkInputDialog : MessageDialog - { - public Entry InputEntry { get; } - - public GtkInputDialog(Window parent, string title, string mainText, uint inputMax) : base(parent, DialogFlags.Modal | DialogFlags.DestroyWithParent, MessageType.Question, ButtonsType.OkCancel, null) - { - SetDefaultSize(300, 0); - - Title = title; - - Label mainTextLabel = new() - { - Text = mainText, - }; - - InputEntry = new Entry - { - MaxLength = (int)inputMax, - }; - - Label inputMaxTextLabel = new() - { - Text = $"(Max length: {inputMax})", - }; - - ((Box)MessageArea).PackStart(mainTextLabel, true, true, 0); - ((Box)MessageArea).PackStart(InputEntry, true, true, 5); - ((Box)MessageArea).PackStart(inputMaxTextLabel, true, true, 0); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs deleted file mode 100644 index 3b3e2fbbe..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common.Configuration; -using System; -using System.Reflection; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Widgets -{ - public class ProfileDialog : Dialog - { - public string FileName { get; private set; } - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Entry _profileEntry; - [GUI] Label _errorMessage; -#pragma warning restore CS0649, IDE0044 - - public ProfileDialog() : this(new Builder("Ryujinx.Gtk3.UI.Widgets.ProfileDialog.glade")) { } - - private ProfileDialog(Builder builder) : base(builder.GetRawOwnedObject("_profileDialog")) - { - builder.Autoconnect(this); - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - } - - private void OkToggle_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - bool validFileName = true; - - foreach (char invalidChar in System.IO.Path.GetInvalidFileNameChars()) - { - if (_profileEntry.Text.Contains(invalidChar)) - { - validFileName = false; - } - } - - if (validFileName && !string.IsNullOrEmpty(_profileEntry.Text)) - { - FileName = $"{_profileEntry.Text}.json"; - - Respond(ResponseType.Ok); - } - else - { - _errorMessage.Text = "The file name contains invalid characters. Please try again."; - } - } - - private void CancelToggle_Activated(object sender, EventArgs args) - { - Respond(ResponseType.Cancel); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade b/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade deleted file mode 100644 index adaf6608f..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/ProfileDialog.glade +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - False - Ryujinx - Profile Dialog - True - center - 400 - dialog - - - - - - False - vertical - 2 - - - False - end - - - OK - True - True - True - - - - False - True - 0 - - - - - Cancel - True - True - True - - - - False - True - 5 - 1 - - - - - False - False - 0 - - - - - True - False - vertical - - - True - False - 10 - 10 - 20 - 10 - Enter a name for the new profile: - - - True - True - 0 - - - - - True - True - 20 - 20 - 20 - - - True - True - 1 - - - - - True - False - start - 20 - 10 - 10 - 10 - - - - - - False - True - 2 - - - - - True - True - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs b/src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs deleted file mode 100644 index 6470790e8..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/RawInputToTextEntry.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Gtk; - -namespace Ryujinx.UI.Widgets -{ - public class RawInputToTextEntry : Entry - { - public void SendKeyPressEvent(object o, KeyPressEventArgs args) - { - base.OnKeyPressEvent(args.Event); - } - - public void SendKeyReleaseEvent(object o, KeyReleaseEventArgs args) - { - base.OnKeyReleaseEvent(args.Event); - } - - public void SendButtonPressEvent(object o, ButtonPressEventArgs args) - { - base.OnButtonPressEvent(args.Event); - } - - public void SendButtonReleaseEvent(object o, ButtonReleaseEventArgs args) - { - base.OnButtonReleaseEvent(args.Event); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs b/src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs deleted file mode 100644 index f0b55cd8b..000000000 --- a/src/Ryujinx.Gtk3/UI/Widgets/UserErrorDialog.cs +++ /dev/null @@ -1,123 +0,0 @@ -using Gtk; -using Ryujinx.UI.Common; -using Ryujinx.UI.Common.Helper; - -namespace Ryujinx.UI.Widgets -{ - internal class UserErrorDialog : MessageDialog - { - private const string SetupGuideUrl = "https://github.com/Ryujinx/Ryujinx/wiki/Ryujinx-Setup-&-Configuration-Guide"; - private const int OkResponseId = 0; - private const int SetupGuideResponseId = 1; - - private readonly UserError _userError; - - private UserErrorDialog(UserError error) : base(null, DialogFlags.Modal, MessageType.Error, ButtonsType.None, null) - { - _userError = error; - - WindowPosition = WindowPosition.Center; - SecondaryUseMarkup = true; - - Response += UserErrorDialog_Response; - - SetSizeRequest(120, 50); - - AddButton("OK", OkResponseId); - - bool isInSetupGuide = IsCoveredBySetupGuide(error); - - if (isInSetupGuide) - { - AddButton("Open the Setup Guide", SetupGuideResponseId); - } - - string errorCode = GetErrorCode(error); - - SecondaryUseMarkup = true; - - Title = $"Ryujinx error ({errorCode})"; - Text = $"{errorCode}: {GetErrorTitle(error)}"; - SecondaryText = GetErrorDescription(error); - - if (isInSetupGuide) - { - SecondaryText += "\nFor more information on how to fix this error, follow our Setup Guide."; - } - } - - private static string GetErrorCode(UserError error) - { - return $"RYU-{(uint)error:X4}"; - } - - private static string GetErrorTitle(UserError error) - { - return error switch - { - UserError.NoKeys => "Keys not found", - UserError.NoFirmware => "Firmware not found", - UserError.FirmwareParsingFailed => "Firmware parsing error", - UserError.ApplicationNotFound => "Application not found", - UserError.Unknown => "Unknown error", - _ => "Undefined error", - }; - } - - private static string GetErrorDescription(UserError error) - { - return error switch - { - UserError.NoKeys => "Ryujinx was unable to find your 'prod.keys' file", - UserError.NoFirmware => "Ryujinx was unable to find any firmwares installed", - UserError.FirmwareParsingFailed => "Ryujinx was unable to parse the provided firmware. This is usually caused by outdated keys.", - UserError.ApplicationNotFound => "Ryujinx couldn't find a valid application at the given path.", - UserError.Unknown => "An unknown error occured!", - _ => "An undefined error occured! This shouldn't happen, please contact a dev!", - }; - } - - private static bool IsCoveredBySetupGuide(UserError error) - { - return error switch - { - UserError.NoKeys or - UserError.NoFirmware or - UserError.FirmwareParsingFailed => true, - _ => false, - }; - } - - private static string GetSetupGuideUrl(UserError error) - { - if (!IsCoveredBySetupGuide(error)) - { - return null; - } - - return error switch - { - UserError.NoKeys => SetupGuideUrl + "#initial-setup---placement-of-prodkeys", - UserError.NoFirmware => SetupGuideUrl + "#initial-setup-continued---installation-of-firmware", - _ => SetupGuideUrl, - }; - } - - private void UserErrorDialog_Response(object sender, ResponseArgs args) - { - int responseId = (int)args.ResponseId; - - if (responseId == SetupGuideResponseId) - { - OpenHelper.OpenUrl(GetSetupGuideUrl(_userError)); - } - - Dispose(); - } - - public static void CreateUserErrorDialog(UserError error) - { - new UserErrorDialog(error).Run(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs deleted file mode 100644 index fd912ef9a..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.Designer.cs +++ /dev/null @@ -1,511 +0,0 @@ -using Gtk; -using Pango; -using Ryujinx.UI.Common.Configuration; -using System.Reflection; - -namespace Ryujinx.UI.Windows -{ - public partial class AboutWindow : Window - { - private Box _mainBox; - private Box _leftBox; - private Box _logoBox; - private Image _ryujinxLogo; - private Box _logoTextBox; - private Label _ryujinxLabel; - private Label _ryujinxPhoneticLabel; - private EventBox _ryujinxLink; - private Label _ryujinxLinkLabel; - private Label _versionLabel; - private Label _disclaimerLabel; - private EventBox _amiiboApiLink; - private Label _amiiboApiLinkLabel; - private Box _socialBox; - private EventBox _patreonEventBox; - private Box _patreonBox; - private Image _patreonLogo; - private Label _patreonLabel; - private EventBox _githubEventBox; - private Box _githubBox; - private Image _githubLogo; - private Label _githubLabel; - private Box _discordBox; - private EventBox _discordEventBox; - private Image _discordLogo; - private Label _discordLabel; - private EventBox _twitterEventBox; - private Box _twitterBox; - private Image _twitterLogo; - private Label _twitterLabel; - private Separator _separator; - private Box _rightBox; - private Label _aboutLabel; - private Label _aboutDescriptionLabel; - private Label _createdByLabel; - private TextView _createdByText; - private EventBox _contributorsEventBox; - private Label _contributorsLinkLabel; - private Label _patreonNamesLabel; - private ScrolledWindow _patreonNamesScrolled; - private TextView _patreonNamesText; - private EventBox _changelogEventBox; - private Label _changelogLinkLabel; - - private void InitializeComponent() - { - - // - // AboutWindow - // - CanFocus = false; - Resizable = false; - Modal = true; - WindowPosition = WindowPosition.Center; - DefaultWidth = 800; - DefaultHeight = 450; - TypeHint = Gdk.WindowTypeHint.Dialog; - - // - // _mainBox - // - _mainBox = new Box(Orientation.Horizontal, 0); - - // - // _leftBox - // - _leftBox = new Box(Orientation.Vertical, 0) - { - Margin = 15, - MarginStart = 30, - MarginEnd = 0, - }; - - // - // _logoBox - // - _logoBox = new Box(Orientation.Horizontal, 0); - - // - // _ryujinxLogo - // - _ryujinxLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png", 100, 100)) - { - Margin = 10, - MarginStart = 15, - }; - - // - // _logoTextBox - // - _logoTextBox = new Box(Orientation.Vertical, 0); - - // - // _ryujinxLabel - // - _ryujinxLabel = new Label("Ryujinx") - { - MarginTop = 15, - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _ryujinxLabel.Attributes.Insert(new Pango.AttrScale(2.7f)); - - // - // _ryujinxPhoneticLabel - // - _ryujinxPhoneticLabel = new Label("(REE-YOU-JINX)") - { - Justify = Justification.Center, - }; - - // - // _ryujinxLink - // - _ryujinxLink = new EventBox() - { - Margin = 5 - }; - _ryujinxLink.ButtonPressEvent += RyujinxButton_Pressed; - - // - // _ryujinxLinkLabel - // - _ryujinxLinkLabel = new Label("www.ryujinx.org") - { - TooltipText = "Click to open the Ryujinx website in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _ryujinxLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _versionLabel - // - _versionLabel = new Label(Program.Version) - { - Expand = true, - Justify = Justification.Center, - Margin = 5, - }; - - // - // _changelogEventBox - // - _changelogEventBox = new EventBox(); - _changelogEventBox.ButtonPressEvent += ChangelogButton_Pressed; - - // - // _changelogLinkLabel - // - _changelogLinkLabel = new Label("View Changelog on GitHub") - { - TooltipText = "Click to open the changelog for this version in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _changelogLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _disclaimerLabel - // - _disclaimerLabel = new Label("Ryujinx is not affiliated with Nintendo™,\nor any of its partners, in any way.") - { - Expand = true, - Justify = Justification.Center, - Margin = 5, - Attributes = new AttrList(), - }; - _disclaimerLabel.Attributes.Insert(new Pango.AttrScale(0.8f)); - - // - // _amiiboApiLink - // - _amiiboApiLink = new EventBox() - { - Margin = 5, - }; - _amiiboApiLink.ButtonPressEvent += AmiiboApiButton_Pressed; - - // - // _amiiboApiLinkLabel - // - _amiiboApiLinkLabel = new Label("AmiiboAPI (www.amiiboapi.com) is used\nin our Amiibo emulation.") - { - TooltipText = "Click to open the AmiiboAPI website in your default browser.", - Justify = Justification.Center, - Attributes = new AttrList(), - }; - _amiiboApiLinkLabel.Attributes.Insert(new Pango.AttrScale(0.9f)); - - // - // _socialBox - // - _socialBox = new Box(Orientation.Horizontal, 0) - { - Margin = 25, - MarginBottom = 10, - }; - - // - // _patreonEventBox - // - _patreonEventBox = new EventBox() - { - TooltipText = "Click to open the Ryujinx Patreon page in your default browser.", - }; - _patreonEventBox.ButtonPressEvent += PatreonButton_Pressed; - - // - // _patreonBox - // - _patreonBox = new Box(Orientation.Vertical, 0); - - // - // _patreonLogo - // - _patreonLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Patreon_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _patreonLabel - // - _patreonLabel = new Label("Patreon") - { - Justify = Justification.Center, - }; - - // - // _githubEventBox - // - _githubEventBox = new EventBox() - { - TooltipText = "Click to open the Ryujinx GitHub page in your default browser.", - }; - _githubEventBox.ButtonPressEvent += GitHubButton_Pressed; - - // - // _githubBox - // - _githubBox = new Box(Orientation.Vertical, 0); - - // - // _githubLogo - // - _githubLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_GitHub_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _githubLabel - // - _githubLabel = new Label("GitHub") - { - Justify = Justification.Center, - }; - - // - // _discordBox - // - _discordBox = new Box(Orientation.Vertical, 0); - - // - // _discordEventBox - // - _discordEventBox = new EventBox() - { - TooltipText = "Click to open an invite to the Ryujinx Discord server in your default browser.", - }; - _discordEventBox.ButtonPressEvent += DiscordButton_Pressed; - - // - // _discordLogo - // - _discordLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Discord_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _discordLabel - // - _discordLabel = new Label("Discord") - { - Justify = Justification.Center, - }; - - // - // _twitterEventBox - // - _twitterEventBox = new EventBox() - { - TooltipText = "Click to open the Ryujinx Twitter page in your default browser.", - }; - _twitterEventBox.ButtonPressEvent += TwitterButton_Pressed; - - // - // _twitterBox - // - _twitterBox = new Box(Orientation.Vertical, 0); - - // - // _twitterLogo - // - _twitterLogo = new Image(new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Twitter_Light.png", 30, 30)) - { - Margin = 10, - }; - - // - // _twitterLabel - // - _twitterLabel = new Label("Twitter") - { - Justify = Justification.Center, - }; - - // - // _separator - // - _separator = new Separator(Orientation.Vertical) - { - Margin = 15, - }; - - // - // _rightBox - // - _rightBox = new Box(Orientation.Vertical, 0) - { - Margin = 15, - MarginTop = 40, - }; - - // - // _aboutLabel - // - _aboutLabel = new Label("About :") - { - Halign = Align.Start, - Attributes = new AttrList(), - }; - _aboutLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - _aboutLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _aboutDescriptionLabel - // - _aboutDescriptionLabel = new Label("Ryujinx is an emulator for the Nintendo Switch™.\n" + - "Please support us on Patreon.\n" + - "Get all the latest news on our Twitter or Discord.\n" + - "Developers interested in contributing can find out more on our GitHub or Discord.") - { - Margin = 15, - Halign = Align.Start, - }; - - // - // _createdByLabel - // - _createdByLabel = new Label("Maintained by :") - { - Halign = Align.Start, - Attributes = new AttrList(), - }; - _createdByLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - _createdByLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _createdByText - // - _createdByText = new TextView() - { - WrapMode = Gtk.WrapMode.Word, - Editable = false, - CursorVisible = false, - Margin = 15, - MarginEnd = 30, - }; - _createdByText.Buffer.Text = "gdkchan, Ac_K, Thog, rip in peri peri, LDj3SNuD, emmaus, Thealexbarney, Xpl0itR, GoffyDude, »jD« and more..."; - - // - // _contributorsEventBox - // - _contributorsEventBox = new EventBox(); - _contributorsEventBox.ButtonPressEvent += ContributorsButton_Pressed; - - // - // _contributorsLinkLabel - // - _contributorsLinkLabel = new Label("See All Contributors...") - { - TooltipText = "Click to open the Contributors page in your default browser.", - MarginEnd = 30, - Halign = Align.End, - Attributes = new AttrList(), - }; - _contributorsLinkLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _patreonNamesLabel - // - _patreonNamesLabel = new Label("Supported on Patreon by :") - { - Halign = Align.Start, - Attributes = new AttrList(), - }; - _patreonNamesLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - _patreonNamesLabel.Attributes.Insert(new Pango.AttrUnderline(Underline.Single)); - - // - // _patreonNamesScrolled - // - _patreonNamesScrolled = new ScrolledWindow() - { - Margin = 15, - MarginEnd = 30, - Expand = true, - ShadowType = ShadowType.In, - }; - _patreonNamesScrolled.SetPolicy(PolicyType.Never, PolicyType.Automatic); - - // - // _patreonNamesText - // - _patreonNamesText = new TextView() - { - WrapMode = Gtk.WrapMode.Word, - }; - _patreonNamesText.Buffer.Text = "Loading..."; - _patreonNamesText.SetProperty("editable", new GLib.Value(false)); - - ShowComponent(); - } - - private void ShowComponent() - { - _logoBox.Add(_ryujinxLogo); - - _ryujinxLink.Add(_ryujinxLinkLabel); - - _logoTextBox.Add(_ryujinxLabel); - _logoTextBox.Add(_ryujinxPhoneticLabel); - _logoTextBox.Add(_ryujinxLink); - - _logoBox.Add(_logoTextBox); - - _amiiboApiLink.Add(_amiiboApiLinkLabel); - - _patreonBox.Add(_patreonLogo); - _patreonBox.Add(_patreonLabel); - _patreonEventBox.Add(_patreonBox); - - _githubBox.Add(_githubLogo); - _githubBox.Add(_githubLabel); - _githubEventBox.Add(_githubBox); - - _discordBox.Add(_discordLogo); - _discordBox.Add(_discordLabel); - _discordEventBox.Add(_discordBox); - - _twitterBox.Add(_twitterLogo); - _twitterBox.Add(_twitterLabel); - _twitterEventBox.Add(_twitterBox); - - _socialBox.Add(_patreonEventBox); - _socialBox.Add(_githubEventBox); - _socialBox.Add(_discordEventBox); - _socialBox.Add(_twitterEventBox); - - _changelogEventBox.Add(_changelogLinkLabel); - - _leftBox.Add(_logoBox); - _leftBox.Add(_versionLabel); - _leftBox.Add(_changelogEventBox); - _leftBox.Add(_disclaimerLabel); - _leftBox.Add(_amiiboApiLink); - _leftBox.Add(_socialBox); - - _contributorsEventBox.Add(_contributorsLinkLabel); - _patreonNamesScrolled.Add(_patreonNamesText); - - _rightBox.Add(_aboutLabel); - _rightBox.Add(_aboutDescriptionLabel); - _rightBox.Add(_createdByLabel); - _rightBox.Add(_createdByText); - _rightBox.Add(_contributorsEventBox); - _rightBox.Add(_patreonNamesLabel); - _rightBox.Add(_patreonNamesScrolled); - - _mainBox.Add(_leftBox); - _mainBox.Add(_separator); - _mainBox.Add(_rightBox); - - Add(_mainBox); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs deleted file mode 100644 index f4bb533c3..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AboutWindow.cs +++ /dev/null @@ -1,85 +0,0 @@ -using Gtk; -using Ryujinx.Common.Utilities; -using Ryujinx.UI.Common.Helper; -using System.Net.Http; -using System.Net.NetworkInformation; -using System.Reflection; -using System.Threading.Tasks; - -namespace Ryujinx.UI.Windows -{ - public partial class AboutWindow : Window - { - public AboutWindow() : base($"Ryujinx {Program.Version} - About") - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(OpenHelper)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - InitializeComponent(); - - _ = DownloadPatronsJson(); - } - - private async Task DownloadPatronsJson() - { - if (!NetworkInterface.GetIsNetworkAvailable()) - { - _patreonNamesText.Buffer.Text = "Connection Error."; - } - - HttpClient httpClient = new(); - - try - { - string patreonJsonString = await httpClient.GetStringAsync("https://patreon.ryujinx.org/"); - - _patreonNamesText.Buffer.Text = string.Join(", ", JsonHelper.Deserialize(patreonJsonString, CommonJsonContext.Default.StringArray)); - } - catch - { - _patreonNamesText.Buffer.Text = "API Error."; - } - } - - // - // Events - // - private void RyujinxButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://ryujinx.org"); - } - - private void AmiiboApiButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://amiiboapi.com"); - } - - private void PatreonButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://www.patreon.com/ryujinx"); - } - - private void GitHubButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx"); - } - - private void DiscordButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://discordapp.com/invite/N2FmfVc"); - } - - private void TwitterButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://twitter.com/RyujinxEmu"); - } - - private void ContributorsButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/graphs/contributors?type=a"); - } - - private void ChangelogButton_Pressed(object sender, ButtonPressEventArgs args) - { - OpenHelper.OpenUrl("https://github.com/Ryujinx/Ryujinx/wiki/Changelog#ryujinx-changelog"); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs deleted file mode 100644 index 3bf73318f..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.Designer.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Gtk; - -namespace Ryujinx.UI.Windows -{ - public partial class AmiiboWindow : Window - { - private Box _mainBox; - private ButtonBox _buttonBox; - private Button _scanButton; - private Button _cancelButton; - private CheckButton _randomUuidCheckBox; - private Box _amiiboBox; - private Box _amiiboHeadBox; - private Box _amiiboSeriesBox; - private Label _amiiboSeriesLabel; - private ComboBoxText _amiiboSeriesComboBox; - private Box _amiiboCharsBox; - private Label _amiiboCharsLabel; - private ComboBoxText _amiiboCharsComboBox; - private CheckButton _showAllCheckBox; - private Image _amiiboImage; - private Label _gameUsageLabel; - - private void InitializeComponent() - { - // - // AmiiboWindow - // - CanFocus = false; - Resizable = false; - Modal = true; - WindowPosition = WindowPosition.Center; - DefaultWidth = 600; - DefaultHeight = 470; - TypeHint = Gdk.WindowTypeHint.Dialog; - - // - // _mainBox - // - _mainBox = new Box(Orientation.Vertical, 2); - - // - // _buttonBox - // - _buttonBox = new ButtonBox(Orientation.Horizontal) - { - Margin = 20, - LayoutStyle = ButtonBoxStyle.End, - }; - - // - // _scanButton - // - _scanButton = new Button() - { - Label = "Scan It!", - CanFocus = true, - ReceivesDefault = true, - MarginStart = 10, - }; - _scanButton.Clicked += ScanButton_Pressed; - - // - // _randomUuidCheckBox - // - _randomUuidCheckBox = new CheckButton() - { - Label = "Hack: Use Random Tag Uuid", - TooltipText = "This allows multiple scans of a single Amiibo.\n(used in The Legend of Zelda: Breath of the Wild)", - }; - - // - // _cancelButton - // - _cancelButton = new Button() - { - Label = "Cancel", - CanFocus = true, - ReceivesDefault = true, - MarginStart = 10, - }; - _cancelButton.Clicked += CancelButton_Pressed; - - // - // _amiiboBox - // - _amiiboBox = new Box(Orientation.Vertical, 0); - - // - // _amiiboHeadBox - // - _amiiboHeadBox = new Box(Orientation.Horizontal, 0) - { - Margin = 20, - Hexpand = true, - }; - - // - // _amiiboSeriesBox - // - _amiiboSeriesBox = new Box(Orientation.Horizontal, 0) - { - Hexpand = true, - }; - - // - // _amiiboSeriesLabel - // - _amiiboSeriesLabel = new Label("Amiibo Series:"); - - // - // _amiiboSeriesComboBox - // - _amiiboSeriesComboBox = new ComboBoxText(); - - // - // _amiiboCharsBox - // - _amiiboCharsBox = new Box(Orientation.Horizontal, 0) - { - Hexpand = true, - }; - - // - // _amiiboCharsLabel - // - _amiiboCharsLabel = new Label("Character:"); - - // - // _amiiboCharsComboBox - // - _amiiboCharsComboBox = new ComboBoxText(); - - // - // _showAllCheckBox - // - _showAllCheckBox = new CheckButton() - { - Label = "Show All Amiibo", - }; - - // - // _amiiboImage - // - _amiiboImage = new Image() - { - HeightRequest = 350, - WidthRequest = 350, - }; - - // - // _gameUsageLabel - // - _gameUsageLabel = new Label("") - { - MarginTop = 20, - }; - - ShowComponent(); - } - - private void ShowComponent() - { - _buttonBox.Add(_showAllCheckBox); - _buttonBox.Add(_randomUuidCheckBox); - _buttonBox.Add(_scanButton); - _buttonBox.Add(_cancelButton); - - _amiiboSeriesBox.Add(_amiiboSeriesLabel); - _amiiboSeriesBox.Add(_amiiboSeriesComboBox); - - _amiiboCharsBox.Add(_amiiboCharsLabel); - _amiiboCharsBox.Add(_amiiboCharsComboBox); - - _amiiboHeadBox.Add(_amiiboSeriesBox); - _amiiboHeadBox.Add(_amiiboCharsBox); - - _amiiboBox.PackStart(_amiiboHeadBox, true, true, 0); - _amiiboBox.PackEnd(_gameUsageLabel, false, false, 0); - _amiiboBox.PackEnd(_amiiboImage, false, false, 0); - - _mainBox.Add(_amiiboBox); - _mainBox.PackEnd(_buttonBox, false, false, 0); - - Add(_mainBox); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs deleted file mode 100644 index d8c0b0c0d..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AmiiboWindow.cs +++ /dev/null @@ -1,438 +0,0 @@ -using Gdk; -using Gtk; -using Ryujinx.Common; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Models.Amiibo; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Threading.Tasks; -using Window = Gtk.Window; - -namespace Ryujinx.UI.Windows -{ - public partial class AmiiboWindow : Window - { - private const string DefaultJson = "{ \"amiibo\": [] }"; - - public string AmiiboId { get; private set; } - - public int DeviceId { get; set; } - public string TitleId { get; set; } - public string LastScannedAmiiboId { get; set; } - public bool LastScannedAmiiboShowAll { get; set; } - - public ResponseType Response { get; private set; } - - public bool UseRandomUuid - { - get - { - return _randomUuidCheckBox.Active; - } - } - - private readonly HttpClient _httpClient; - private readonly string _amiiboJsonPath; - - private readonly byte[] _amiiboLogoBytes; - - private List _amiiboList; - - private static readonly AmiiboJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - public AmiiboWindow() : base($"Ryujinx {Program.Version} - Amiibo") - { - Icon = new Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - InitializeComponent(); - - _httpClient = new HttpClient - { - Timeout = TimeSpan.FromSeconds(30), - }; - - Directory.CreateDirectory(System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo")); - - _amiiboJsonPath = System.IO.Path.Join(AppDataManager.BaseDirPath, "system", "amiibo", "Amiibo.json"); - _amiiboList = new List(); - - _amiiboLogoBytes = EmbeddedResources.Read("Ryujinx.UI.Common/Resources/Logo_Amiibo.png"); - _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes); - - _scanButton.Sensitive = false; - _randomUuidCheckBox.Sensitive = false; - - _ = LoadContentAsync(); - } - - private static bool TryGetAmiiboJson(string json, out AmiiboJson amiiboJson) - { - if (string.IsNullOrEmpty(json)) - { - amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson); - - return false; - } - - try - { - amiiboJson = JsonHelper.Deserialize(json, _serializerContext.AmiiboJson); - - return true; - } - catch (JsonException exception) - { - Logger.Error?.Print(LogClass.Application, $"Unable to deserialize amiibo data: {exception}"); - amiiboJson = JsonHelper.Deserialize(DefaultJson, _serializerContext.AmiiboJson); - - return false; - } - } - - private async Task GetMostRecentAmiiboListOrDefaultJson() - { - bool localIsValid = false; - bool remoteIsValid = false; - AmiiboJson amiiboJson = new(); - - try - { - try - { - if (File.Exists(_amiiboJsonPath)) - { - localIsValid = TryGetAmiiboJson(await File.ReadAllTextAsync(_amiiboJsonPath), out amiiboJson); - } - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"Unable to read data from '{_amiiboJsonPath}': {exception}"); - } - - if (!localIsValid || await NeedsUpdate(amiiboJson.LastUpdated)) - { - remoteIsValid = TryGetAmiiboJson(await DownloadAmiiboJson(), out amiiboJson); - } - } - catch (Exception exception) - { - if (!(localIsValid || remoteIsValid)) - { - Logger.Error?.Print(LogClass.Application, $"Couldn't get valid amiibo data: {exception}"); - - // Neither local or remote files are valid JSON, close window. - ShowInfoDialog(); - Close(); - } - else if (!remoteIsValid) - { - Logger.Warning?.Print(LogClass.Application, $"Couldn't update amiibo data: {exception}"); - - // Only the local file is valid, the local one should be used - // but the user should be warned. - ShowInfoDialog(); - } - } - - return amiiboJson; - } - - private async Task LoadContentAsync() - { - AmiiboJson amiiboJson = await GetMostRecentAmiiboListOrDefaultJson(); - - _amiiboList = amiiboJson.Amiibo.OrderBy(amiibo => amiibo.AmiiboSeries).ToList(); - - if (LastScannedAmiiboShowAll) - { - _showAllCheckBox.Click(); - } - - ParseAmiiboData(); - - _showAllCheckBox.Clicked += ShowAllCheckBox_Clicked; - } - - private void ParseAmiiboData() - { - List comboxItemList = new(); - - for (int i = 0; i < _amiiboList.Count; i++) - { - if (!comboxItemList.Contains(_amiiboList[i].AmiiboSeries)) - { - if (!_showAllCheckBox.Active) - { - foreach (var game in _amiiboList[i].GamesSwitch) - { - if (game != null) - { - if (game.GameId.Contains(TitleId)) - { - comboxItemList.Add(_amiiboList[i].AmiiboSeries); - _amiiboSeriesComboBox.Append(_amiiboList[i].AmiiboSeries, _amiiboList[i].AmiiboSeries); - - break; - } - } - } - } - else - { - comboxItemList.Add(_amiiboList[i].AmiiboSeries); - _amiiboSeriesComboBox.Append(_amiiboList[i].AmiiboSeries, _amiiboList[i].AmiiboSeries); - } - } - } - - _amiiboSeriesComboBox.Changed += SeriesComboBox_Changed; - _amiiboCharsComboBox.Changed += CharacterComboBox_Changed; - - if (LastScannedAmiiboId != "") - { - SelectLastScannedAmiibo(); - } - else - { - _amiiboSeriesComboBox.Active = 0; - } - } - - private void SelectLastScannedAmiibo() - { - bool isSet = _amiiboSeriesComboBox.SetActiveId(_amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == LastScannedAmiiboId).AmiiboSeries); - isSet = _amiiboCharsComboBox.SetActiveId(LastScannedAmiiboId); - - if (isSet == false) - { - _amiiboSeriesComboBox.Active = 0; - } - } - - private async Task NeedsUpdate(DateTime oldLastModified) - { - try - { - HttpResponseMessage response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, "https://amiibo.ryujinx.org/")); - - if (response.IsSuccessStatusCode) - { - return response.Content.Headers.LastModified != oldLastModified; - } - } - catch (HttpRequestException exception) - { - Logger.Error?.Print(LogClass.Application, $"Unable to check for amiibo data updates: {exception}"); - } - - return false; - } - - private async Task DownloadAmiiboJson() - { - try - { - HttpResponseMessage response = await _httpClient.GetAsync("https://amiibo.ryujinx.org/"); - - if (response.IsSuccessStatusCode) - { - string amiiboJsonString = await response.Content.ReadAsStringAsync(); - - try - { - using FileStream dlcJsonStream = File.Create(_amiiboJsonPath, 4096, FileOptions.WriteThrough); - dlcJsonStream.Write(Encoding.UTF8.GetBytes(amiiboJsonString)); - } - catch (Exception exception) - { - Logger.Warning?.Print(LogClass.Application, $"Couldn't write amiibo data to file '{_amiiboJsonPath}: {exception}'"); - } - - return amiiboJsonString; - } - - Logger.Error?.Print(LogClass.Application, $"Failed to download amiibo data. Response status code: {response.StatusCode}"); - } - catch (HttpRequestException exception) - { - Logger.Error?.Print(LogClass.Application, $"Failed to request amiibo data: {exception}"); - } - - GtkDialog.CreateInfoDialog("Amiibo API", "An error occured while fetching information from the API."); - - return null; - } - - private async Task UpdateAmiiboPreview(string imageUrl) - { - HttpResponseMessage response = await _httpClient.GetAsync(imageUrl); - - if (response.IsSuccessStatusCode) - { - byte[] amiiboPreviewBytes = await response.Content.ReadAsByteArrayAsync(); - Pixbuf amiiboPreview = new(amiiboPreviewBytes); - - float ratio = Math.Min((float)_amiiboImage.AllocatedWidth / amiiboPreview.Width, - (float)_amiiboImage.AllocatedHeight / amiiboPreview.Height); - - int resizeHeight = (int)(amiiboPreview.Height * ratio); - int resizeWidth = (int)(amiiboPreview.Width * ratio); - - _amiiboImage.Pixbuf = amiiboPreview.ScaleSimple(resizeWidth, resizeHeight, InterpType.Bilinear); - } - else - { - Logger.Error?.Print(LogClass.Application, $"Failed to get amiibo preview. Response status code: {response.StatusCode}"); - } - } - - private static void ShowInfoDialog() - { - GtkDialog.CreateInfoDialog("Amiibo API", "Unable to connect to Amiibo API server. The service may be down or you may need to verify your internet connection is online."); - } - - // - // Events - // - private void SeriesComboBox_Changed(object sender, EventArgs args) - { - _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; - - _amiiboCharsComboBox.RemoveAll(); - - List amiiboSortedList = _amiiboList.Where(amiibo => amiibo.AmiiboSeries == _amiiboSeriesComboBox.ActiveId).OrderBy(amiibo => amiibo.Name).ToList(); - - List comboxItemList = new(); - - for (int i = 0; i < amiiboSortedList.Count; i++) - { - if (!comboxItemList.Contains(amiiboSortedList[i].Head + amiiboSortedList[i].Tail)) - { - if (!_showAllCheckBox.Active) - { - foreach (var game in amiiboSortedList[i].GamesSwitch) - { - if (game != null) - { - if (game.GameId.Contains(TitleId)) - { - comboxItemList.Add(amiiboSortedList[i].Head + amiiboSortedList[i].Tail); - _amiiboCharsComboBox.Append(amiiboSortedList[i].Head + amiiboSortedList[i].Tail, amiiboSortedList[i].Name); - - break; - } - } - } - } - else - { - comboxItemList.Add(amiiboSortedList[i].Head + amiiboSortedList[i].Tail); - _amiiboCharsComboBox.Append(amiiboSortedList[i].Head + amiiboSortedList[i].Tail, amiiboSortedList[i].Name); - } - } - } - - _amiiboCharsComboBox.Changed += CharacterComboBox_Changed; - - _amiiboCharsComboBox.Active = 0; - - _scanButton.Sensitive = true; - _randomUuidCheckBox.Sensitive = true; - } - - private void CharacterComboBox_Changed(object sender, EventArgs args) - { - AmiiboId = _amiiboCharsComboBox.ActiveId; - - _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes); - - string imageUrl = _amiiboList.Find(amiibo => amiibo.Head + amiibo.Tail == _amiiboCharsComboBox.ActiveId).Image; - - var usageStringBuilder = new StringBuilder(); - - for (int i = 0; i < _amiiboList.Count; i++) - { - if (_amiiboList[i].Head + _amiiboList[i].Tail == _amiiboCharsComboBox.ActiveId) - { - bool writable = false; - - foreach (var item in _amiiboList[i].GamesSwitch) - { - if (item.GameId.Contains(TitleId)) - { - foreach (AmiiboApiUsage usageItem in item.AmiiboUsage) - { - usageStringBuilder.Append(Environment.NewLine); - usageStringBuilder.Append($"- {usageItem.Usage.Replace("/", Environment.NewLine + "-")}"); - - writable = usageItem.Write; - } - } - } - - if (usageStringBuilder.Length == 0) - { - usageStringBuilder.Append("Unknown."); - } - - _gameUsageLabel.Text = $"Usage{(writable ? " (Writable)" : "")} : {usageStringBuilder}"; - } - } - - _ = UpdateAmiiboPreview(imageUrl); - } - - private void ShowAllCheckBox_Clicked(object sender, EventArgs e) - { - _amiiboImage.Pixbuf = new Pixbuf(_amiiboLogoBytes); - - _amiiboSeriesComboBox.Changed -= SeriesComboBox_Changed; - _amiiboCharsComboBox.Changed -= CharacterComboBox_Changed; - - _amiiboSeriesComboBox.RemoveAll(); - _amiiboCharsComboBox.RemoveAll(); - - _scanButton.Sensitive = false; - _randomUuidCheckBox.Sensitive = false; - - new Task(ParseAmiiboData).Start(); - } - - private void ScanButton_Pressed(object sender, EventArgs args) - { - LastScannedAmiiboShowAll = _showAllCheckBox.Active; - - Response = ResponseType.Ok; - - Close(); - } - - private void CancelButton_Pressed(object sender, EventArgs args) - { - AmiiboId = ""; - LastScannedAmiiboId = ""; - LastScannedAmiiboShowAll = false; - - Response = ResponseType.Cancel; - - Close(); - } - - protected override void Dispose(bool disposing) - { - _httpClient.Dispose(); - - base.Dispose(disposing); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs deleted file mode 100644 index fcd960df0..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/AvatarWindow.cs +++ /dev/null @@ -1,298 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.FsSystem; -using LibHac.Ncm; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Memory; -using Ryujinx.HLE.FileSystem; -using Ryujinx.UI.Common.Configuration; -using SkiaSharp; -using System; -using System.Buffers.Binary; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Runtime.InteropServices; - -namespace Ryujinx.UI.Windows -{ - public class AvatarWindow : Window - { - public byte[] SelectedProfileImage; - public bool NewUser; - - private static readonly Dictionary _avatarDict = new(); - - private readonly ListStore _listStore; - private readonly IconView _iconView; - private readonly Button _setBackgroungColorButton; - private Gdk.RGBA _backgroundColor; - - public AvatarWindow() : base($"Ryujinx {Program.Version} - Manage Accounts - Avatar") - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - CanFocus = false; - Resizable = false; - Modal = true; - TypeHint = Gdk.WindowTypeHint.Dialog; - - SetDefaultSize(740, 400); - SetPosition(WindowPosition.Center); - - Box vbox = new(Orientation.Vertical, 0); - Add(vbox); - - ScrolledWindow scrolledWindow = new() - { - ShadowType = ShadowType.EtchedIn, - }; - scrolledWindow.SetPolicy(PolicyType.Automatic, PolicyType.Automatic); - - Box hbox = new(Orientation.Horizontal, 0); - - Button chooseButton = new() - { - Label = "Choose", - CanFocus = true, - ReceivesDefault = true, - }; - chooseButton.Clicked += ChooseButton_Pressed; - - _setBackgroungColorButton = new Button() - { - Label = "Set Background Color", - CanFocus = true, - }; - _setBackgroungColorButton.Clicked += SetBackgroungColorButton_Pressed; - - _backgroundColor.Red = 1; - _backgroundColor.Green = 1; - _backgroundColor.Blue = 1; - _backgroundColor.Alpha = 1; - - Button closeButton = new() - { - Label = "Close", - CanFocus = true, - }; - closeButton.Clicked += CloseButton_Pressed; - - vbox.PackStart(scrolledWindow, true, true, 0); - hbox.PackStart(chooseButton, true, true, 0); - hbox.PackStart(_setBackgroungColorButton, true, true, 0); - hbox.PackStart(closeButton, true, true, 0); - vbox.PackStart(hbox, false, false, 0); - - _listStore = new ListStore(typeof(string), typeof(Gdk.Pixbuf)); - _listStore.SetSortColumnId(0, SortType.Ascending); - - _iconView = new IconView(_listStore) - { - ItemWidth = 64, - ItemPadding = 10, - PixbufColumn = 1, - }; - - _iconView.SelectionChanged += IconView_SelectionChanged; - - scrolledWindow.Add(_iconView); - - _iconView.GrabFocus(); - - ProcessAvatars(); - - ShowAll(); - } - - public static void PreloadAvatars(ContentManager contentManager, VirtualFileSystem virtualFileSystem) - { - if (_avatarDict.Count > 0) - { - return; - } - - string contentPath = contentManager.GetInstalledContentPath(0x010000000000080A, StorageId.BuiltInSystem, NcaContentType.Data); - string avatarPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); - - if (!string.IsNullOrWhiteSpace(avatarPath)) - { - using IStorage ncaFileStream = new LocalStorage(avatarPath, FileAccess.Read, FileMode.Open); - - Nca nca = new(virtualFileSystem.KeySet, ncaFileStream); - IFileSystem romfs = nca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.ErrorOnInvalid); - - foreach (var item in romfs.EnumerateEntries()) - { - // TODO: Parse DatabaseInfo.bin and table.bin files for more accuracy. - - if (item.Type == DirectoryEntryType.File && item.FullPath.Contains("chara") && item.FullPath.Contains("szs")) - { - using var file = new UniqueRef(); - - romfs.OpenFile(ref file.Ref, ("/" + item.FullPath).ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - using MemoryStream stream = MemoryStreamManager.Shared.GetStream(); - using MemoryStream streamPng = MemoryStreamManager.Shared.GetStream(); - file.Get.AsStream().CopyTo(stream); - - stream.Position = 0; - - using var avatarImage = new SKBitmap(new SKImageInfo(256, 256, SKColorType.Rgba8888)); - var data = DecompressYaz0(stream); - Marshal.Copy(data, 0, avatarImage.GetPixels(), data.Length); - - avatarImage.Encode(streamPng, SKEncodedImageFormat.Png, 80); - - _avatarDict.Add(item.FullPath, streamPng.ToArray()); - } - } - } - } - - private void ProcessAvatars() - { - _listStore.Clear(); - - foreach (var avatar in _avatarDict) - { - _listStore.AppendValues(avatar.Key, new Gdk.Pixbuf(ProcessImage(avatar.Value), 96, 96)); - } - - _iconView.SelectPath(new TreePath(new[] { 0 })); - } - - private byte[] ProcessImage(byte[] data) - { - using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - - using var avatarImage = SKBitmap.Decode(data); - using var surface = SKSurface.Create(avatarImage.Info); - - var background = new SKColor( - (byte)(_backgroundColor.Red * 255), - (byte)(_backgroundColor.Green * 255), - (byte)(_backgroundColor.Blue * 255), - (byte)(_backgroundColor.Alpha * 255) - ); - var canvas = surface.Canvas; - canvas.Clear(background); - canvas.DrawBitmap(avatarImage, new SKPoint()); - - surface.Flush(); - using var snapshot = surface.Snapshot(); - using var encoded = snapshot.Encode(SKEncodedImageFormat.Jpeg, 80); - encoded.SaveTo(streamJpg); - - return streamJpg.ToArray(); - } - - private void CloseButton_Pressed(object sender, EventArgs e) - { - SelectedProfileImage = null; - - Close(); - } - - private void IconView_SelectionChanged(object sender, EventArgs e) - { - if (_iconView.SelectedItems.Length > 0) - { - _listStore.GetIter(out TreeIter iter, _iconView.SelectedItems[0]); - - SelectedProfileImage = ProcessImage(_avatarDict[(string)_listStore.GetValue(iter, 0)]); - } - } - - private void SetBackgroungColorButton_Pressed(object sender, EventArgs e) - { - using ColorChooserDialog colorChooserDialog = new("Set Background Color", this); - - colorChooserDialog.UseAlpha = false; - colorChooserDialog.Rgba = _backgroundColor; - - if (colorChooserDialog.Run() == (int)ResponseType.Ok) - { - _backgroundColor = colorChooserDialog.Rgba; - - ProcessAvatars(); - } - - colorChooserDialog.Hide(); - } - - private void ChooseButton_Pressed(object sender, EventArgs e) - { - Close(); - } - - private static byte[] DecompressYaz0(Stream stream) - { - using BinaryReader reader = new(stream); - - reader.ReadInt32(); // Magic - - uint decodedLength = BinaryPrimitives.ReverseEndianness(reader.ReadUInt32()); - - reader.ReadInt64(); // Padding - - byte[] input = new byte[stream.Length - stream.Position]; - stream.ReadExactly(input, 0, input.Length); - - long inputOffset = 0; - - byte[] output = new byte[decodedLength]; - long outputOffset = 0; - - ushort mask = 0; - byte header = 0; - - while (outputOffset < decodedLength) - { - if ((mask >>= 1) == 0) - { - header = input[inputOffset++]; - mask = 0x80; - } - - if ((header & mask) > 0) - { - if (outputOffset == output.Length) - { - break; - } - - output[outputOffset++] = input[inputOffset++]; - } - else - { - byte byte1 = input[inputOffset++]; - byte byte2 = input[inputOffset++]; - - int dist = ((byte1 & 0xF) << 8) | byte2; - int position = (int)outputOffset - (dist + 1); - - int length = byte1 >> 4; - if (length == 0) - { - length = input[inputOffset++] + 0x12; - } - else - { - length += 2; - } - - while (length-- > 0) - { - output[outputOffset++] = output[position++]; - } - } - } - - return output; - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs deleted file mode 100644 index d9f01918f..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.cs +++ /dev/null @@ -1,163 +0,0 @@ -using Gtk; -using LibHac.Tools.FsSystem; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Windows -{ - public class CheatWindow : Window - { - private readonly string _enabledCheatsPath; - private readonly bool _noCheatsFound; - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Label _baseTitleInfoLabel; - [GUI] TextView _buildIdTextView; - [GUI] TreeView _cheatTreeView; - [GUI] Button _saveButton; -#pragma warning restore CS0649, IDE0044 - - public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Gtk3.UI.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { } - - private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow")) - { - builder.Autoconnect(this); - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - _baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]"; - _buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetBuildId(virtualFileSystem, checkLevel, titlePath)}"; - - string modsBasePath = ModLoader.GetModsBasePath(); - string titleModsPath = ModLoader.GetApplicationDir(modsBasePath, titleId.ToString("X16")); - - _enabledCheatsPath = System.IO.Path.Combine(titleModsPath, "cheats", "enabled.txt"); - - _cheatTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string), typeof(string)); - - CellRendererToggle enableToggle = new(); - enableToggle.Toggled += (sender, args) => - { - _cheatTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - bool newValue = !(bool)_cheatTreeView.Model.GetValue(treeIter, 0); - _cheatTreeView.Model.SetValue(treeIter, 0, newValue); - - if (_cheatTreeView.Model.IterChildren(out TreeIter childIter, treeIter)) - { - do - { - _cheatTreeView.Model.SetValue(childIter, 0, newValue); - } - while (_cheatTreeView.Model.IterNext(ref childIter)); - } - }; - - _cheatTreeView.AppendColumn("Enabled", enableToggle, "active", 0); - _cheatTreeView.AppendColumn("Name", new CellRendererText(), "text", 1); - _cheatTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); - - var buildIdColumn = _cheatTreeView.AppendColumn("Build Id", new CellRendererText(), "text", 3); - buildIdColumn.Visible = false; - - string[] enabled = Array.Empty(); - - if (File.Exists(_enabledCheatsPath)) - { - enabled = File.ReadAllLines(_enabledCheatsPath); - } - - int cheatAdded = 0; - - var mods = new ModLoader.ModCache(); - - ModLoader.QueryContentsDir(mods, new DirectoryInfo(System.IO.Path.Combine(modsBasePath, "contents")), titleId); - - string currentCheatFile = string.Empty; - string buildId = string.Empty; - TreeIter parentIter = default; - - foreach (var cheat in mods.Cheats) - { - if (cheat.Path.FullName != currentCheatFile) - { - currentCheatFile = cheat.Path.FullName; - string parentPath = currentCheatFile.Replace(titleModsPath, ""); - - buildId = System.IO.Path.GetFileNameWithoutExtension(currentCheatFile).ToUpper(); - parentIter = ((TreeStore)_cheatTreeView.Model).AppendValues(false, buildId, parentPath, ""); - } - - string cleanName = cheat.Name[1..^7]; - ((TreeStore)_cheatTreeView.Model).AppendValues(parentIter, enabled.Contains($"{buildId}-{cheat.Name}"), cleanName, "", buildId); - - cheatAdded++; - } - - if (cheatAdded == 0) - { - ((TreeStore)_cheatTreeView.Model).AppendValues(false, "No Cheats Found", "", ""); - _cheatTreeView.GetColumn(0).Visible = false; - - _noCheatsFound = true; - - _saveButton.Visible = false; - } - - _cheatTreeView.ExpandAll(); - } - - private void SaveButton_Clicked(object sender, EventArgs args) - { - if (_noCheatsFound) - { - return; - } - - List enabledCheats = new(); - - if (_cheatTreeView.Model.GetIterFirst(out TreeIter parentIter)) - { - do - { - if (_cheatTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) - { - do - { - var enabled = (bool)_cheatTreeView.Model.GetValue(childIter, 0); - - if (enabled) - { - var name = _cheatTreeView.Model.GetValue(childIter, 1).ToString(); - var buildId = _cheatTreeView.Model.GetValue(childIter, 3).ToString(); - - enabledCheats.Add($"{buildId}-<{name} Cheat>"); - } - } - while (_cheatTreeView.Model.IterNext(ref childIter)); - } - } - while (_cheatTreeView.Model.IterNext(ref parentIter)); - } - - Directory.CreateDirectory(System.IO.Path.GetDirectoryName(_enabledCheatsPath)); - - File.WriteAllLines(_enabledCheatsPath, enabledCheats); - - Dispose(); - } - - private void CancelButton_Clicked(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade deleted file mode 100644 index 9a165f1a8..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/CheatWindow.glade +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - False - Ryujinx - Cheat Manager - 440 - 550 - - - True - False - vertical - - - True - False - vertical - - - True - False - 10 - 10 - Available Cheats - - - False - True - 0 - - - - - True - 10 - center - 10 - False - False - - - False - True - 1 - - - - - True - True - 10 - 10 - in - - - True - False - - - True - True - - - - - - - - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - - - True - False - 10 - 10 - end - - - Save - True - True - True - 10 - 2 - 2 - - - - True - True - 0 - - - - - Cancel - True - True - True - 10 - 2 - 2 - - - - True - True - 1 - - - - - True - True - 1 - - - - - False - True - 1 - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs deleted file mode 100644 index d0b8266f4..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.cs +++ /dev/null @@ -1,1232 +0,0 @@ -using Gtk; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Hid.Controller; -using Ryujinx.Common.Configuration.Hid.Controller.Motion; -using Ryujinx.Common.Configuration.Hid.Keyboard; -using Ryujinx.Common.Logging; -using Ryujinx.Common.Utilities; -using Ryujinx.Input; -using Ryujinx.Input.Assigner; -using Ryujinx.Input.GTK3; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Helper; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Text.Json; -using System.Threading; -using Button = Ryujinx.Input.Button; -using ConfigGamepadInputId = Ryujinx.Common.Configuration.Hid.Controller.GamepadInputId; -using ConfigStickInputId = Ryujinx.Common.Configuration.Hid.Controller.StickInputId; -using GUI = Gtk.Builder.ObjectAttribute; -using Key = Ryujinx.Common.Configuration.Hid.Key; - -namespace Ryujinx.UI.Windows -{ - public class ControllerWindow : Window - { - private readonly PlayerIndex _playerIndex; - private readonly InputConfig _inputConfig; - - private bool _isWaitingForInput; - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Adjustment _controllerStrongRumble; - [GUI] Adjustment _controllerWeakRumble; - [GUI] Adjustment _controllerDeadzoneLeft; - [GUI] Adjustment _controllerDeadzoneRight; - [GUI] Adjustment _controllerRangeLeft; - [GUI] Adjustment _controllerRangeRight; - [GUI] Adjustment _controllerTriggerThreshold; - [GUI] Adjustment _slotNumber; - [GUI] Adjustment _altSlotNumber; - [GUI] Adjustment _sensitivity; - [GUI] Adjustment _gyroDeadzone; - [GUI] CheckButton _enableMotion; - [GUI] CheckButton _enableCemuHook; - [GUI] CheckButton _mirrorInput; - [GUI] Entry _dsuServerHost; - [GUI] Entry _dsuServerPort; - [GUI] ComboBoxText _inputDevice; - [GUI] ComboBoxText _profile; - [GUI] Box _settingsBox; - [GUI] Box _motionAltBox; - [GUI] Box _motionBox; - [GUI] Box _dsuServerHostBox; - [GUI] Box _dsuServerPortBox; - [GUI] Box _motionControllerSlot; - [GUI] Grid _leftStickKeyboard; - [GUI] Grid _leftStickController; - [GUI] Box _deadZoneLeftBox; - [GUI] Box _rangeLeftBox; - [GUI] Grid _rightStickKeyboard; - [GUI] Grid _rightStickController; - [GUI] Box _deadZoneRightBox; - [GUI] Box _rangeRightBox; - [GUI] Grid _leftSideTriggerBox; - [GUI] Grid _rightSideTriggerBox; - [GUI] Box _triggerThresholdBox; - [GUI] ComboBoxText _controllerType; - [GUI] ToggleButton _lStick; - [GUI] CheckButton _invertLStickX; - [GUI] CheckButton _invertLStickY; - [GUI] CheckButton _rotateL90CW; - [GUI] ToggleButton _lStickUp; - [GUI] ToggleButton _lStickDown; - [GUI] ToggleButton _lStickLeft; - [GUI] ToggleButton _lStickRight; - [GUI] ToggleButton _lStickButton; - [GUI] ToggleButton _dpadUp; - [GUI] ToggleButton _dpadDown; - [GUI] ToggleButton _dpadLeft; - [GUI] ToggleButton _dpadRight; - [GUI] ToggleButton _minus; - [GUI] ToggleButton _l; - [GUI] ToggleButton _zL; - [GUI] ToggleButton _rStick; - [GUI] CheckButton _invertRStickX; - [GUI] CheckButton _invertRStickY; - [GUI] CheckButton _rotateR90CW; - [GUI] ToggleButton _rStickUp; - [GUI] ToggleButton _rStickDown; - [GUI] ToggleButton _rStickLeft; - [GUI] ToggleButton _rStickRight; - [GUI] ToggleButton _rStickButton; - [GUI] ToggleButton _a; - [GUI] ToggleButton _b; - [GUI] ToggleButton _x; - [GUI] ToggleButton _y; - [GUI] ToggleButton _plus; - [GUI] ToggleButton _r; - [GUI] ToggleButton _zR; - [GUI] ToggleButton _lSl; - [GUI] ToggleButton _lSr; - [GUI] ToggleButton _rSl; - [GUI] ToggleButton _rSr; - [GUI] Image _controllerImage; - [GUI] CheckButton _enableRumble; - [GUI] Box _rumbleBox; -#pragma warning restore CS0649, IDE0044 - - private readonly MainWindow _mainWindow; - private readonly IGamepadDriver _gtk3KeyboardDriver; - private IGamepad _selectedGamepad; - private bool _mousePressed; - private bool _middleMousePressed; - - private static readonly InputConfigJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - - public ControllerWindow(MainWindow mainWindow, PlayerIndex controllerId) : this(mainWindow, new Builder("Ryujinx.Gtk3.UI.Windows.ControllerWindow.glade"), controllerId) { } - - private ControllerWindow(MainWindow mainWindow, Builder builder, PlayerIndex controllerId) : base(builder.GetRawOwnedObject("_controllerWin")) - { - _mainWindow = mainWindow; - _selectedGamepad = null; - - // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused... - _gtk3KeyboardDriver = new GTK3KeyboardDriver(this); - - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - builder.Autoconnect(this); - - _playerIndex = controllerId; - _inputConfig = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == _playerIndex); - - Title = $"Ryujinx - Controller Settings - {_playerIndex}"; - - if (_playerIndex == PlayerIndex.Handheld) - { - _controllerType.Append(ControllerType.Handheld.ToString(), "Handheld"); - _controllerType.Sensitive = false; - } - else - { - _controllerType.Append(ControllerType.ProController.ToString(), "Pro Controller"); - _controllerType.Append(ControllerType.JoyconPair.ToString(), "Joycon Pair"); - _controllerType.Append(ControllerType.JoyconLeft.ToString(), "Joycon Left"); - _controllerType.Append(ControllerType.JoyconRight.ToString(), "Joycon Right"); - } - - _controllerType.Active = 0; // Set initial value to first in list. - - // Bind Events. - _lStick.Clicked += ButtonForStick_Pressed; - _lStickUp.Clicked += Button_Pressed; - _lStickDown.Clicked += Button_Pressed; - _lStickLeft.Clicked += Button_Pressed; - _lStickRight.Clicked += Button_Pressed; - _lStickButton.Clicked += Button_Pressed; - _dpadUp.Clicked += Button_Pressed; - _dpadDown.Clicked += Button_Pressed; - _dpadLeft.Clicked += Button_Pressed; - _dpadRight.Clicked += Button_Pressed; - _minus.Clicked += Button_Pressed; - _l.Clicked += Button_Pressed; - _zL.Clicked += Button_Pressed; - _lSl.Clicked += Button_Pressed; - _lSr.Clicked += Button_Pressed; - _rStick.Clicked += ButtonForStick_Pressed; - _rStickUp.Clicked += Button_Pressed; - _rStickDown.Clicked += Button_Pressed; - _rStickLeft.Clicked += Button_Pressed; - _rStickRight.Clicked += Button_Pressed; - _rStickButton.Clicked += Button_Pressed; - _a.Clicked += Button_Pressed; - _b.Clicked += Button_Pressed; - _x.Clicked += Button_Pressed; - _y.Clicked += Button_Pressed; - _plus.Clicked += Button_Pressed; - _r.Clicked += Button_Pressed; - _zR.Clicked += Button_Pressed; - _rSl.Clicked += Button_Pressed; - _rSr.Clicked += Button_Pressed; - _enableCemuHook.Clicked += CemuHookCheckButtonPressed; - - // Setup current values. - UpdateInputDeviceList(); - SetAvailableOptions(); - - ClearValues(); - if (_inputDevice.ActiveId != null) - { - SetCurrentValues(); - } - - mainWindow.InputManager.GamepadDriver.OnGamepadConnected += HandleOnGamepadConnected; - mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected += HandleOnGamepadDisconnected; - - _mainWindow.RendererWidget?.NpadManager.BlockInputUpdates(); - } - - private void CemuHookCheckButtonPressed(object sender, EventArgs e) - { - UpdateCemuHookSpecificFieldsVisibility(); - } - - private void HandleOnGamepadDisconnected(string id) - { - Application.Invoke(delegate - { - UpdateInputDeviceList(); - }); - } - - private void HandleOnGamepadConnected(string id) - { - Application.Invoke(delegate - { - UpdateInputDeviceList(); - }); - } - - protected override void OnDestroyed() - { - _mainWindow.InputManager.GamepadDriver.OnGamepadConnected -= HandleOnGamepadConnected; - _mainWindow.InputManager.GamepadDriver.OnGamepadDisconnected -= HandleOnGamepadDisconnected; - - _mainWindow.RendererWidget?.NpadManager.UnblockInputUpdates(); - - _selectedGamepad?.Dispose(); - - _gtk3KeyboardDriver.Dispose(); - } - - private static string GetShortGamepadName(string str) - { - const string ShrinkChars = "..."; - const int MaxSize = 50; - - if (str.Length > MaxSize) - { - return $"{str.AsSpan(0, MaxSize - ShrinkChars.Length)}{ShrinkChars}"; - } - - return str; - } - - private void UpdateInputDeviceList() - { - _inputDevice.RemoveAll(); - _inputDevice.Append("disabled", "Disabled"); - _inputDevice.SetActiveId("disabled"); - - foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds) - { - IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id); - - if (gamepad != null) - { - _inputDevice.Append($"keyboard/{id}", GetShortGamepadName($"{gamepad.Name} ({id})")); - - gamepad.Dispose(); - } - } - - foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds) - { - IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); - - if (gamepad != null) - { - _inputDevice.Append($"controller/{id}", GetShortGamepadName($"{gamepad.Name} ({id})")); - - gamepad.Dispose(); - } - } - - switch (_inputConfig) - { - case StandardKeyboardInputConfig keyboard: - _inputDevice.SetActiveId($"keyboard/{keyboard.Id}"); - break; - case StandardControllerInputConfig controller: - _inputDevice.SetActiveId($"controller/{controller.Id}"); - break; - } - } - - private void UpdateCemuHookSpecificFieldsVisibility() - { - if (_enableCemuHook.Active) - { - _dsuServerHostBox.Show(); - _dsuServerPortBox.Show(); - _motionControllerSlot.Show(); - _motionAltBox.Show(); - _mirrorInput.Show(); - } - else - { - _dsuServerHostBox.Hide(); - _dsuServerPortBox.Hide(); - _motionControllerSlot.Hide(); - _motionAltBox.Hide(); - _mirrorInput.Hide(); - } - } - - private void SetAvailableOptions() - { - if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("keyboard")) - { - ShowAll(); - _leftStickController.Hide(); - _rightStickController.Hide(); - _deadZoneLeftBox.Hide(); - _deadZoneRightBox.Hide(); - _rangeLeftBox.Hide(); - _rangeRightBox.Hide(); - _triggerThresholdBox.Hide(); - _motionBox.Hide(); - _rumbleBox.Hide(); - } - else if (_inputDevice.ActiveId != null && _inputDevice.ActiveId.StartsWith("controller")) - { - ShowAll(); - _leftStickKeyboard.Hide(); - _rightStickKeyboard.Hide(); - - UpdateCemuHookSpecificFieldsVisibility(); - } - else - { - _settingsBox.Hide(); - } - - ClearValues(); - } - - private void SetCurrentValues() - { - SetControllerSpecificFields(); - - SetProfiles(); - - if (_inputDevice.ActiveId.StartsWith("keyboard") && _inputConfig is StandardKeyboardInputConfig) - { - SetValues(_inputConfig); - } - else if (_inputDevice.ActiveId.StartsWith("controller") && _inputConfig is StandardControllerInputConfig) - { - SetValues(_inputConfig); - } - } - - private void SetControllerSpecificFields() - { - _leftSideTriggerBox.Hide(); - _rightSideTriggerBox.Hide(); - _motionAltBox.Hide(); - - switch (_controllerType.ActiveId) - { - case "JoyconLeft": - _leftSideTriggerBox.Show(); - break; - case "JoyconRight": - _rightSideTriggerBox.Show(); - break; - case "JoyconPair": - _motionAltBox.Show(); - break; - } - - if (!OperatingSystem.IsMacOS()) - { - _controllerImage.Pixbuf = _controllerType.ActiveId switch - { - "ProController" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_ProCon.svg", 400, 400), - "JoyconLeft" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_JoyConLeft.svg", 400, 500), - "JoyconRight" => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_JoyConRight.svg", 400, 500), - _ => new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Controller_JoyConPair.svg", 400, 500), - }; - } - } - - private void ClearValues() - { - _lStick.Label = "Unbound"; - _lStickUp.Label = "Unbound"; - _lStickDown.Label = "Unbound"; - _lStickLeft.Label = "Unbound"; - _lStickRight.Label = "Unbound"; - _lStickButton.Label = "Unbound"; - _dpadUp.Label = "Unbound"; - _dpadDown.Label = "Unbound"; - _dpadLeft.Label = "Unbound"; - _dpadRight.Label = "Unbound"; - _minus.Label = "Unbound"; - _l.Label = "Unbound"; - _zL.Label = "Unbound"; - _lSl.Label = "Unbound"; - _lSr.Label = "Unbound"; - _rStick.Label = "Unbound"; - _rStickUp.Label = "Unbound"; - _rStickDown.Label = "Unbound"; - _rStickLeft.Label = "Unbound"; - _rStickRight.Label = "Unbound"; - _rStickButton.Label = "Unbound"; - _a.Label = "Unbound"; - _b.Label = "Unbound"; - _x.Label = "Unbound"; - _y.Label = "Unbound"; - _plus.Label = "Unbound"; - _r.Label = "Unbound"; - _zR.Label = "Unbound"; - _rSl.Label = "Unbound"; - _rSr.Label = "Unbound"; - _controllerStrongRumble.Value = 1; - _controllerWeakRumble.Value = 1; - _controllerDeadzoneLeft.Value = 0; - _controllerDeadzoneRight.Value = 0; - _controllerRangeLeft.Value = 1; - _controllerRangeRight.Value = 1; - _controllerTriggerThreshold.Value = 0; - _mirrorInput.Active = false; - _enableMotion.Active = false; - _enableCemuHook.Active = false; - _slotNumber.Value = 0; - _altSlotNumber.Value = 0; - _sensitivity.Value = 100; - _gyroDeadzone.Value = 1; - _dsuServerHost.Buffer.Text = ""; - _dsuServerPort.Buffer.Text = ""; - _enableRumble.Active = false; - } - - private void SetValues(InputConfig config) - { - switch (config) - { - case StandardKeyboardInputConfig keyboardConfig: - if (!_controllerType.SetActiveId(keyboardConfig.ControllerType.ToString())) - { - _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld - ? ControllerType.Handheld.ToString() - : ControllerType.ProController.ToString()); - } - - _lStickUp.Label = keyboardConfig.LeftJoyconStick.StickUp.ToString(); - _lStickDown.Label = keyboardConfig.LeftJoyconStick.StickDown.ToString(); - _lStickLeft.Label = keyboardConfig.LeftJoyconStick.StickLeft.ToString(); - _lStickRight.Label = keyboardConfig.LeftJoyconStick.StickRight.ToString(); - _lStickButton.Label = keyboardConfig.LeftJoyconStick.StickButton.ToString(); - _dpadUp.Label = keyboardConfig.LeftJoycon.DpadUp.ToString(); - _dpadDown.Label = keyboardConfig.LeftJoycon.DpadDown.ToString(); - _dpadLeft.Label = keyboardConfig.LeftJoycon.DpadLeft.ToString(); - _dpadRight.Label = keyboardConfig.LeftJoycon.DpadRight.ToString(); - _minus.Label = keyboardConfig.LeftJoycon.ButtonMinus.ToString(); - _l.Label = keyboardConfig.LeftJoycon.ButtonL.ToString(); - _zL.Label = keyboardConfig.LeftJoycon.ButtonZl.ToString(); - _lSl.Label = keyboardConfig.LeftJoycon.ButtonSl.ToString(); - _lSr.Label = keyboardConfig.LeftJoycon.ButtonSr.ToString(); - _rStickUp.Label = keyboardConfig.RightJoyconStick.StickUp.ToString(); - _rStickDown.Label = keyboardConfig.RightJoyconStick.StickDown.ToString(); - _rStickLeft.Label = keyboardConfig.RightJoyconStick.StickLeft.ToString(); - _rStickRight.Label = keyboardConfig.RightJoyconStick.StickRight.ToString(); - _rStickButton.Label = keyboardConfig.RightJoyconStick.StickButton.ToString(); - _a.Label = keyboardConfig.RightJoycon.ButtonA.ToString(); - _b.Label = keyboardConfig.RightJoycon.ButtonB.ToString(); - _x.Label = keyboardConfig.RightJoycon.ButtonX.ToString(); - _y.Label = keyboardConfig.RightJoycon.ButtonY.ToString(); - _plus.Label = keyboardConfig.RightJoycon.ButtonPlus.ToString(); - _r.Label = keyboardConfig.RightJoycon.ButtonR.ToString(); - _zR.Label = keyboardConfig.RightJoycon.ButtonZr.ToString(); - _rSl.Label = keyboardConfig.RightJoycon.ButtonSl.ToString(); - _rSr.Label = keyboardConfig.RightJoycon.ButtonSr.ToString(); - break; - - case StandardControllerInputConfig controllerConfig: - if (!_controllerType.SetActiveId(controllerConfig.ControllerType.ToString())) - { - _controllerType.SetActiveId(_playerIndex == PlayerIndex.Handheld - ? ControllerType.Handheld.ToString() - : ControllerType.ProController.ToString()); - } - - _lStick.Label = controllerConfig.LeftJoyconStick.Joystick.ToString(); - _invertLStickX.Active = controllerConfig.LeftJoyconStick.InvertStickX; - _invertLStickY.Active = controllerConfig.LeftJoyconStick.InvertStickY; - _rotateL90CW.Active = controllerConfig.LeftJoyconStick.Rotate90CW; - _lStickButton.Label = controllerConfig.LeftJoyconStick.StickButton.ToString(); - _dpadUp.Label = controllerConfig.LeftJoycon.DpadUp.ToString(); - _dpadDown.Label = controllerConfig.LeftJoycon.DpadDown.ToString(); - _dpadLeft.Label = controllerConfig.LeftJoycon.DpadLeft.ToString(); - _dpadRight.Label = controllerConfig.LeftJoycon.DpadRight.ToString(); - _minus.Label = controllerConfig.LeftJoycon.ButtonMinus.ToString(); - _l.Label = controllerConfig.LeftJoycon.ButtonL.ToString(); - _zL.Label = controllerConfig.LeftJoycon.ButtonZl.ToString(); - _lSl.Label = controllerConfig.LeftJoycon.ButtonSl.ToString(); - _lSr.Label = controllerConfig.LeftJoycon.ButtonSr.ToString(); - _rStick.Label = controllerConfig.RightJoyconStick.Joystick.ToString(); - _invertRStickX.Active = controllerConfig.RightJoyconStick.InvertStickX; - _invertRStickY.Active = controllerConfig.RightJoyconStick.InvertStickY; - _rotateR90CW.Active = controllerConfig.RightJoyconStick.Rotate90CW; - _rStickButton.Label = controllerConfig.RightJoyconStick.StickButton.ToString(); - _a.Label = controllerConfig.RightJoycon.ButtonA.ToString(); - _b.Label = controllerConfig.RightJoycon.ButtonB.ToString(); - _x.Label = controllerConfig.RightJoycon.ButtonX.ToString(); - _y.Label = controllerConfig.RightJoycon.ButtonY.ToString(); - _plus.Label = controllerConfig.RightJoycon.ButtonPlus.ToString(); - _r.Label = controllerConfig.RightJoycon.ButtonR.ToString(); - _zR.Label = controllerConfig.RightJoycon.ButtonZr.ToString(); - _rSl.Label = controllerConfig.RightJoycon.ButtonSl.ToString(); - _rSr.Label = controllerConfig.RightJoycon.ButtonSr.ToString(); - _controllerStrongRumble.Value = controllerConfig.Rumble.StrongRumble; - _controllerWeakRumble.Value = controllerConfig.Rumble.WeakRumble; - _enableRumble.Active = controllerConfig.Rumble.EnableRumble; - _controllerDeadzoneLeft.Value = controllerConfig.DeadzoneLeft; - _controllerDeadzoneRight.Value = controllerConfig.DeadzoneRight; - _controllerRangeLeft.Value = controllerConfig.RangeLeft; - _controllerRangeRight.Value = controllerConfig.RangeRight; - _controllerTriggerThreshold.Value = controllerConfig.TriggerThreshold; - _sensitivity.Value = controllerConfig.Motion.Sensitivity; - _gyroDeadzone.Value = controllerConfig.Motion.GyroDeadzone; - _enableMotion.Active = controllerConfig.Motion.EnableMotion; - _enableCemuHook.Active = controllerConfig.Motion.MotionBackend == MotionInputBackendType.CemuHook; - - // If both stick ranges are 0 (usually indicative of an outdated profile load) then both sticks will be set to 1.0. - if (_controllerRangeLeft.Value <= 0.0 && _controllerRangeRight.Value <= 0.0) - { - _controllerRangeLeft.Value = 1.0; - _controllerRangeRight.Value = 1.0; - - Logger.Info?.Print(LogClass.Application, $"{config.PlayerIndex} stick range reset. Save the profile now to update your configuration"); - } - - if (controllerConfig.Motion is CemuHookMotionConfigController cemuHookMotionConfig) - { - _slotNumber.Value = cemuHookMotionConfig.Slot; - _altSlotNumber.Value = cemuHookMotionConfig.AltSlot; - _mirrorInput.Active = cemuHookMotionConfig.MirrorInput; - _dsuServerHost.Buffer.Text = cemuHookMotionConfig.DsuServerHost; - _dsuServerPort.Buffer.Text = cemuHookMotionConfig.DsuServerPort.ToString(); - } - - break; - } - } - - private InputConfig GetValues() - { - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { -#pragma warning disable CA1806, IDE0055 // Disable formatting - Enum.TryParse(_lStickUp.Label, out Key lStickUp); - Enum.TryParse(_lStickDown.Label, out Key lStickDown); - Enum.TryParse(_lStickLeft.Label, out Key lStickLeft); - Enum.TryParse(_lStickRight.Label, out Key lStickRight); - Enum.TryParse(_lStickButton.Label, out Key lStickButton); - Enum.TryParse(_dpadUp.Label, out Key lDPadUp); - Enum.TryParse(_dpadDown.Label, out Key lDPadDown); - Enum.TryParse(_dpadLeft.Label, out Key lDPadLeft); - Enum.TryParse(_dpadRight.Label, out Key lDPadRight); - Enum.TryParse(_minus.Label, out Key lButtonMinus); - Enum.TryParse(_l.Label, out Key lButtonL); - Enum.TryParse(_zL.Label, out Key lButtonZl); - Enum.TryParse(_lSl.Label, out Key lButtonSl); - Enum.TryParse(_lSr.Label, out Key lButtonSr); - - Enum.TryParse(_rStickUp.Label, out Key rStickUp); - Enum.TryParse(_rStickDown.Label, out Key rStickDown); - Enum.TryParse(_rStickLeft.Label, out Key rStickLeft); - Enum.TryParse(_rStickRight.Label, out Key rStickRight); - Enum.TryParse(_rStickButton.Label, out Key rStickButton); - Enum.TryParse(_a.Label, out Key rButtonA); - Enum.TryParse(_b.Label, out Key rButtonB); - Enum.TryParse(_x.Label, out Key rButtonX); - Enum.TryParse(_y.Label, out Key rButtonY); - Enum.TryParse(_plus.Label, out Key rButtonPlus); - Enum.TryParse(_r.Label, out Key rButtonR); - Enum.TryParse(_zR.Label, out Key rButtonZr); - Enum.TryParse(_rSl.Label, out Key rButtonSl); - Enum.TryParse(_rSr.Label, out Key rButtonSr); -#pragma warning restore CA1806, IDE0055 - - return new StandardKeyboardInputConfig - { - Backend = InputBackendType.WindowKeyboard, - Version = InputConfig.CurrentVersion, - Id = _inputDevice.ActiveId.Split("/")[1], - ControllerType = Enum.Parse(_controllerType.ActiveId), - PlayerIndex = _playerIndex, - LeftJoycon = new LeftJoyconCommonConfig - { - ButtonMinus = lButtonMinus, - ButtonL = lButtonL, - ButtonZl = lButtonZl, - ButtonSl = lButtonSl, - ButtonSr = lButtonSr, - DpadUp = lDPadUp, - DpadDown = lDPadDown, - DpadLeft = lDPadLeft, - DpadRight = lDPadRight, - }, - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = lStickUp, - StickDown = lStickDown, - StickLeft = lStickLeft, - StickRight = lStickRight, - StickButton = lStickButton, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = rButtonA, - ButtonB = rButtonB, - ButtonX = rButtonX, - ButtonY = rButtonY, - ButtonPlus = rButtonPlus, - ButtonR = rButtonR, - ButtonZr = rButtonZr, - ButtonSl = rButtonSl, - ButtonSr = rButtonSr, - }, - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = rStickUp, - StickDown = rStickDown, - StickLeft = rStickLeft, - StickRight = rStickRight, - StickButton = rStickButton, - }, - }; - } - - if (_inputDevice.ActiveId.StartsWith("controller")) - { -#pragma warning disable CA1806, IDE0055 // Disable formatting - Enum.TryParse(_lStick.Label, out ConfigStickInputId lStick); - Enum.TryParse(_lStickButton.Label, out ConfigGamepadInputId lStickButton); - Enum.TryParse(_minus.Label, out ConfigGamepadInputId lButtonMinus); - Enum.TryParse(_l.Label, out ConfigGamepadInputId lButtonL); - Enum.TryParse(_zL.Label, out ConfigGamepadInputId lButtonZl); - Enum.TryParse(_lSl.Label, out ConfigGamepadInputId lButtonSl); - Enum.TryParse(_lSr.Label, out ConfigGamepadInputId lButtonSr); - Enum.TryParse(_dpadUp.Label, out ConfigGamepadInputId lDPadUp); - Enum.TryParse(_dpadDown.Label, out ConfigGamepadInputId lDPadDown); - Enum.TryParse(_dpadLeft.Label, out ConfigGamepadInputId lDPadLeft); - Enum.TryParse(_dpadRight.Label, out ConfigGamepadInputId lDPadRight); - - Enum.TryParse(_rStick.Label, out ConfigStickInputId rStick); - Enum.TryParse(_rStickButton.Label, out ConfigGamepadInputId rStickButton); - Enum.TryParse(_a.Label, out ConfigGamepadInputId rButtonA); - Enum.TryParse(_b.Label, out ConfigGamepadInputId rButtonB); - Enum.TryParse(_x.Label, out ConfigGamepadInputId rButtonX); - Enum.TryParse(_y.Label, out ConfigGamepadInputId rButtonY); - Enum.TryParse(_plus.Label, out ConfigGamepadInputId rButtonPlus); - Enum.TryParse(_r.Label, out ConfigGamepadInputId rButtonR); - Enum.TryParse(_zR.Label, out ConfigGamepadInputId rButtonZr); - Enum.TryParse(_rSl.Label, out ConfigGamepadInputId rButtonSl); - Enum.TryParse(_rSr.Label, out ConfigGamepadInputId rButtonSr); - - int.TryParse(_dsuServerPort.Buffer.Text, out int port); -#pragma warning restore CA1806, IDE0055 - - MotionConfigController motionConfig; - - if (_enableCemuHook.Active) - { - motionConfig = new CemuHookMotionConfigController - { - MotionBackend = MotionInputBackendType.CemuHook, - EnableMotion = _enableMotion.Active, - Sensitivity = (int)_sensitivity.Value, - GyroDeadzone = _gyroDeadzone.Value, - MirrorInput = _mirrorInput.Active, - Slot = (int)_slotNumber.Value, - AltSlot = (int)_altSlotNumber.Value, - DsuServerHost = _dsuServerHost.Buffer.Text, - DsuServerPort = port, - }; - } - else - { - motionConfig = new StandardMotionConfigController - { - MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = _enableMotion.Active, - Sensitivity = (int)_sensitivity.Value, - GyroDeadzone = _gyroDeadzone.Value, - }; - } - - return new StandardControllerInputConfig - { - Backend = InputBackendType.GamepadSDL2, - Version = InputConfig.CurrentVersion, - Id = _inputDevice.ActiveId.Split("/")[1].Split(" ")[0], - ControllerType = Enum.Parse(_controllerType.ActiveId), - PlayerIndex = _playerIndex, - DeadzoneLeft = (float)_controllerDeadzoneLeft.Value, - DeadzoneRight = (float)_controllerDeadzoneRight.Value, - RangeLeft = (float)_controllerRangeLeft.Value, - RangeRight = (float)_controllerRangeRight.Value, - TriggerThreshold = (float)_controllerTriggerThreshold.Value, - LeftJoycon = new LeftJoyconCommonConfig - { - ButtonMinus = lButtonMinus, - ButtonL = lButtonL, - ButtonZl = lButtonZl, - ButtonSl = lButtonSl, - ButtonSr = lButtonSr, - DpadUp = lDPadUp, - DpadDown = lDPadDown, - DpadLeft = lDPadLeft, - DpadRight = lDPadRight, - }, - LeftJoyconStick = new JoyconConfigControllerStick - { - InvertStickX = _invertLStickX.Active, - Joystick = lStick, - InvertStickY = _invertLStickY.Active, - StickButton = lStickButton, - Rotate90CW = _rotateL90CW.Active, - }, - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = rButtonA, - ButtonB = rButtonB, - ButtonX = rButtonX, - ButtonY = rButtonY, - ButtonPlus = rButtonPlus, - ButtonR = rButtonR, - ButtonZr = rButtonZr, - ButtonSl = rButtonSl, - ButtonSr = rButtonSr, - }, - RightJoyconStick = new JoyconConfigControllerStick - { - InvertStickX = _invertRStickX.Active, - Joystick = rStick, - InvertStickY = _invertRStickY.Active, - StickButton = rStickButton, - Rotate90CW = _rotateR90CW.Active, - }, - Motion = motionConfig, - Rumble = new RumbleConfigController - { - StrongRumble = (float)_controllerStrongRumble.Value, - WeakRumble = (float)_controllerWeakRumble.Value, - EnableRumble = _enableRumble.Active, - }, - }; - } - - if (!_inputDevice.ActiveId.StartsWith("disabled")) - { - GtkDialog.CreateErrorDialog("Invalid data detected in one or more fields; the configuration was not saved."); - } - - return null; - } - - private string GetProfileBasePath() - { - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - return System.IO.Path.Combine(AppDataManager.ProfilesDirPath, "keyboard"); - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - return System.IO.Path.Combine(AppDataManager.ProfilesDirPath, "controller"); - } - - return AppDataManager.ProfilesDirPath; - } - - // - // Events - // - private void InputDevice_Changed(object sender, EventArgs args) - { - SetAvailableOptions(); - SetControllerSpecificFields(); - - _selectedGamepad?.Dispose(); - _selectedGamepad = null; - - if (_inputDevice.ActiveId != null) - { - SetProfiles(); - - string id = GetCurrentGamepadId(); - - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - if (_inputConfig is StandardKeyboardInputConfig) - { - SetValues(_inputConfig); - } - - if (_mainWindow.InputManager.KeyboardDriver is GTK3KeyboardDriver) - { - // NOTE: To get input in this window, we need to bind a custom keyboard driver instead of using the InputManager one as the main window isn't focused... - _selectedGamepad = _gtk3KeyboardDriver.GetGamepad(id); - } - else - { - _selectedGamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id); - } - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - if (_inputConfig is StandardControllerInputConfig) - { - SetValues(_inputConfig); - } - - _selectedGamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id); - } - } - } - - private string GetCurrentGamepadId() - { - if (_inputDevice.ActiveId == null || _inputDevice.ActiveId == "disabled") - { - return null; - } - - return _inputDevice.ActiveId.Split("/")[1].Split(" ")[0]; - } - - private void Controller_Changed(object sender, EventArgs args) - { - SetControllerSpecificFields(); - } - - private IButtonAssigner CreateButtonAssigner(bool forStick) - { - IButtonAssigner assigner; - - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - assigner = new KeyboardKeyAssigner((IKeyboard)_selectedGamepad); - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - assigner = new GamepadButtonAssigner(_selectedGamepad, (float)_controllerTriggerThreshold.Value, forStick); - } - else - { - throw new Exception("Controller not supported"); - } - - return assigner; - } - - private void HandleButtonPressed(ToggleButton button, bool forStick) - { - if (_isWaitingForInput) - { - button.Active = false; - - return; - } - - _mousePressed = false; - - ButtonPressEvent += MouseClick; - - IButtonAssigner assigner = CreateButtonAssigner(forStick); - - _isWaitingForInput = true; - - // Open GTK3 keyboard for cancel operations - IKeyboard keyboard = (IKeyboard)_gtk3KeyboardDriver.GetGamepad("0"); - - Thread inputThread = new(() => - { - assigner.Initialize(); - - while (true) - { - Thread.Sleep(10); - assigner.ReadInput(); - - if (_mousePressed || keyboard.IsPressed(Ryujinx.Input.Key.Escape) || assigner.IsAnyButtonPressed() || assigner.ShouldCancel()) - { - break; - } - } - - string pressedButton = ButtonHelper.ToString(assigner.GetPressedButton() ?? new Button(Input.Key.Unknown)); - - Application.Invoke(delegate - { - if (_middleMousePressed) - { - button.Label = "Unbound"; - } - else if (pressedButton != "") - { - button.Label = pressedButton; - } - - _middleMousePressed = false; - - ButtonPressEvent -= MouseClick; - keyboard.Dispose(); - - button.Active = false; - _isWaitingForInput = false; - }); - }) - { - Name = "GUI.InputThread", - IsBackground = true, - }; - inputThread.Start(); - } - - private void Button_Pressed(object sender, EventArgs args) - { - HandleButtonPressed((ToggleButton)sender, false); - } - - private void ButtonForStick_Pressed(object sender, EventArgs args) - { - HandleButtonPressed((ToggleButton)sender, true); - } - - private void MouseClick(object sender, ButtonPressEventArgs args) - { - _mousePressed = true; - _middleMousePressed = args.Event.Button == 2; - } - - private void SetProfiles() - { - _profile.RemoveAll(); - - string basePath = GetProfileBasePath(); - - if (!Directory.Exists(basePath)) - { - Directory.CreateDirectory(basePath); - } - - if (_inputDevice.ActiveId == null || _inputDevice.ActiveId.Equals("disabled")) - { - _profile.Append("default", "None"); - } - else - { - _profile.Append("default", "Default"); - - foreach (string profile in Directory.GetFiles(basePath, "*.*", SearchOption.AllDirectories)) - { - _profile.Append(System.IO.Path.GetFileName(profile), System.IO.Path.GetFileNameWithoutExtension(profile)); - } - } - - _profile.SetActiveId("default"); - } - - private void ProfileLoad_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == null) - { - return; - } - - InputConfig config = null; - int pos = _profile.Active; - - if (_profile.ActiveId == "default") - { - if (_inputDevice.ActiveId.StartsWith("keyboard")) - { - config = new StandardKeyboardInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.WindowKeyboard, - Id = null, - ControllerType = ControllerType.ProController, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = Key.Up, - DpadDown = Key.Down, - DpadLeft = Key.Left, - DpadRight = Key.Right, - ButtonMinus = Key.Minus, - ButtonL = Key.E, - ButtonZl = Key.Q, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - LeftJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.W, - StickDown = Key.S, - StickLeft = Key.A, - StickRight = Key.D, - StickButton = Key.F, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = Key.Z, - ButtonB = Key.X, - ButtonX = Key.C, - ButtonY = Key.V, - ButtonPlus = Key.Plus, - ButtonR = Key.U, - ButtonZr = Key.O, - ButtonSl = Key.Unbound, - ButtonSr = Key.Unbound, - }, - - RightJoyconStick = new JoyconConfigKeyboardStick - { - StickUp = Key.I, - StickDown = Key.K, - StickLeft = Key.J, - StickRight = Key.L, - StickButton = Key.H, - }, - }; - } - else if (_inputDevice.ActiveId.StartsWith("controller")) - { - bool isNintendoStyle = _inputDevice.ActiveText.Contains("Nintendo"); - - config = new StandardControllerInputConfig - { - Version = InputConfig.CurrentVersion, - Backend = InputBackendType.GamepadSDL2, - Id = null, - ControllerType = ControllerType.JoyconPair, - DeadzoneLeft = 0.1f, - DeadzoneRight = 0.1f, - RangeLeft = 1.0f, - RangeRight = 1.0f, - TriggerThreshold = 0.5f, - LeftJoycon = new LeftJoyconCommonConfig - { - DpadUp = ConfigGamepadInputId.DpadUp, - DpadDown = ConfigGamepadInputId.DpadDown, - DpadLeft = ConfigGamepadInputId.DpadLeft, - DpadRight = ConfigGamepadInputId.DpadRight, - ButtonMinus = ConfigGamepadInputId.Minus, - ButtonL = ConfigGamepadInputId.LeftShoulder, - ButtonZl = ConfigGamepadInputId.LeftTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - LeftJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Left, - StickButton = ConfigGamepadInputId.LeftStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - RightJoycon = new RightJoyconCommonConfig - { - ButtonA = isNintendoStyle ? ConfigGamepadInputId.A : ConfigGamepadInputId.B, - ButtonB = isNintendoStyle ? ConfigGamepadInputId.B : ConfigGamepadInputId.A, - ButtonX = isNintendoStyle ? ConfigGamepadInputId.X : ConfigGamepadInputId.Y, - ButtonY = isNintendoStyle ? ConfigGamepadInputId.Y : ConfigGamepadInputId.X, - ButtonPlus = ConfigGamepadInputId.Plus, - ButtonR = ConfigGamepadInputId.RightShoulder, - ButtonZr = ConfigGamepadInputId.RightTrigger, - ButtonSl = ConfigGamepadInputId.Unbound, - ButtonSr = ConfigGamepadInputId.Unbound, - }, - - RightJoyconStick = new JoyconConfigControllerStick - { - Joystick = ConfigStickInputId.Right, - StickButton = ConfigGamepadInputId.RightStick, - InvertStickX = false, - InvertStickY = false, - Rotate90CW = false, - }, - - Motion = new StandardMotionConfigController - { - MotionBackend = MotionInputBackendType.GamepadDriver, - EnableMotion = true, - Sensitivity = 100, - GyroDeadzone = 1, - }, - Rumble = new RumbleConfigController - { - StrongRumble = 1f, - WeakRumble = 1f, - EnableRumble = false, - }, - }; - } - } - else - { - string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId); - - if (!File.Exists(path)) - { - if (pos >= 0) - { - _profile.Remove(pos); - } - - return; - } - - try - { - config = JsonHelper.DeserializeFromFile(path, _serializerContext.InputConfig); - } - catch (JsonException) { } - } - - SetValues(config); - } - - private void ProfileAdd_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - if (_inputDevice.ActiveId == "disabled") - { - return; - } - - InputConfig inputConfig = GetValues(); - ProfileDialog profileDialog = new(); - - if (inputConfig == null) - { - return; - } - - if (profileDialog.Run() == (int)ResponseType.Ok) - { - string path = System.IO.Path.Combine(GetProfileBasePath(), profileDialog.FileName); - string jsonString = JsonHelper.Serialize(inputConfig, _serializerContext.InputConfig); - - File.WriteAllText(path, jsonString); - } - - profileDialog.Dispose(); - - SetProfiles(); - } - - private void ProfileRemove_Activated(object sender, EventArgs args) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - if (_inputDevice.ActiveId == "disabled" || _profile.ActiveId == "default" || _profile.ActiveId == null) - { - return; - } - - MessageDialog confirmDialog = GtkDialog.CreateConfirmationDialog("Deleting Profile", "This action is irreversible, are you sure you want to continue?"); - - if (confirmDialog.Run() == (int)ResponseType.Yes) - { - string path = System.IO.Path.Combine(GetProfileBasePath(), _profile.ActiveId); - - if (File.Exists(path)) - { - File.Delete(path); - } - - SetProfiles(); - } - } - - private void SaveToggle_Activated(object sender, EventArgs args) - { - InputConfig inputConfig = GetValues(); - - var newConfig = new List(); - newConfig.AddRange(ConfigurationState.Instance.Hid.InputConfig.Value); - - if (_inputConfig == null && inputConfig != null) - { - newConfig.Add(inputConfig); - } - else - { - if (_inputDevice.ActiveId == "disabled") - { - newConfig.Remove(_inputConfig); - } - else if (inputConfig != null) - { - int index = newConfig.IndexOf(_inputConfig); - - newConfig[index] = inputConfig; - } - } - - _mainWindow.RendererWidget?.NpadManager.ReloadConfiguration(newConfig, ConfigurationState.Instance.Hid.EnableKeyboard, ConfigurationState.Instance.Hid.EnableMouse); - - // Atomically replace and signal input change. - // NOTE: Do not modify InputConfig.Value directly as other code depends on the on-change event. - ConfigurationState.Instance.Hid.InputConfig.Value = newConfig; - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - - Dispose(); - } - - private void CloseToggle_Activated(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade deleted file mode 100644 index e433f5cc4..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/ControllerWindow.glade +++ /dev/null @@ -1,2241 +0,0 @@ - - - - - - 4 - 1 - 4 - - - 0.1 - 10 - 1.0 - 0.1 - 1.0 - - - 0.1 - 10 - 1.0 - 0.1 - 1.0 - - - 1 - 0.050000000000000003 - 0.01 - 0.10000000000000001 - - - 1 - 0.050000000000000003 - 0.01 - 0.10000000000000001 - - - 2 - 1.000000000000000003 - 0.01 - 0.10000000000000001 - - - 2 - 1.000000000000000003 - 0.01 - 0.10000000000000001 - - - 1 - 0.5 - 0.01 - 0.10000000000000001 - - - 100 - 0.01 - 0.01 - 0.10000000000000001 - 0.10000000000000001 - - - 1000 - 100 - 1 - 4 - - - 4 - 1 - 4 - - - False - Ryujinx - Controller Settings - True - center - 1200 - 720 - - - - - - True - False - vertical - - - True - True - in - - - True - False - - - True - False - vertical - - - True - False - 10 - 10 - 10 - - - True - False - - - True - False - 5 - Input Device - - - False - True - 0 - - - - - True - False - 0 - disabled - - Disabled - - - - - True - True - 1 - - - - - False - True - 0 - - - - - True - False - 20 - - - True - False - The controller's type - center - 5 - Controller Type: - - - False - True - 0 - - - - - True - False - The controller's type - 0 - - - - False - True - 1 - - - - - False - True - 1 - - - - - True - False - 20 - - - True - False - 5 - Profile: - - - False - True - 0 - - - - - True - False - 5 - 0 - default - - - False - True - 1 - - - - - Load - 60 - True - True - True - 5 - - - - False - True - 2 - - - - - Add - 60 - True - True - True - 5 - - - - False - True - 3 - - - - - Remove - 60 - True - True - True - - - - False - True - 4 - - - - - False - True - 2 - - - - - False - True - 0 - - - - - True - False - - - True - False - vertical - - - True - False - 10 - 5 - - - 156 - True - False - 10 - vertical - - - True - False - 5 - 5 - Buttons - - - - - - False - True - 0 - - - - - True - False - 3 - 10 - - - 80 - True - False - A - - - 0 - 0 - - - - - 80 - True - False - B - - - 0 - 1 - - - - - 80 - True - False - X - - - 0 - 2 - - - - - 80 - True - False - Y - - - 0 - 3 - - - - - - 70 - True - True - True - - - 1 - 0 - - - - - - 70 - True - True - True - - - 1 - 1 - - - - - - 70 - True - True - True - - - 1 - 2 - - - - - - 70 - True - True - True - - - 1 - 3 - - - - - 80 - True - False - + - - - 0 - 4 - - - - - 80 - True - False - - - - - 0 - 5 - - - - - - 70 - True - True - True - - - 1 - 5 - - - - - - 70 - True - True - True - - - 1 - 4 - - - - - False - True - 1 - - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - 160 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Left Stick - - - - - - False - True - 0 - - - - - True - False - 5 - 3 - 10 - - - 80 - True - False - LStick Button - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - False - True - 1 - - - - - True - False - 3 - 10 - - - - 65 - True - True - True - - - 1 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 2 - - - - - - 65 - True - True - True - - - 1 - 3 - - - - - 80 - True - False - LStick Down - 0 - - - 0 - 1 - - - - - 80 - True - False - LStick Up - 0 - - - 0 - 0 - - - - - 80 - True - False - LStick Right - 0 - - - 0 - 3 - - - - - 80 - True - False - LStick Left - 0 - - - 0 - 2 - - - - - False - True - 2 - - - - - True - False - 3 - 10 - - - 80 - True - False - LStick - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - Invert Stick X - True - True - False - True - - - 2 - 0 - - - - - Invert Stick Y - True - True - False - True - - - 2 - 1 - - - - - Rotate 90° Clockwise - True - True - False - True - - - 2 - 2 - - - - - False - True - 3 - - - - - True - False - 10 - vertical - - - True - False - start - Deadzone Left - - - False - True - 0 - - - - - True - True - _controllerDeadzoneLeft - 2 - 2 - - - True - True - 1 - - - - - False - True - 4 - - - - - True - False - 10 - vertical - - - True - False - start - Range Left - - - False - True - 0 - - - - - True - True - _controllerRangeLeft - 2 - 2 - - - True - True - 1 - - - - - False - True - 5 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - - 150 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Triggers - - - - - - False - True - 0 - - - - - True - False - 3 - 10 - - - 80 - True - False - L - - - 0 - 0 - - - - - 80 - True - False - R - - - 0 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - 80 - True - False - ZL - - - 0 - 2 - - - - - 80 - True - False - ZR - - - 0 - 3 - - - - - - 65 - True - True - True - - - 1 - 2 - - - - - - 65 - True - True - True - - - 1 - 3 - - - - - False - True - 1 - - - - - _sideTriggerBox - True - False - 5 - 3 - 10 - - - 80 - True - False - Left SL - - - 0 - 0 - - - - - 80 - True - False - Left SR - - - 0 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - False - True - 2 - - - - - _sideTriggerBox - True - False - 5 - 3 - 10 - - - 80 - True - False - Right SL - - - 0 - 0 - - - - - 80 - True - False - Right SR - - - 0 - 1 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - False - True - 3 - - - - - True - False - 10 - vertical - - - True - False - start - 10 - Trigger Threshold - - - False - True - 0 - - - - - True - True - _controllerTriggerThreshold - 2 - 2 - - - True - True - 1 - - - - - False - True - 4 - - - - - False - True - 4 - - - - - False - True - 0 - - - - - True - False - 10 - - - 156 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Directional Pad - - - - - - False - True - 0 - - - - - True - False - 3 - 10 - - - 80 - True - False - Dpad Up - 0 - - - 0 - 0 - - - - - 80 - True - False - Dpad Down - 0 - - - 0 - 1 - - - - - 80 - True - False - Dpad Left - 0 - - - 0 - 2 - - - - - 80 - True - False - Dpad Right - 0 - - - 0 - 3 - - - - - - 70 - True - True - True - - - 1 - 0 - - - - - - 70 - True - True - True - - - 1 - 1 - - - - - - 70 - True - True - True - - - 1 - 2 - - - - - - 70 - True - True - True - - - 1 - 3 - - - - - False - True - 1 - - - - - True - False - 10 - vertical - - - True - False - 10 - 5 - Rumble - - - - - - False - True - 0 - - - - - Enable - True - True - False - True - - - False - True - 1 - - - - - True - False - 10 - vertical - - - True - False - start - Strong rumble multiplier - - - False - True - 0 - - - - - True - True - _controllerStrongRumble - 1 - 1 - - - True - True - 1 - - - - - False - True - 2 - - - - - True - False - 10 - vertical - - - True - False - start - Weak rumble multiplier - - - False - True - 0 - - - - - True - True - _controllerWeakRumble - 1 - 1 - - - True - True - 1 - - - - - False - True - 3 - - - - - False - True - 2 - - - - - False - True - 0 - - - - - True - False - - - False - True - 1 - - - - - 160 - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - Right Stick - - - - - - False - True - 0 - - - - - True - False - 5 - 3 - 10 - - - 80 - True - False - RStick Button - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - False - True - 1 - - - - - True - False - 3 - 10 - - - 80 - True - False - RStick Up - 0 - - - 0 - 0 - - - - - 80 - True - False - RStick Down - 0 - - - 0 - 1 - - - - - 80 - True - False - RStick Left - 0 - - - 0 - 2 - - - - - 80 - True - False - RStick Right - 0 - - - 0 - 3 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - - 65 - True - True - True - - - 1 - 1 - - - - - - 65 - True - True - True - - - 1 - 2 - - - - - - 65 - True - True - True - - - 1 - 3 - - - - - False - True - 2 - - - - - True - False - 3 - 10 - - - 80 - True - False - RStick - 0 - - - 0 - 0 - - - - - - 65 - True - True - True - - - 1 - 0 - - - - - Invert Stick X - True - True - False - True - - - 2 - 0 - - - - - Invert Stick Y - True - True - False - True - - - 2 - 1 - - - - - Rotate 90° Clockwise - True - True - False - True - - - 2 - 2 - - - - - False - True - 3 - - - - - True - False - 10 - vertical - - - True - False - start - Deadzone Right - - - False - True - 0 - - - - - True - True - _controllerDeadzoneRight - 2 - 2 - - - True - True - 1 - - - - - False - True - 4 - - - - - True - False - 10 - vertical - - - True - False - start - Range Right - - - False - True - 0 - - - - - True - True - _controllerRangeRight - 2 - 2 - - - True - True - 1 - - - - - False - True - 5 - - - - - False - True - 2 - - - - - True - False - - - False - True - 3 - - - - - True - False - 10 - 10 - vertical - 5 - - - True - False - 5 - 5 - Motion - - - - - - False - True - 0 - - - - - Enable Motion Controls - True - True - False - True - - - False - True - 1 - - - - - Use CemuHook compatible motion - True - True - False - True - - - False - True - 2 - - - - - True - False - 10 - - - True - False - 17 - Controller Slot - - - False - True - 5 - 0 - - - - - True - True - 10 - _slotNumber - 1 - True - True - - - False - True - 1 - - - - - False - True - 5 - 3 - - - - - True - False - 10 - - - True - False - 5 - Gyro Sensitivity % - - - False - True - 5 - 0 - - - - - True - True - 0 - _sensitivity - 1 - True - True - - - False - True - 1 - - - - - False - True - 5 - 4 - - - - - True - False - vertical - - - Mirror Input - True - True - False - True - - - False - True - 0 - - - - - True - False - 10 - - - True - False - Right JoyCon Slot - - - False - True - 5 - 0 - - - - - True - True - 0 - _altSlotNumber - 1 - True - True - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - False - True - 5 - - - - - True - False - 30 - - - True - False - Server Host - - - False - True - 5 - 0 - - - - - True - True - - - False - True - 1 - - - - - False - True - 5 - 6 - - - - - True - False - 30 - - - True - False - Server Port - - - False - True - 5 - 0 - - - - - True - True - - - False - True - 1 - - - - - False - True - 5 - 7 - - - - - True - False - start - Gyro Deadzone - - - False - True - 8 - - - - - True - True - _gyroDeadzone - 2 - 2 - - - True - True - 9 - - - - - False - True - 4 - - - - - False - True - 1 - - - - - True - True - 0 - - - - - True - False - 10 - 20 - 5 - 5 - - - True - True - 1 - - - - - True - True - 1 - - - - - - - - - True - True - 0 - - - - - True - False - 5 - 3 - 3 - end - - - Save - True - True - True - - - - False - True - 0 - - - - - Close - True - True - True - 4 - - - - False - True - 5 - 1 - - - - - False - False - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs deleted file mode 100644 index fb3189e1c..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.cs +++ /dev/null @@ -1,288 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Tools.Fs; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Windows -{ - public class DlcWindow : Window - { - private readonly VirtualFileSystem _virtualFileSystem; - private readonly string _applicationIdBase; - private readonly string _dlcJsonPath; - private readonly List _dlcContainerList; - - private static readonly DownloadableContentJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Label _baseTitleInfoLabel; - [GUI] TreeView _dlcTreeView; - [GUI] TreeSelection _dlcTreeSelection; -#pragma warning restore CS0649, IDE0044 - - public DlcWindow(VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.DlcWindow.glade"), virtualFileSystem, applicationIdBase, applicationData) { } - - private DlcWindow(Builder builder, VirtualFileSystem virtualFileSystem, string applicationIdBase, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_dlcWindow")) - { - builder.Autoconnect(this); - - _applicationIdBase = applicationIdBase; - _virtualFileSystem = virtualFileSystem; - _dlcJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, _applicationIdBase, "dlc.json"); - _baseTitleInfoLabel.Text = $"DLC Available for {applicationData.Name} [{applicationIdBase.ToUpper()}]"; - - try - { - _dlcContainerList = JsonHelper.DeserializeFromFile(_dlcJsonPath, _serializerContext.ListDownloadableContentContainer); - } - catch - { - _dlcContainerList = new List(); - } - - _dlcTreeView.Model = new TreeStore(typeof(bool), typeof(string), typeof(string)); - - CellRendererToggle enableToggle = new(); - enableToggle.Toggled += (sender, args) => - { - _dlcTreeView.Model.GetIter(out TreeIter treeIter, new TreePath(args.Path)); - bool newValue = !(bool)_dlcTreeView.Model.GetValue(treeIter, 0); - _dlcTreeView.Model.SetValue(treeIter, 0, newValue); - - if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, treeIter)) - { - do - { - _dlcTreeView.Model.SetValue(childIter, 0, newValue); - } - while (_dlcTreeView.Model.IterNext(ref childIter)); - } - }; - - _dlcTreeView.AppendColumn("Enabled", enableToggle, "active", 0); - _dlcTreeView.AppendColumn("ApplicationId", new CellRendererText(), "text", 1); - _dlcTreeView.AppendColumn("Path", new CellRendererText(), "text", 2); - - foreach (DownloadableContentContainer dlcContainer in _dlcContainerList) - { - if (File.Exists(dlcContainer.ContainerPath)) - { - // The parent tree item has its own "enabled" check box, but it's the actual - // nca entries that store the enabled / disabled state. A bit of a UI inconsistency. - // Maybe a tri-state check box would be better, but for now we check the parent - // "enabled" box if all child NCAs are enabled. Usually fine since each nsp has only one nca. - bool areAllContentPacksEnabled = dlcContainer.DownloadableContentNcaList.TrueForAll((nca) => nca.Enabled); - TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(areAllContentPacksEnabled, "", dlcContainer.ContainerPath); - - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(dlcContainer.ContainerPath, _virtualFileSystem, false); - - if (partitionFileSystem == null) - { - continue; - } - - foreach (DownloadableContentNca dlcNca in dlcContainer.DownloadableContentNcaList) - { - using var ncaFile = new UniqueRef(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, dlcNca.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), dlcContainer.ContainerPath); - - if (nca != null) - { - ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter, dlcNca.Enabled, nca.Header.TitleId.ToString("X16"), dlcNca.FullPath); - } - } - } - else - { - // DLC file moved or renamed. Allow the user to remove it without crashing the whole dialog. - TreeIter parentIter = ((TreeStore)_dlcTreeView.Model).AppendValues(false, "", $"(MISSING) {dlcContainer.ContainerPath}"); - } - } - - // NOTE: Try to load downloadable contents from PFS last to preserve enabled state. - AddDlc(applicationData.Path, true); - } - - private Nca TryCreateNca(IStorage ncaStorage, string containerPath) - { - try - { - return new Nca(_virtualFileSystem.KeySet, ncaStorage); - } - catch (Exception exception) - { - GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {containerPath}"); - } - - return null; - } - - private void AddDlc(string path, bool ignoreNotFound = false) - { - if (!File.Exists(path) || _dlcContainerList.Any(x => x.ContainerPath == path)) - { - return; - } - - using IFileSystem partitionFileSystem = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); - - bool containsDlc = false; - - TreeIter? parentIter = null; - - foreach (DirectoryEntryEx fileEntry in partitionFileSystem.EnumerateEntries("/", "*.nca")) - { - using var ncaFile = new UniqueRef(); - - partitionFileSystem.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure(); - - Nca nca = TryCreateNca(ncaFile.Get.AsStorage(), path); - - if (nca == null) - { - continue; - } - - if (nca.Header.ContentType == NcaContentType.PublicData) - { - if (nca.GetProgramIdBase() != ulong.Parse(_applicationIdBase, NumberStyles.HexNumber)) - { - continue; - } - - parentIter ??= ((TreeStore)_dlcTreeView.Model).AppendValues(true, "", path); - - ((TreeStore)_dlcTreeView.Model).AppendValues(parentIter.Value, true, nca.Header.TitleId.ToString("X16"), fileEntry.FullPath); - containsDlc = true; - } - } - - if (!containsDlc && !ignoreNotFound) - { - GtkDialog.CreateErrorDialog("The specified file does not contain DLC for the selected title!"); - } - } - - private void AddButton_Clicked(object sender, EventArgs args) - { - FileChooserNative fileChooser = new("Select DLC files", this, FileChooserAction.Open, "Add", "Cancel") - { - SelectMultiple = true, - }; - - FileFilter filter = new() - { - Name = "Switch Game DLCs", - }; - filter.AddPattern("*.nsp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - foreach (string containerPath in fileChooser.Filenames) - { - AddDlc(containerPath); - } - } - - fileChooser.Dispose(); - } - - private void RemoveButton_Clicked(object sender, EventArgs args) - { - if (_dlcTreeSelection.GetSelected(out ITreeModel treeModel, out TreeIter treeIter)) - { - if (_dlcTreeView.Model.IterParent(out TreeIter parentIter, treeIter) && _dlcTreeView.Model.IterNChildren(parentIter) <= 1) - { - ((TreeStore)treeModel).Remove(ref parentIter); - } - else - { - ((TreeStore)treeModel).Remove(ref treeIter); - } - } - } - - private void RemoveAllButton_Clicked(object sender, EventArgs args) - { - List toRemove = new(); - - if (_dlcTreeView.Model.GetIterFirst(out TreeIter iter)) - { - do - { - toRemove.Add(iter); - } - while (_dlcTreeView.Model.IterNext(ref iter)); - } - - foreach (TreeIter i in toRemove) - { - TreeIter j = i; - ((TreeStore)_dlcTreeView.Model).Remove(ref j); - } - } - - private void SaveButton_Clicked(object sender, EventArgs args) - { - _dlcContainerList.Clear(); - - if (_dlcTreeView.Model.GetIterFirst(out TreeIter parentIter)) - { - do - { - if (_dlcTreeView.Model.IterChildren(out TreeIter childIter, parentIter)) - { - DownloadableContentContainer dlcContainer = new() - { - ContainerPath = (string)_dlcTreeView.Model.GetValue(parentIter, 2), - DownloadableContentNcaList = new List(), - }; - - do - { - dlcContainer.DownloadableContentNcaList.Add(new DownloadableContentNca - { - Enabled = (bool)_dlcTreeView.Model.GetValue(childIter, 0), - TitleId = Convert.ToUInt64(_dlcTreeView.Model.GetValue(childIter, 1).ToString(), 16), - FullPath = (string)_dlcTreeView.Model.GetValue(childIter, 2), - }); - } - while (_dlcTreeView.Model.IterNext(ref childIter)); - - _dlcContainerList.Add(dlcContainer); - } - } - while (_dlcTreeView.Model.IterNext(ref parentIter)); - } - - JsonHelper.SerializeToFile(_dlcJsonPath, _dlcContainerList, _serializerContext.ListDownloadableContentContainer); - - Dispose(); - } - - private void CancelButton_Clicked(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade deleted file mode 100644 index bdb0e647a..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/DlcWindow.glade +++ /dev/null @@ -1,202 +0,0 @@ - - - - - - False - Ryujinx - DLC Manager - True - center - 550 - 350 - - - True - False - vertical - - - True - False - vertical - - - True - False - 10 - 10 - 10 - 10 - Available DLC - - - False - True - 0 - - - - - True - True - 10 - 10 - in - - - True - False - - - True - True - False - - - - - - - - - - True - True - 1 - - - - - True - True - 0 - - - - - True - False - - - True - False - 10 - 10 - start - - - Add - True - True - True - Adds a DLC to this list - 10 - - - - True - True - 0 - - - - - Remove - True - True - True - Removes the selected DLC - 10 - - - - True - True - 1 - - - - - Remove All - True - True - True - Removes all DLCs - 10 - - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - 10 - 10 - end - - - Save - True - True - True - 10 - 2 - 2 - - - - True - True - 0 - - - - - Cancel - True - True - True - 10 - 2 - 2 - - - - True - True - 1 - - - - - True - True - 1 - - - - - False - True - 1 - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs deleted file mode 100644 index dc467c0f2..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs +++ /dev/null @@ -1,847 +0,0 @@ -using Gtk; -using LibHac.Tools.FsSystem; -using Ryujinx.Audio.Backends.OpenAL; -using Ryujinx.Audio.Backends.SDL2; -using Ryujinx.Audio.Backends.SoundIo; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Configuration.Hid; -using Ryujinx.Common.Configuration.Multiplayer; -using Ryujinx.Common.GraphicsDriver; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.Services.Time.TimeZone; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Common.Configuration.System; -using Ryujinx.UI.Helper; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Net.NetworkInformation; -using System.Reflection; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using GUI = Gtk.Builder.ObjectAttribute; - -namespace Ryujinx.UI.Windows -{ - public class SettingsWindow : Window - { - private readonly MainWindow _parent; - private readonly ListStore _gameDirsBoxStore; - private readonly ListStore _audioBackendStore; - private readonly TimeZoneContentManager _timeZoneContentManager; - private readonly HashSet _validTzRegions; - - private long _systemTimeOffset; - private float _previousVolumeLevel; - private bool _directoryChanged = false; - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] CheckButton _traceLogToggle; - [GUI] CheckButton _errorLogToggle; - [GUI] CheckButton _warningLogToggle; - [GUI] CheckButton _infoLogToggle; - [GUI] CheckButton _stubLogToggle; - [GUI] CheckButton _debugLogToggle; - [GUI] CheckButton _fileLogToggle; - [GUI] CheckButton _guestLogToggle; - [GUI] CheckButton _fsAccessLogToggle; - [GUI] Adjustment _fsLogSpinAdjustment; - [GUI] ComboBoxText _graphicsDebugLevel; - [GUI] CheckButton _dockedModeToggle; - [GUI] CheckButton _discordToggle; - [GUI] CheckButton _checkUpdatesToggle; - [GUI] CheckButton _showConfirmExitToggle; - [GUI] RadioButton _hideCursorNever; - [GUI] RadioButton _hideCursorOnIdle; - [GUI] RadioButton _hideCursorAlways; - [GUI] CheckButton _vSyncToggle; - [GUI] CheckButton _shaderCacheToggle; - [GUI] CheckButton _textureRecompressionToggle; - [GUI] CheckButton _macroHLEToggle; - [GUI] CheckButton _ptcToggle; - [GUI] CheckButton _internetToggle; - [GUI] CheckButton _fsicToggle; - [GUI] RadioButton _mmSoftware; - [GUI] RadioButton _mmHost; - [GUI] RadioButton _mmHostUnsafe; - [GUI] CheckButton _expandRamToggle; - [GUI] CheckButton _ignoreToggle; - [GUI] CheckButton _directKeyboardAccess; - [GUI] CheckButton _directMouseAccess; - [GUI] ComboBoxText _systemLanguageSelect; - [GUI] ComboBoxText _systemRegionSelect; - [GUI] Entry _systemTimeZoneEntry; - [GUI] EntryCompletion _systemTimeZoneCompletion; - [GUI] Box _audioBackendBox; - [GUI] ComboBox _audioBackendSelect; - [GUI] Label _audioVolumeLabel; - [GUI] Scale _audioVolumeSlider; - [GUI] SpinButton _systemTimeYearSpin; - [GUI] SpinButton _systemTimeMonthSpin; - [GUI] SpinButton _systemTimeDaySpin; - [GUI] SpinButton _systemTimeHourSpin; - [GUI] SpinButton _systemTimeMinuteSpin; - [GUI] Adjustment _systemTimeYearSpinAdjustment; - [GUI] Adjustment _systemTimeMonthSpinAdjustment; - [GUI] Adjustment _systemTimeDaySpinAdjustment; - [GUI] Adjustment _systemTimeHourSpinAdjustment; - [GUI] Adjustment _systemTimeMinuteSpinAdjustment; - [GUI] ComboBoxText _multiLanSelect; - [GUI] ComboBoxText _multiModeSelect; - [GUI] CheckButton _custThemeToggle; - [GUI] Entry _custThemePath; - [GUI] ToggleButton _browseThemePath; - [GUI] Label _custThemePathLabel; - [GUI] TreeView _gameDirsBox; - [GUI] Entry _addGameDirBox; - [GUI] ComboBoxText _galThreading; - [GUI] Entry _graphicsShadersDumpPath; - [GUI] ComboBoxText _anisotropy; - [GUI] ComboBoxText _aspectRatio; - [GUI] ComboBoxText _antiAliasing; - [GUI] ComboBoxText _scalingFilter; - [GUI] ComboBoxText _graphicsBackend; - [GUI] ComboBoxText _preferredGpu; - [GUI] ComboBoxText _resScaleCombo; - [GUI] Entry _resScaleText; - [GUI] Adjustment _scalingFilterLevel; - [GUI] Scale _scalingFilterSlider; - [GUI] ToggleButton _configureController1; - [GUI] ToggleButton _configureController2; - [GUI] ToggleButton _configureController3; - [GUI] ToggleButton _configureController4; - [GUI] ToggleButton _configureController5; - [GUI] ToggleButton _configureController6; - [GUI] ToggleButton _configureController7; - [GUI] ToggleButton _configureController8; - [GUI] ToggleButton _configureControllerH; - -#pragma warning restore CS0649, IDE0044 - - public SettingsWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this(parent, new Builder("Ryujinx.Gtk3.UI.Windows.SettingsWindow.glade"), virtualFileSystem, contentManager) { } - - private SettingsWindow(MainWindow parent, Builder builder, VirtualFileSystem virtualFileSystem, ContentManager contentManager) : base(builder.GetRawOwnedObject("_settingsWin")) - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - _parent = parent; - - builder.Autoconnect(this); - - _timeZoneContentManager = new TimeZoneContentManager(); - _timeZoneContentManager.InitializeInstance(virtualFileSystem, contentManager, IntegrityCheckLevel.None); - - _validTzRegions = new HashSet(_timeZoneContentManager.LocationNameCache.Length, StringComparer.Ordinal); // Zone regions are identifiers. Must match exactly. - - // Bind Events. - _configureController1.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player1); - _configureController2.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player2); - _configureController3.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player3); - _configureController4.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player4); - _configureController5.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player5); - _configureController6.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player6); - _configureController7.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player7); - _configureController8.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Player8); - _configureControllerH.Pressed += (sender, args) => ConfigureController_Pressed(sender, PlayerIndex.Handheld); - _systemTimeZoneEntry.FocusOutEvent += TimeZoneEntry_FocusOut; - - _resScaleCombo.Changed += (sender, args) => _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; - _scalingFilter.Changed += (sender, args) => _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; - _galThreading.Changed += (sender, args) => - { - if (_galThreading.ActiveId != ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()) - { - GtkDialog.CreateInfoDialog("Warning - Backend Threading", "Ryujinx must be restarted after changing this option for it to apply fully. Depending on your platform, you may need to manually disable your driver's own multithreading when using Ryujinx's."); - } - }; - - // Setup Currents. - if (ConfigurationState.Instance.Logger.EnableTrace) - { - _traceLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableFileLog) - { - _fileLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableError) - { - _errorLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableWarn) - { - _warningLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableInfo) - { - _infoLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableStub) - { - _stubLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableDebug) - { - _debugLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableGuest) - { - _guestLogToggle.Click(); - } - - if (ConfigurationState.Instance.Logger.EnableFsAccessLog) - { - _fsAccessLogToggle.Click(); - } - - foreach (GraphicsDebugLevel level in Enum.GetValues()) - { - _graphicsDebugLevel.Append(level.ToString(), level.ToString()); - } - - _graphicsDebugLevel.SetActiveId(ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value.ToString()); - - if (ConfigurationState.Instance.System.EnableDockedMode) - { - _dockedModeToggle.Click(); - } - - if (ConfigurationState.Instance.EnableDiscordIntegration) - { - _discordToggle.Click(); - } - - if (ConfigurationState.Instance.CheckUpdatesOnStart) - { - _checkUpdatesToggle.Click(); - } - - if (ConfigurationState.Instance.ShowConfirmExit) - { - _showConfirmExitToggle.Click(); - } - - switch (ConfigurationState.Instance.HideCursor.Value) - { - case HideCursorMode.Never: - _hideCursorNever.Click(); - break; - case HideCursorMode.OnIdle: - _hideCursorOnIdle.Click(); - break; - case HideCursorMode.Always: - _hideCursorAlways.Click(); - break; - } - - if (ConfigurationState.Instance.Graphics.EnableVsync) - { - _vSyncToggle.Click(); - } - - if (ConfigurationState.Instance.Graphics.EnableShaderCache) - { - _shaderCacheToggle.Click(); - } - - if (ConfigurationState.Instance.Graphics.EnableTextureRecompression) - { - _textureRecompressionToggle.Click(); - } - - if (ConfigurationState.Instance.Graphics.EnableMacroHLE) - { - _macroHLEToggle.Click(); - } - - if (ConfigurationState.Instance.System.EnablePtc) - { - _ptcToggle.Click(); - } - - if (ConfigurationState.Instance.System.EnableInternetAccess) - { - _internetToggle.Click(); - } - - if (ConfigurationState.Instance.System.EnableFsIntegrityChecks) - { - _fsicToggle.Click(); - } - - switch (ConfigurationState.Instance.System.MemoryManagerMode.Value) - { - case MemoryManagerMode.SoftwarePageTable: - _mmSoftware.Click(); - break; - case MemoryManagerMode.HostMapped: - _mmHost.Click(); - break; - case MemoryManagerMode.HostMappedUnsafe: - _mmHostUnsafe.Click(); - break; - } - - if (ConfigurationState.Instance.System.ExpandRam) - { - _expandRamToggle.Click(); - } - - if (ConfigurationState.Instance.System.IgnoreMissingServices) - { - _ignoreToggle.Click(); - } - - if (ConfigurationState.Instance.Hid.EnableKeyboard) - { - _directKeyboardAccess.Click(); - } - - if (ConfigurationState.Instance.Hid.EnableMouse) - { - _directMouseAccess.Click(); - } - - if (ConfigurationState.Instance.UI.EnableCustomTheme) - { - _custThemeToggle.Click(); - } - - // Custom EntryCompletion Columns. If added to glade, need to override more signals - ListStore tzList = new(typeof(string), typeof(string), typeof(string)); - _systemTimeZoneCompletion.Model = tzList; - - CellRendererText offsetCol = new(); - CellRendererText abbrevCol = new(); - - _systemTimeZoneCompletion.PackStart(offsetCol, false); - _systemTimeZoneCompletion.AddAttribute(offsetCol, "text", 0); - _systemTimeZoneCompletion.TextColumn = 1; // Regions Column - _systemTimeZoneCompletion.PackStart(abbrevCol, false); - _systemTimeZoneCompletion.AddAttribute(abbrevCol, "text", 2); - - int maxLocationLength = 0; - - foreach (var (offset, location, abbr) in _timeZoneContentManager.ParseTzOffsets()) - { - var hours = Math.DivRem(offset, 3600, out int seconds); - var minutes = Math.Abs(seconds) / 60; - - var abbr2 = (abbr.StartsWith('+') || abbr.StartsWith('-')) ? string.Empty : abbr; - - tzList.AppendValues($"UTC{hours:+0#;-0#;+00}:{minutes:D2} ", location, abbr2); - _validTzRegions.Add(location); - - maxLocationLength = Math.Max(maxLocationLength, location.Length); - } - - _systemTimeZoneEntry.WidthChars = Math.Max(20, maxLocationLength + 1); // Ensure minimum Entry width - _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone); - - _systemTimeZoneCompletion.MatchFunc = TimeZoneMatchFunc; - - _systemLanguageSelect.SetActiveId(ConfigurationState.Instance.System.Language.Value.ToString()); - _systemRegionSelect.SetActiveId(ConfigurationState.Instance.System.Region.Value.ToString()); - _galThreading.SetActiveId(ConfigurationState.Instance.Graphics.BackendThreading.Value.ToString()); - _resScaleCombo.SetActiveId(ConfigurationState.Instance.Graphics.ResScale.Value.ToString()); - _anisotropy.SetActiveId(ConfigurationState.Instance.Graphics.MaxAnisotropy.Value.ToString()); - _aspectRatio.SetActiveId(((int)ConfigurationState.Instance.Graphics.AspectRatio.Value).ToString()); - _graphicsBackend.SetActiveId(((int)ConfigurationState.Instance.Graphics.GraphicsBackend.Value).ToString()); - _antiAliasing.SetActiveId(((int)ConfigurationState.Instance.Graphics.AntiAliasing.Value).ToString()); - _scalingFilter.SetActiveId(((int)ConfigurationState.Instance.Graphics.ScalingFilter.Value).ToString()); - - UpdatePreferredGpuComboBox(); - - _graphicsBackend.Changed += (sender, e) => UpdatePreferredGpuComboBox(); - PopulateNetworkInterfaces(); - _multiLanSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value); - _multiModeSelect.SetActiveId(ConfigurationState.Instance.Multiplayer.Mode.Value.ToString()); - - _custThemePath.Buffer.Text = ConfigurationState.Instance.UI.CustomThemePath; - _resScaleText.Buffer.Text = ConfigurationState.Instance.Graphics.ResScaleCustom.Value.ToString(); - _scalingFilterLevel.Value = ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value; - _resScaleText.Visible = _resScaleCombo.ActiveId == "-1"; - _scalingFilterSlider.Visible = _scalingFilter.ActiveId == "2"; - _graphicsShadersDumpPath.Buffer.Text = ConfigurationState.Instance.Graphics.ShadersDumpPath; - _fsLogSpinAdjustment.Value = ConfigurationState.Instance.System.FsGlobalAccessLogMode; - _systemTimeOffset = ConfigurationState.Instance.System.SystemTimeOffset; - - _gameDirsBox.AppendColumn("", new CellRendererText(), "text", 0); - _gameDirsBoxStore = new ListStore(typeof(string)); - _gameDirsBox.Model = _gameDirsBoxStore; - - foreach (string gameDir in ConfigurationState.Instance.UI.GameDirs.Value) - { - _gameDirsBoxStore.AppendValues(gameDir); - } - - if (_custThemeToggle.Active == false) - { - _custThemePath.Sensitive = false; - _custThemePathLabel.Sensitive = false; - _browseThemePath.Sensitive = false; - } - - // Setup system time spinners - UpdateSystemTimeSpinners(); - - _audioBackendStore = new ListStore(typeof(string), typeof(AudioBackend)); - - TreeIter openAlIter = _audioBackendStore.AppendValues("OpenAL", AudioBackend.OpenAl); - TreeIter soundIoIter = _audioBackendStore.AppendValues("SoundIO", AudioBackend.SoundIo); - TreeIter sdl2Iter = _audioBackendStore.AppendValues("SDL2", AudioBackend.SDL2); - TreeIter dummyIter = _audioBackendStore.AppendValues("Dummy", AudioBackend.Dummy); - - _audioBackendSelect = ComboBox.NewWithModelAndEntry(_audioBackendStore); - _audioBackendSelect.EntryTextColumn = 0; - _audioBackendSelect.Entry.IsEditable = false; - - switch (ConfigurationState.Instance.System.AudioBackend.Value) - { - case AudioBackend.OpenAl: - _audioBackendSelect.SetActiveIter(openAlIter); - break; - case AudioBackend.SoundIo: - _audioBackendSelect.SetActiveIter(soundIoIter); - break; - case AudioBackend.SDL2: - _audioBackendSelect.SetActiveIter(sdl2Iter); - break; - case AudioBackend.Dummy: - _audioBackendSelect.SetActiveIter(dummyIter); - break; - default: - throw new InvalidOperationException($"{nameof(ConfigurationState.Instance.System.AudioBackend)} contains an invalid value: {ConfigurationState.Instance.System.AudioBackend.Value}"); - } - - _audioBackendBox.Add(_audioBackendSelect); - _audioBackendSelect.Show(); - - _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume; - _audioVolumeLabel = new Label("Volume: "); - _audioVolumeSlider = new Scale(Orientation.Horizontal, 0, 100, 1); - _audioVolumeLabel.MarginStart = 10; - _audioVolumeSlider.ValuePos = PositionType.Right; - _audioVolumeSlider.WidthRequest = 200; - - _audioVolumeSlider.Value = _previousVolumeLevel * 100; - _audioVolumeSlider.ValueChanged += VolumeSlider_OnChange; - _audioBackendBox.Add(_audioVolumeLabel); - _audioBackendBox.Add(_audioVolumeSlider); - _audioVolumeLabel.Show(); - _audioVolumeSlider.Show(); - - bool openAlIsSupported = false; - bool soundIoIsSupported = false; - bool sdl2IsSupported = false; - - Task.Run(() => - { - openAlIsSupported = OpenALHardwareDeviceDriver.IsSupported; - soundIoIsSupported = !OperatingSystem.IsMacOS() && SoundIoHardwareDeviceDriver.IsSupported; - sdl2IsSupported = SDL2HardwareDeviceDriver.IsSupported; - }); - - // This function runs whenever the dropdown is opened - _audioBackendSelect.SetCellDataFunc(_audioBackendSelect.Cells[0], (layout, cell, model, iter) => - { - cell.Sensitive = ((AudioBackend)_audioBackendStore.GetValue(iter, 1)) switch - { - AudioBackend.OpenAl => openAlIsSupported, - AudioBackend.SoundIo => soundIoIsSupported, - AudioBackend.SDL2 => sdl2IsSupported, - AudioBackend.Dummy => true, - _ => throw new InvalidOperationException($"{nameof(_audioBackendStore)} contains an invalid value for iteration {iter}: {_audioBackendStore.GetValue(iter, 1)}"), - }; - }); - - if (OperatingSystem.IsMacOS()) - { - var store = (_graphicsBackend.Model as ListStore); - store.GetIter(out TreeIter openglIter, new TreePath(new[] { 1 })); - store.Remove(ref openglIter); - - _graphicsBackend.Model = store; - } - } - - private void UpdatePreferredGpuComboBox() - { - _preferredGpu.RemoveAll(); - - if (Enum.Parse(_graphicsBackend.ActiveId) == GraphicsBackend.Vulkan) - { - var devices = Graphics.Vulkan.VulkanRenderer.GetPhysicalDevices(); - string preferredGpuIdFromConfig = ConfigurationState.Instance.Graphics.PreferredGpu.Value; - string preferredGpuId = preferredGpuIdFromConfig; - bool noGpuId = string.IsNullOrEmpty(preferredGpuIdFromConfig); - - foreach (var device in devices) - { - string dGpu = device.IsDiscrete ? " (dGPU)" : ""; - _preferredGpu.Append(device.Id, $"{device.Name}{dGpu}"); - - // If there's no GPU selected yet, we just pick the first GPU. - // If there's a discrete GPU available, we always prefer that over the previous selection, - // as it is likely to have better performance and more features. - // If the configuration file already has a GPU selection, we always prefer that instead. - if (noGpuId && (string.IsNullOrEmpty(preferredGpuId) || device.IsDiscrete)) - { - preferredGpuId = device.Id; - } - } - - if (!string.IsNullOrEmpty(preferredGpuId)) - { - _preferredGpu.SetActiveId(preferredGpuId); - } - } - } - - private void PopulateNetworkInterfaces() - { - NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); - - foreach (NetworkInterface nif in interfaces) - { - string guid = nif.Id; - string name = nif.Name; - - _multiLanSelect.Append(guid, name); - } - } - - private void UpdateSystemTimeSpinners() - { - //Bind system time events - _systemTimeYearSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeMonthSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeDaySpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeHourSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - _systemTimeMinuteSpin.ValueChanged -= SystemTimeSpin_ValueChanged; - - //Apply actual system time + SystemTimeOffset to system time spin buttons - DateTime systemTime = DateTime.Now.AddSeconds(_systemTimeOffset); - - _systemTimeYearSpinAdjustment.Value = systemTime.Year; - _systemTimeMonthSpinAdjustment.Value = systemTime.Month; - _systemTimeDaySpinAdjustment.Value = systemTime.Day; - _systemTimeHourSpinAdjustment.Value = systemTime.Hour; - _systemTimeMinuteSpinAdjustment.Value = systemTime.Minute; - - //Format spin buttons text to include leading zeros - _systemTimeYearSpin.Text = systemTime.Year.ToString("0000"); - _systemTimeMonthSpin.Text = systemTime.Month.ToString("00"); - _systemTimeDaySpin.Text = systemTime.Day.ToString("00"); - _systemTimeHourSpin.Text = systemTime.Hour.ToString("00"); - _systemTimeMinuteSpin.Text = systemTime.Minute.ToString("00"); - - //Bind system time events - _systemTimeYearSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeMonthSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeDaySpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeHourSpin.ValueChanged += SystemTimeSpin_ValueChanged; - _systemTimeMinuteSpin.ValueChanged += SystemTimeSpin_ValueChanged; - } - - private void SaveSettings() - { - if (_directoryChanged) - { - List gameDirs = new(); - - _gameDirsBoxStore.GetIterFirst(out TreeIter treeIter); - - for (int i = 0; i < _gameDirsBoxStore.IterNChildren(); i++) - { - gameDirs.Add((string)_gameDirsBoxStore.GetValue(treeIter, 0)); - - _gameDirsBoxStore.IterNext(ref treeIter); - } - - ConfigurationState.Instance.UI.GameDirs.Value = gameDirs; - - _directoryChanged = false; - } - - HideCursorMode hideCursor = HideCursorMode.Never; - - if (_hideCursorOnIdle.Active) - { - hideCursor = HideCursorMode.OnIdle; - } - - if (_hideCursorAlways.Active) - { - hideCursor = HideCursorMode.Always; - } - - if (!float.TryParse(_resScaleText.Buffer.Text, out float resScaleCustom) || resScaleCustom <= 0.0f) - { - resScaleCustom = 1.0f; - } - - if (_validTzRegions.Contains(_systemTimeZoneEntry.Text)) - { - ConfigurationState.Instance.System.TimeZone.Value = _systemTimeZoneEntry.Text; - } - - MemoryManagerMode memoryMode = MemoryManagerMode.SoftwarePageTable; - - if (_mmHost.Active) - { - memoryMode = MemoryManagerMode.HostMapped; - } - - if (_mmHostUnsafe.Active) - { - memoryMode = MemoryManagerMode.HostMappedUnsafe; - } - - BackendThreading backendThreading = Enum.Parse(_galThreading.ActiveId); - if (ConfigurationState.Instance.Graphics.BackendThreading != backendThreading) - { - DriverUtilities.ToggleOGLThreading(backendThreading == BackendThreading.Off); - } - - ConfigurationState.Instance.Logger.EnableError.Value = _errorLogToggle.Active; - ConfigurationState.Instance.Logger.EnableTrace.Value = _traceLogToggle.Active; - ConfigurationState.Instance.Logger.EnableWarn.Value = _warningLogToggle.Active; - ConfigurationState.Instance.Logger.EnableInfo.Value = _infoLogToggle.Active; - ConfigurationState.Instance.Logger.EnableStub.Value = _stubLogToggle.Active; - ConfigurationState.Instance.Logger.EnableDebug.Value = _debugLogToggle.Active; - ConfigurationState.Instance.Logger.EnableGuest.Value = _guestLogToggle.Active; - ConfigurationState.Instance.Logger.EnableFsAccessLog.Value = _fsAccessLogToggle.Active; - ConfigurationState.Instance.Logger.EnableFileLog.Value = _fileLogToggle.Active; - ConfigurationState.Instance.Logger.GraphicsDebugLevel.Value = Enum.Parse(_graphicsDebugLevel.ActiveId); - ConfigurationState.Instance.System.EnableDockedMode.Value = _dockedModeToggle.Active; - ConfigurationState.Instance.EnableDiscordIntegration.Value = _discordToggle.Active; - ConfigurationState.Instance.CheckUpdatesOnStart.Value = _checkUpdatesToggle.Active; - ConfigurationState.Instance.ShowConfirmExit.Value = _showConfirmExitToggle.Active; - ConfigurationState.Instance.HideCursor.Value = hideCursor; - ConfigurationState.Instance.Graphics.EnableVsync.Value = _vSyncToggle.Active; - ConfigurationState.Instance.Graphics.EnableShaderCache.Value = _shaderCacheToggle.Active; - ConfigurationState.Instance.Graphics.EnableTextureRecompression.Value = _textureRecompressionToggle.Active; - ConfigurationState.Instance.Graphics.EnableMacroHLE.Value = _macroHLEToggle.Active; - ConfigurationState.Instance.System.EnablePtc.Value = _ptcToggle.Active; - ConfigurationState.Instance.System.EnableInternetAccess.Value = _internetToggle.Active; - ConfigurationState.Instance.System.EnableFsIntegrityChecks.Value = _fsicToggle.Active; - ConfigurationState.Instance.System.MemoryManagerMode.Value = memoryMode; - ConfigurationState.Instance.System.ExpandRam.Value = _expandRamToggle.Active; - ConfigurationState.Instance.System.IgnoreMissingServices.Value = _ignoreToggle.Active; - ConfigurationState.Instance.Hid.EnableKeyboard.Value = _directKeyboardAccess.Active; - ConfigurationState.Instance.Hid.EnableMouse.Value = _directMouseAccess.Active; - ConfigurationState.Instance.UI.EnableCustomTheme.Value = _custThemeToggle.Active; - ConfigurationState.Instance.System.Language.Value = Enum.Parse(_systemLanguageSelect.ActiveId); - ConfigurationState.Instance.System.Region.Value = Enum.Parse(_systemRegionSelect.ActiveId); - ConfigurationState.Instance.System.SystemTimeOffset.Value = _systemTimeOffset; - ConfigurationState.Instance.UI.CustomThemePath.Value = _custThemePath.Buffer.Text; - ConfigurationState.Instance.Graphics.ShadersDumpPath.Value = _graphicsShadersDumpPath.Buffer.Text; - ConfigurationState.Instance.System.FsGlobalAccessLogMode.Value = (int)_fsLogSpinAdjustment.Value; - ConfigurationState.Instance.Graphics.MaxAnisotropy.Value = float.Parse(_anisotropy.ActiveId, CultureInfo.InvariantCulture); - ConfigurationState.Instance.Graphics.AspectRatio.Value = Enum.Parse(_aspectRatio.ActiveId); - ConfigurationState.Instance.Graphics.BackendThreading.Value = backendThreading; - ConfigurationState.Instance.Graphics.GraphicsBackend.Value = Enum.Parse(_graphicsBackend.ActiveId); - ConfigurationState.Instance.Graphics.PreferredGpu.Value = _preferredGpu.ActiveId; - ConfigurationState.Instance.Graphics.ResScale.Value = int.Parse(_resScaleCombo.ActiveId); - ConfigurationState.Instance.Graphics.ResScaleCustom.Value = resScaleCustom; - ConfigurationState.Instance.System.AudioVolume.Value = (float)_audioVolumeSlider.Value / 100.0f; - ConfigurationState.Instance.Graphics.AntiAliasing.Value = Enum.Parse(_antiAliasing.ActiveId); - ConfigurationState.Instance.Graphics.ScalingFilter.Value = Enum.Parse(_scalingFilter.ActiveId); - ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; - - _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; - - ConfigurationState.Instance.Multiplayer.Mode.Value = Enum.Parse(_multiModeSelect.ActiveId); - ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; - - if (_audioBackendSelect.GetActiveIter(out TreeIter activeIter)) - { - ConfigurationState.Instance.System.AudioBackend.Value = (AudioBackend)_audioBackendStore.GetValue(activeIter, 1); - } - - ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); - - _parent.UpdateInternetAccess(); - MainWindow.UpdateGraphicsConfig(); - ThemeHelper.ApplyTheme(); - } - - // - // Events - // - private void TimeZoneEntry_FocusOut(object sender, FocusOutEventArgs e) - { - if (!_validTzRegions.Contains(_systemTimeZoneEntry.Text)) - { - _systemTimeZoneEntry.Text = _timeZoneContentManager.SanityCheckDeviceLocationName(ConfigurationState.Instance.System.TimeZone); - } - } - - private bool TimeZoneMatchFunc(EntryCompletion compl, string key, TreeIter iter) - { - key = key.Trim().Replace(' ', '_'); - - return ((string)compl.Model.GetValue(iter, 1)).Contains(key, StringComparison.OrdinalIgnoreCase) || // region - ((string)compl.Model.GetValue(iter, 2)).StartsWith(key, StringComparison.OrdinalIgnoreCase) || // abbr - ((string)compl.Model.GetValue(iter, 0))[3..].StartsWith(key); // offset - } - - private void SystemTimeSpin_ValueChanged(object sender, EventArgs e) - { - int year = _systemTimeYearSpin.ValueAsInt; - int month = _systemTimeMonthSpin.ValueAsInt; - int day = _systemTimeDaySpin.ValueAsInt; - int hour = _systemTimeHourSpin.ValueAsInt; - int minute = _systemTimeMinuteSpin.ValueAsInt; - - if (!DateTime.TryParse(year + "-" + month + "-" + day + " " + hour + ":" + minute, out DateTime newTime)) - { - UpdateSystemTimeSpinners(); - - return; - } - - newTime = newTime.AddSeconds(DateTime.Now.Second).AddMilliseconds(DateTime.Now.Millisecond); - - long systemTimeOffset = (long)Math.Ceiling((newTime - DateTime.Now).TotalMinutes) * 60L; - - if (_systemTimeOffset != systemTimeOffset) - { - _systemTimeOffset = systemTimeOffset; - UpdateSystemTimeSpinners(); - } - } - - private void AddDir_Pressed(object sender, EventArgs args) - { - if (Directory.Exists(_addGameDirBox.Buffer.Text)) - { - _gameDirsBoxStore.AppendValues(_addGameDirBox.Buffer.Text); - _directoryChanged = true; - } - else - { - FileChooserNative fileChooser = new("Choose the game directory to add to the list", this, FileChooserAction.SelectFolder, "Add", "Cancel") - { - SelectMultiple = true, - }; - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - _directoryChanged = false; - foreach (string directory in fileChooser.Filenames) - { - if (_gameDirsBoxStore.GetIterFirst(out TreeIter treeIter)) - { - do - { - if (directory.Equals((string)_gameDirsBoxStore.GetValue(treeIter, 0))) - { - break; - } - } while (_gameDirsBoxStore.IterNext(ref treeIter)); - } - - if (!_directoryChanged) - { - _gameDirsBoxStore.AppendValues(directory); - } - } - - _directoryChanged = true; - } - - fileChooser.Dispose(); - } - - _addGameDirBox.Buffer.Text = ""; - - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - } - - private void RemoveDir_Pressed(object sender, EventArgs args) - { - TreeSelection selection = _gameDirsBox.Selection; - - if (selection.GetSelected(out TreeIter treeIter)) - { - _gameDirsBoxStore.Remove(ref treeIter); - - _directoryChanged = true; - } - - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - } - - private void CustThemeToggle_Activated(object sender, EventArgs args) - { - _custThemePath.Sensitive = _custThemeToggle.Active; - _custThemePathLabel.Sensitive = _custThemeToggle.Active; - _browseThemePath.Sensitive = _custThemeToggle.Active; - } - - private void BrowseThemeDir_Pressed(object sender, EventArgs args) - { - using (FileChooserNative fileChooser = new("Choose the theme to load", this, FileChooserAction.Open, "Select", "Cancel")) - { - FileFilter filter = new() - { - Name = "Theme Files", - }; - filter.AddPattern("*.css"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - _custThemePath.Buffer.Text = fileChooser.Filename; - } - } - - _browseThemePath.SetStateFlags(StateFlags.Normal, true); - } - - private void ConfigureController_Pressed(object sender, PlayerIndex playerIndex) - { - ((ToggleButton)sender).SetStateFlags(StateFlags.Normal, true); - - ControllerWindow controllerWindow = new(_parent, playerIndex); - - controllerWindow.SetSizeRequest((int)(controllerWindow.DefaultWidth * Program.WindowScaleFactor), (int)(controllerWindow.DefaultHeight * Program.WindowScaleFactor)); - controllerWindow.Show(); - } - - private void VolumeSlider_OnChange(object sender, EventArgs args) - { - ConfigurationState.Instance.System.AudioVolume.Value = (float)(_audioVolumeSlider.Value / 100); - } - - private void SaveToggle_Activated(object sender, EventArgs args) - { - SaveSettings(); - Dispose(); - } - - private void ApplyToggle_Activated(object sender, EventArgs args) - { - SaveSettings(); - } - - private void CloseToggle_Activated(object sender, EventArgs args) - { - ConfigurationState.Instance.System.AudioVolume.Value = _previousVolumeLevel; - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade deleted file mode 100644 index f0dbd6b63..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade +++ /dev/null @@ -1,3221 +0,0 @@ - - - - - - 3 - 1 - 10 - - - 101 - 1 - 5 - 1 - - - 1 - 31 - 1 - 5 - - - 23 - 1 - 5 - - - 59 - 1 - 5 - - - 1 - 12 - 1 - 5 - - - 2000 - 2060 - 1 - 10 - - - 0 - True - True - - - False - Ryujinx - Settings - True - center - 650 - 650 - - - True - False - vertical - - - True - True - in - - - True - False - - - True - True - - - True - False - 5 - 10 - 5 - vertical - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - General - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - Enable Discord Rich Presence - True - True - False - Choose whether or not to display Ryujinx on your "currently playing" Discord activity - start - True - - - False - True - 5 - 0 - - - - - Check for Updates on Launch - True - True - False - start - True - - - False - True - 5 - 1 - - - - - Show "Confirm Exit" Dialog - True - True - False - start - True - - - False - True - 5 - 2 - - - - - True - False - - - True - False - end - Hide Cursor: - - - False - True - 5 - 2 - - - - - Never - True - True - False - start - 5 - 5 - True - True - - - False - True - 3 - - - - - On Idle - True - True - False - start - 5 - 5 - True - _hideCursorNever - - - False - True - 4 - - - - - Always - True - True - False - start - 5 - 5 - True - _hideCursorNever - - - False - True - 5 - - - - - False - True - 5 - 4 - - - - - True - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - 5 - 5 - - - False - True - 5 - 2 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - Game Directories - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - True - 10 - in - - - True - True - False - False - - - - - - - - - True - True - 0 - - - - - True - False - - - True - True - Enter a game directory to add to the list - - - True - True - 0 - - - - - Add - 80 - True - True - True - Add a game directory to the list - 5 - - - - False - True - 1 - - - - - Remove - 80 - True - True - True - Remove selected game directory - 5 - - - - False - True - 3 - - - - - False - True - 1 - - - - - True - True - 1 - - - - - True - True - 5 - 4 - - - - - True - False - 5 - 5 - - - False - True - 5 - 5 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - Themes - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - Use Custom Theme - True - True - False - Enable or disable custom themes in the GUI - start - True - - - - False - True - 5 - 1 - - - - - True - False - - - True - False - Path to custom GUI theme - Custom Theme Path: - - - False - True - 5 - 0 - - - - - True - True - Path to custom GUI theme - center - - - True - True - 1 - - - - - Browse... - 80 - True - True - True - Browse for a custom GUI theme - 5 - - - - False - True - 2 - - - - - False - True - 10 - 2 - - - - - False - True - 1 - - - - - False - True - 5 - 6 - - - - - - - True - False - General - - - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - 5 - 5 - - - Enable Docked Mode - True - True - False - Docked mode makes the emulated system behave as a docked Nintendo Switch. This improves graphical fidelity in most games. Conversely, disabling this will make the emulated system behave as a handheld Nintendo Switch, reducing graphics quality. Configure player 1 controls if planning to use docked mode; configure handheld controls if planning to use handheld mode. Leave ON if unsure. - True - - - False - True - 10 - 0 - - - - - Direct Keyboard Access - True - True - False - Direct keyboard access (HID) support. Provides games access to your keyboard as a text entry device. - True - - - False - False - 10 - 1 - - - - - Direct Mouse Access - True - True - False - Direct mouse access (HID) support. Provides games access to your mouse as a pointing device. - True - - - False - False - 10 - 2 - - - - - False - True - 5 - 0 - - - - - True - False - - - False - True - 1 - - - - - - True - False - center - center - 20 - - - True - False - vertical - - - True - False - 20 - 20 - Player 1 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 0 - 0 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 3 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 4 - 0 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 2 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 2 - 0 - - - - - True - False - vertical - - - True - False - 20 - 20 - Handheld - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 4 - 4 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 6 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 4 - 2 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 5 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 2 - 2 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 7 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 0 - 4 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 4 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 0 - 2 - - - - - True - False - vertical - - - True - False - 20 - 20 - Player 8 - - - False - True - 0 - - - - - Configure - True - True - True - 20 - 20 - 20 - 20 - - - False - True - 1 - - - - - 2 - 4 - - - - - True - False - - - 1 - 0 - - - - - True - False - - - 3 - 0 - - - - - True - False - - - 3 - 2 - - - - - True - False - - - 3 - 4 - - - - - True - False - - - 1 - 2 - - - - - True - False - - - 1 - 4 - - - - - True - False - - - 1 - 1 - - - - - True - False - - - 1 - 3 - - - - - True - False - - - 3 - 1 - - - - - True - False - - - 3 - 3 - - - - - True - False - - - 0 - 1 - - - - - True - False - - - 2 - 1 - - - - - True - False - - - 4 - 1 - - - - - True - False - - - 0 - 3 - - - - - True - False - - - 2 - 3 - - - - - True - False - - - 4 - 3 - - - - - True - True - 2 - - - - - True - False - - - False - True - 3 - - - - - 1 - - - - - True - False - Input - - - 1 - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - start - 5 - 5 - vertical - - - True - False - start - 5 - Core - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - False - - - True - False - Change System Region - end - System Region: - - - False - True - 5 - 2 - - - - - True - False - Change System Region - 5 - - Japan - USA - Europe - Australia - China - Korea - Taiwan - - - - False - True - 3 - - - - - False - True - 5 - 0 - - - - - True - False - - - True - False - Change System Language - end - System Language: - - - False - True - 5 - 0 - - - - - True - False - Change System Language - - American English - British English - Canadian French - Chinese - Dutch - French - German - Italian - Japanese - Korean - Latin American Spanish - Portuguese - Russian - Simplified Chinese - Spanish - Taiwanese - Traditional Chinese - Brazilian Portuguese - - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - - - True - False - Change System TimeZone - end - System TimeZone: - - - False - True - 5 - 1 - - - - - True - True - Change System TimeZone - 5 - _systemTimeZoneCompletion - - - False - True - 2 - - - - - False - True - 5 - 2 - - - - - True - False - - - True - False - Change System Time - end - System Time: - - - False - True - 5 - 0 - - - - - True - True - 2000 - vertical - _systemTimeYearSpinAdjustment - True - 2000 - - - False - True - 1 - - - - - True - False - end - - - - - False - True - 5 - 2 - - - - - True - True - 1 - vertical - _systemTimeMonthSpinAdjustment - True - 1 - - - False - True - 3 - - - - - True - False - end - - - - - False - True - 5 - 4 - - - - - True - True - 1 - vertical - _systemTimeDaySpinAdjustment - True - 1 - - - False - True - 5 - - - - - True - True - 0 - vertical - _systemTimeHourSpinAdjustment - True - - - False - True - 6 - - - - - True - False - end - : - - - False - True - 5 - 7 - - - - - True - True - 0 - vertical - _systemTimeMinuteSpinAdjustment - True - - - False - True - 8 - - - - - False - True - 5 - 3 - - - - - Enable VSync - True - True - False - Emulated console's Vertical Sync. Essentially a frame-limiter for the majority of games; disabling it may cause games to run at higher speed or make loading screens take longer or get stuck. Can be toggled in-game with a hotkey of your preference. We recommend doing this if you plan on disabling it. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 4 - - - - - Enable PPTC (Profiled Persistent Translation Cache) - True - True - False - Saves translated JIT functions so that they do not need to be translated every time the game loads. Reduces stuttering and significantly speeds up boot times after the first boot of a game. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 6 - - - - - Enable Guest Internet Access - True - True - False - Allows the emulated application to connect to the Internet. Games with a LAN mode can connect to each other when this is enabled and the systems are connected to the same access point. This includes real consoles as well. Does NOT allow connecting to Nintendo servers. May cause crashing in certain games that try to connect to the Internet. Leave OFF if unsure. - start - 5 - 5 - True - - - False - True - 7 - - - - - Enable FS Integrity Checks - True - True - False - Checks for corrupt files when booting a game, and if corrupt files are detected, displays a hash error in the log. Has no impact on performance and is meant to help troubleshooting. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 8 - - - - - True - True - 1 - - - - - True - False - - - - - - True - False - Changes the backend used to render audio. SDL2 is the preferred one, while OpenAL and SoundIO are used as fallbacks. Dummy will have no sound. Set to SDL2 if unsure. - end - 5 - Audio Backend: - - - False - True - 5 - 2 - - - - - False - True - 5 - 2 - - - - - True - False - - - - - - True - False - Change how guest memory is mapped and accessed. Greatly affects emulated CPU performance. Set to HOST UNCHECKED if unsure. - end - 5 - Memory Manager Mode: - - - False - True - 5 - 2 - - - - - Software - True - True - False - Use a software page table for address translation. Highest accuracy but slowest performance. - start - 5 - 5 - True - - - False - True - 3 - - - - - Host (fast) - True - True - False - Directly map memory in the host address space. Much faster JIT compilation and execution. - start - 5 - 5 - True - _mmSoftware - - - False - True - 4 - - - - - Host Unchecked (fastest, unsafe) - True - True - False - Directly map memory, but do not mask the address within the guest address space before access. Faster, but at the cost of safety. The guest application can access memory from anywhere in Ryujinx, so only run programs you trust with this mode. - start - 5 - 5 - True - _mmSoftware - - - False - True - 5 - - - - - False - True - 5 - 3 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - - - False - True - 5 - 1 - - - - - True - False - start - 5 - 5 - vertical - - - True - False - - - True - False - start - 5 - Hacks - - - - - - False - True - 0 - - - - - True - False - start - 5 - (may cause instability) - - - False - True - 1 - - - - - False - True - 1 - - - - - True - False - 10 - 10 - vertical - - - Use alternative memory layout (Developers) - True - True - False - Utilizes an alternative MemoryMode layout to mimic a Switch development model. This is only useful for higher-resolution texture packs or 4k resolution mods. Does NOT improve performance. Leave OFF if unsure. - start - 5 - 5 - True - - - False - True - 0 - - - - - Ignore Missing Services - True - True - False - Ignores unimplemented Horizon OS services. This may help in bypassing crashes when booting certain games. Leave OFF if unsure. - start - 5 - 5 - True - - - False - True - 1 - - - - - True - True - 2 - - - - - False - True - 5 - 4 - - - - - 2 - - - - - True - False - end - System - - - 2 - False - - - - - True - False - 5 - vertical - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - 5 - 5 - Features - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - - - True - False - Executes graphics backend commands on a second thread. Speeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading. Set to AUTO if unsure. - Graphics Backend Multithreading: - - - False - True - 5 - 0 - - - - - True - False - Executes graphics backend commands on a second thread. Speeds up shader compilation, reduces stuttering, and improves performance on GPU drivers without multithreading support of their own. Slightly better performance on drivers with multithreading. Set to AUTO if unsure. - -1 - - Auto - Off - On - - - - False - True - 1 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - - - True - False - Graphics Backend to use - Graphics Backend: - - - False - True - 5 - 0 - - - - - True - False - Graphics Backend to use - -1 - - Vulkan - OpenGL - - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - 5 - 5 - - - True - False - Preferred GPU (Vulkan only) - Preferred GPU: - - - False - True - 5 - 0 - - - - - True - False - Preferred GPU (Vulkan only) - -1 - - - False - True - 1 - - - - - False - True - 5 - 2 - - - - - False - True - 2 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - 5 - 5 - Enhancements - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - Enable Shader Cache - True - True - False - Saves a disk shader cache which reduces stuttering in subsequent runs. Leave ON if unsure. - start - 5 - 5 - True - - - False - True - 0 - - - - - Enable Texture Recompression - True - True - False - Enables or disables Texture Recompression. Reduces VRAM usage at the cost of texture quality, and may also increase stuttering - start - 5 - 5 - True - - - False - True - 1 - - - - - Enable Macro HLE - True - True - False - Enables or disables high-level emulation of Macro code. Improves performance but may cause graphical glitches in some games - start - 5 - 5 - True - - - False - True - 2 - - - - - True - False - 5 - 5 - - - True - False - Resolution Scale applied to applicable render targets. - Resolution Scale: - - - False - True - 5 - 0 - - - - - True - False - Resolution Scale applied to applicable render targets. - 1 - - Native (720p/1080p) - 2x (1440p/2160p) - 3x (2160p/3240p) - 4x (2880p/4320p) - Custom (not recommended) - - - - False - True - 1 - - - - - True - True - Floating point resolution scale, such as 1.5. Non-integral scales are more likely to cause issues or crash. - center - False - 1.0 - number - - - True - True - 2 - - - - - False - True - 5 - 3 - - - - - True - False - 5 - 5 - - - True - False - Applies a final effect to the game render - Post Processing Effect: - - - False - True - 5 - 0 - - - - - True - False - Applies anti-aliasing to the game render - 1 - - None - FXAA - SMAA Low - SMAA Medium - SMAA High - SMAA Ultra - - - - False - True - 1 - - - - - False - True - 5 - 4 - - - - - 100 - True - False - 5 - 5 - - - True - False - Enables Framebuffer Upscaling - Upscale: - - - False - True - 5 - 0 - - - - - True - False - Enables Framebuffer Upscaling - 1 - - Bilinear - Nearest - FSR - - - - False - True - 1 - - - - - 200 - True - True - 5 - _scalingFilterLevel - 1 - right - - - False - True - 3 - - - - - False - True - 5 - 5 - - - - - True - False - 5 - 5 - - - True - False - Level of Anisotropic Filtering (set to Auto to use the value requested by the game) - Anisotropic Filtering: - - - False - True - 5 - 0 - - - - - True - False - Level of Anisotropic Filtering (set to Auto to use the value requested by the game) - -1 - - Auto - 2x - 4x - 8x - 16x - - - - False - True - 1 - - - - - False - True - 5 - 6 - - - - - True - False - 5 - 5 - - - True - False - Aspect Ratio applied to the renderer window. - Aspect Ratio: - - - False - True - 5 - 0 - - - - - True - False - Aspect Ratio applied to the renderer window. - 1 - - 4:3 - 16:9 - 16:10 - 21:9 - 32:9 - Stretch to Fit Window - - - - False - True - 1 - - - - - False - True - 5 - 7 - - - - - False - True - 2 - - - - - False - True - 5 - 2 - - - - - True - False - - - False - True - 5 - 3 - - - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - 5 - 5 - Developer Options - - - - - - False - True - 0 - - - - - True - False - 10 - 10 - vertical - - - True - False - 5 - 5 - - - True - False - Graphics Shaders Dump Path - Graphics Shaders Dump Path: - - - False - True - 5 - 0 - - - - - True - True - Graphics Shaders Dump Path - center - False - - - True - True - 1 - - - - - False - True - 5 - 0 - - - - - False - True - 1 - - - - - False - True - 5 - 4 - - - - - 3 - - - - - True - False - Graphics - - - 3 - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - 5 - 5 - vertical - - - True - False - start - 5 - Logging - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - Enable Logging to File - True - True - False - Saves console logging to a log file on disk. Does not affect performance. - start - 5 - 5 - True - - - False - True - 0 - - - - - Enable Stub Logs - True - True - False - Prints stub log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 3 - - - - - Enable Info Logs - True - True - False - Prints info log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 4 - - - - - Enable Warning Logs - True - True - False - Prints warning log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 5 - - - - - Enable Error Logs - True - True - False - Prints error log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 6 - - - - - Enable Guest Logs - True - True - False - Prints guest log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 7 - - - - - Enable Fs Access Logs - True - True - False - Enables FS access log output to the console. Possible modes are 0-3 - start - 5 - 5 - True - - - False - True - 8 - - - - - True - False - - - True - False - Enables FS access log output to the console. Possible modes are 0-3 - Fs Global Access Log Mode: - - - False - True - 5 - 0 - - - - - True - True - Enables FS access log output to the console. Possible modes are 0-3 - 0 - _fsLogSpinAdjustment - - - True - True - 1 - - - - - False - True - 5 - 9 - - - - - True - True - 1 - - - - - False - True - 5 - 0 - - - - - True - False - 5 - 5 - 10 - vertical - - - True - False - Use with care - start - 5 - Developer Options (WARNING: Will reduce performance) - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - True - False - 5 - - - True - False - Requires appropriate log levels enabled. - Graphics Backend Log Level - - - False - True - 5 - 22 - - - - - True - False - Requires appropriate log levels enabled. - 5 - - - False - True - 22 - - - - - False - True - 1 - - - - - Enable Debug Logs - True - True - False - Prints debug log messages in the console. Only use this if specifically instructed by a staff member, as it will make logs difficult to read and worsen emulator performance. - start - 5 - 5 - True - - - False - True - 21 - - - - - Enable Trace Logs - True - True - False - Prints trace log messages in the console. Does not affect performance. - start - 5 - 5 - True - - - False - True - 22 - - - - - False - True - 1 - - - - - False - True - 5 - 22 - - - - - 4 - - - - - True - False - Logging - - - 4 - False - - - - - True - False - 5 - 10 - 5 - vertical - - - True - False - start - 5 - 5 - vertical - - - True - False - start - 5 - Multiplayer - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - True - False - - - True - False - Change Multiplayer Mode - end - Mode: - - - False - True - 5 - 0 - - - - - True - False - Change Multiplayer Mode - Disabled - - Disabled - ldn_mitm - - - - False - True - 1 - - - - - False - True - 3 - - - - - True - True - 2 - - - - - False - True - 5 - 0 - - - - - True - False - start - 5 - 5 - vertical - - - True - False - start - 5 - LAN Mode - - - - - - False - True - 0 - - - - - True - False - start - 10 - 10 - vertical - - - True - False - - - True - False - The network interface used for LAN/LDN features - end - Network Interface: - - - False - True - 5 - 0 - - - - - True - False - The network interface used for LAN/LDN features - 0 - - Default - - - - False - True - 1 - - - - - False - True - 5 - 1 - - - - - True - False - start - 5 - To use LAN functionality in games, Enable Guest Internet Access must be checked in System. - True - - - False - True - 1 - - - - - True - True - 2 - - - - - False - True - 5 - 1 - - - - - 5 - - - - - True - False - Multiplayer - - - 5 - False - - - - - - - - - True - True - 0 - - - - - True - False - 5 - 3 - 3 - 5 - end - - - Save - True - True - True - - - - False - False - 0 - - - - - Close - True - True - True - - - - False - False - 1 - - - - - Apply - True - True - True - - - - True - True - 2 - - - - - False - False - 1 - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs deleted file mode 100644 index a08f59597..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.cs +++ /dev/null @@ -1,234 +0,0 @@ -using Gtk; -using LibHac.Common; -using LibHac.Fs; -using LibHac.Fs.Fsa; -using LibHac.Ncm; -using LibHac.Ns; -using LibHac.Tools.FsSystem; -using LibHac.Tools.FsSystem.NcaUtils; -using Ryujinx.Common.Configuration; -using Ryujinx.Common.Utilities; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.Loaders.Processes.Extensions; -using Ryujinx.HLE.Utilities; -using Ryujinx.UI.App.Common; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Widgets; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using GUI = Gtk.Builder.ObjectAttribute; -using SpanHelpers = LibHac.Common.SpanHelpers; - -namespace Ryujinx.UI.Windows -{ - public class TitleUpdateWindow : Window - { - private readonly MainWindow _parent; - private readonly VirtualFileSystem _virtualFileSystem; - private readonly ApplicationData _applicationData; - private readonly string _updateJsonPath; - - private TitleUpdateMetadata _titleUpdateWindowData; - - private readonly Dictionary _radioButtonToPathDictionary; - private static readonly TitleUpdateMetadataJsonSerializerContext _serializerContext = new(JsonHelper.GetDefaultSerializerOptions()); - -#pragma warning disable CS0649, IDE0044 // Field is never assigned to, Add readonly modifier - [GUI] Label _baseTitleInfoLabel; - [GUI] Box _availableUpdatesBox; - [GUI] RadioButton _noUpdateRadioButton; -#pragma warning restore CS0649, IDE0044 - - public TitleUpdateWindow(MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : this(new Builder("Ryujinx.Gtk3.UI.Windows.TitleUpdateWindow.glade"), parent, virtualFileSystem, applicationData) { } - - private TitleUpdateWindow(Builder builder, MainWindow parent, VirtualFileSystem virtualFileSystem, ApplicationData applicationData) : base(builder.GetRawOwnedObject("_titleUpdateWindow")) - { - _parent = parent; - - builder.Autoconnect(this); - - _applicationData = applicationData; - _virtualFileSystem = virtualFileSystem; - _updateJsonPath = System.IO.Path.Combine(AppDataManager.GamesDirPath, applicationData.IdBaseString, "updates.json"); - _radioButtonToPathDictionary = new Dictionary(); - - try - { - _titleUpdateWindowData = JsonHelper.DeserializeFromFile(_updateJsonPath, _serializerContext.TitleUpdateMetadata); - } - catch - { - _titleUpdateWindowData = new TitleUpdateMetadata - { - Selected = "", - Paths = new List(), - }; - } - - _baseTitleInfoLabel.Text = $"Updates Available for {applicationData.Name} [{applicationData.IdBaseString}]"; - - // Try to get updates from PFS first - AddUpdate(_applicationData.Path, true); - - foreach (string path in _titleUpdateWindowData.Paths) - { - AddUpdate(path); - } - - if (_titleUpdateWindowData.Selected == "") - { - _noUpdateRadioButton.Active = true; - } - else - { - foreach ((RadioButton update, var _) in _radioButtonToPathDictionary.Where(keyValuePair => keyValuePair.Value == _titleUpdateWindowData.Selected)) - { - update.Active = true; - } - } - } - - private void AddUpdate(string path, bool ignoreNotFound = false) - { - if (!File.Exists(path) || _radioButtonToPathDictionary.ContainsValue(path)) - { - return; - } - - IntegrityCheckLevel checkLevel = ConfigurationState.Instance.System.EnableFsIntegrityChecks - ? IntegrityCheckLevel.ErrorOnInvalid - : IntegrityCheckLevel.None; - - try - { - using IFileSystem pfs = PartitionFileSystemUtils.OpenApplicationFileSystem(path, _virtualFileSystem); - - Dictionary updates = pfs.GetContentData(ContentMetaType.Patch, _virtualFileSystem, checkLevel); - - Nca patchNca = null; - Nca controlNca = null; - - if (updates.TryGetValue(_applicationData.Id, out ContentMetaData update)) - { - patchNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Program); - controlNca = update.GetNcaByType(_virtualFileSystem.KeySet, LibHac.Ncm.ContentType.Control); - } - - if (controlNca != null && patchNca != null) - { - ApplicationControlProperty controlData = new(); - - using var nacpFile = new UniqueRef(); - - controlNca.OpenFileSystem(NcaSectionType.Data, IntegrityCheckLevel.None).OpenFile(ref nacpFile.Ref, "/control.nacp".ToU8Span(), OpenMode.Read).ThrowIfFailure(); - nacpFile.Get.Read(out _, 0, SpanHelpers.AsByteSpan(ref controlData), ReadOption.None).ThrowIfFailure(); - - string radioLabel = $"Version {controlData.DisplayVersionString.ToString()} - {path}"; - - if (System.IO.Path.GetExtension(path).ToLower() == ".xci") - { - radioLabel = "Bundled: " + radioLabel; - } - - RadioButton radioButton = new(radioLabel); - radioButton.JoinGroup(_noUpdateRadioButton); - - _availableUpdatesBox.Add(radioButton); - _radioButtonToPathDictionary.Add(radioButton, path); - - radioButton.Show(); - radioButton.Active = true; - } - else - { - if (!ignoreNotFound) - { - GtkDialog.CreateErrorDialog("The specified file does not contain an update for the selected title!"); - } - } - } - catch (Exception exception) - { - GtkDialog.CreateErrorDialog($"{exception.Message}. Errored File: {path}"); - } - } - - private void RemoveUpdates(bool removeSelectedOnly = false) - { - foreach (RadioButton radioButton in _noUpdateRadioButton.Group) - { - if (radioButton.Label != "No Update" && (!removeSelectedOnly || radioButton.Active)) - { - _availableUpdatesBox.Remove(radioButton); - _radioButtonToPathDictionary.Remove(radioButton); - radioButton.Dispose(); - } - } - } - - private void AddButton_Clicked(object sender, EventArgs args) - { - using FileChooserNative fileChooser = new("Select update files", this, FileChooserAction.Open, "Add", "Cancel"); - - fileChooser.SelectMultiple = true; - - FileFilter filter = new() - { - Name = "Switch Game Updates", - }; - filter.AddPattern("*.nsp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - foreach (string path in fileChooser.Filenames) - { - AddUpdate(path); - } - } - } - - private void RemoveButton_Clicked(object sender, EventArgs args) - { - RemoveUpdates(true); - } - - private void RemoveAllButton_Clicked(object sender, EventArgs args) - { - RemoveUpdates(); - } - - private void SaveButton_Clicked(object sender, EventArgs args) - { - _titleUpdateWindowData.Paths.Clear(); - _titleUpdateWindowData.Selected = ""; - - foreach (string paths in _radioButtonToPathDictionary.Values) - { - _titleUpdateWindowData.Paths.Add(paths); - } - - foreach (RadioButton radioButton in _noUpdateRadioButton.Group) - { - if (radioButton.Active) - { - _titleUpdateWindowData.Selected = _radioButtonToPathDictionary.TryGetValue(radioButton, out string updatePath) ? updatePath : ""; - } - } - - JsonHelper.SerializeToFile(_updateJsonPath, _titleUpdateWindowData, _serializerContext.TitleUpdateMetadata); - - _parent.UpdateGameTable(); - - Dispose(); - } - - private void CancelButton_Clicked(object sender, EventArgs args) - { - Dispose(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade deleted file mode 100644 index cfbac86dd..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/TitleUpdateWindow.glade +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - False - Ryujinx - Title Update Manager - True - center - 550 - 250 - - - True - False - vertical - - - True - False - vertical - - - True - False - 10 - 10 - 10 - 10 - Available Updates - - - False - True - 0 - - - - - True - True - 10 - 10 - in - - - True - False - - - True - False - vertical - - - No Update - True - True - False - True - True - - - False - True - 0 - - - - - - - - - True - True - 1 - - - - - True - True - 0 - - - - - True - False - - - True - False - 10 - 10 - start - - - Add - True - True - True - Adds an update to this list - 10 - - - - True - True - 0 - - - - - Remove - True - True - True - Removes the selected update - 10 - - - - True - True - 1 - - - - - Remove All - True - True - True - Removes all the updates - 10 - - - - True - True - 2 - - - - - True - True - 0 - - - - - True - False - 10 - 10 - end - - - Save - True - True - True - 10 - 2 - 2 - - - - True - True - 0 - - - - - Cancel - True - True - True - 10 - 2 - 2 - - - - True - True - 1 - - - - - True - True - 1 - - - - - False - True - 1 - - - - - - - - - diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs deleted file mode 100644 index bc5a18f95..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.Designer.cs +++ /dev/null @@ -1,255 +0,0 @@ -using Gtk; -using Pango; -using System; - -namespace Ryujinx.UI.Windows -{ - public partial class UserProfilesManagerWindow : Window - { - private Box _mainBox; - private Label _selectedLabel; - private Box _selectedUserBox; - private Image _selectedUserImage; - private Box _selectedUserInfoBox; - private Entry _selectedUserNameEntry; - private Label _selectedUserIdLabel; - private Box _selectedUserButtonsBox; - private Button _saveProfileNameButton; - private Button _changeProfileImageButton; - private Box _usersTreeViewBox; - private Label _availableUsersLabel; - private ScrolledWindow _usersTreeViewWindow; - private ListStore _tableStore; - private TreeView _usersTreeView; - private Box _bottomBox; - private Button _addButton; - private Button _deleteButton; - private Button _closeButton; - - private void InitializeComponent() - { - // - // UserProfilesManagerWindow - // - CanFocus = false; - Resizable = false; - Modal = true; - WindowPosition = WindowPosition.Center; - DefaultWidth = 620; - DefaultHeight = 548; - TypeHint = Gdk.WindowTypeHint.Dialog; - - // - // _mainBox - // - _mainBox = new Box(Orientation.Vertical, 0); - - // - // _selectedLabel - // - _selectedLabel = new Label("Selected User Profile:") - { - Margin = 15, - Attributes = new AttrList(), - Halign = Align.Start, - }; - _selectedLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - - // - // _viewBox - // - _usersTreeViewBox = new Box(Orientation.Vertical, 0); - - // - // _SelectedUserBox - // - _selectedUserBox = new Box(Orientation.Horizontal, 0) - { - MarginStart = 30, - }; - - // - // _selectedUserImage - // - _selectedUserImage = new Image(); - - // - // _selectedUserInfoBox - // - _selectedUserInfoBox = new Box(Orientation.Vertical, 0) - { - Homogeneous = true, - }; - - // - // _selectedUserNameEntry - // - _selectedUserNameEntry = new Entry("") - { - MarginStart = 15, - MaxLength = (int)MaxProfileNameLength, - }; - _selectedUserNameEntry.KeyReleaseEvent += SelectedUserNameEntry_KeyReleaseEvent; - - // - // _selectedUserIdLabel - // - _selectedUserIdLabel = new Label("") - { - MarginTop = 15, - MarginStart = 15, - }; - - // - // _selectedUserButtonsBox - // - _selectedUserButtonsBox = new Box(Orientation.Vertical, 0) - { - MarginEnd = 30, - }; - - // - // _saveProfileNameButton - // - _saveProfileNameButton = new Button() - { - Label = "Save Profile Name", - CanFocus = true, - ReceivesDefault = true, - Sensitive = false, - }; - _saveProfileNameButton.Clicked += EditProfileNameButton_Pressed; - - // - // _changeProfileImageButton - // - _changeProfileImageButton = new Button() - { - Label = "Change Profile Image", - CanFocus = true, - ReceivesDefault = true, - MarginTop = 10, - }; - _changeProfileImageButton.Clicked += ChangeProfileImageButton_Pressed; - - // - // _availableUsersLabel - // - _availableUsersLabel = new Label("Available User Profiles:") - { - Margin = 15, - Attributes = new AttrList(), - Halign = Align.Start, - }; - _availableUsersLabel.Attributes.Insert(new Pango.AttrWeight(Weight.Bold)); - - // - // _usersTreeViewWindow - // - _usersTreeViewWindow = new ScrolledWindow() - { - ShadowType = ShadowType.In, - CanFocus = true, - Expand = true, - MarginStart = 30, - MarginEnd = 30, - MarginBottom = 15, - }; - - // - // _tableStore - // - _tableStore = new ListStore(typeof(bool), typeof(Gdk.Pixbuf), typeof(string), typeof(Gdk.RGBA)); - - // - // _usersTreeView - // - _usersTreeView = new TreeView(_tableStore) - { - HoverSelection = true, - HeadersVisible = false, - }; - _usersTreeView.RowActivated += UsersTreeView_Activated; - - // - // _bottomBox - // - _bottomBox = new Box(Orientation.Horizontal, 0) - { - MarginStart = 30, - MarginEnd = 30, - MarginBottom = 15, - }; - - // - // _addButton - // - _addButton = new Button() - { - Label = "Add New Profile", - CanFocus = true, - ReceivesDefault = true, - HeightRequest = 35, - }; - _addButton.Clicked += AddButton_Pressed; - - // - // _deleteButton - // - _deleteButton = new Button() - { - Label = "Delete Selected Profile", - CanFocus = true, - ReceivesDefault = true, - HeightRequest = 35, - MarginStart = 10, - }; - _deleteButton.Clicked += DeleteButton_Pressed; - - // - // _closeButton - // - _closeButton = new Button() - { - Label = "Close", - CanFocus = true, - ReceivesDefault = true, - HeightRequest = 35, - WidthRequest = 80, - }; - _closeButton.Clicked += CloseButton_Pressed; - - ShowComponent(); - } - - private void ShowComponent() - { - _usersTreeViewWindow.Add(_usersTreeView); - - _usersTreeViewBox.Add(_usersTreeViewWindow); - _bottomBox.PackStart(_addButton, false, false, 0); - _bottomBox.PackStart(_deleteButton, false, false, 0); - _bottomBox.PackEnd(_closeButton, false, false, 0); - - _selectedUserInfoBox.Add(_selectedUserNameEntry); - _selectedUserInfoBox.Add(_selectedUserIdLabel); - - _selectedUserButtonsBox.Add(_saveProfileNameButton); - _selectedUserButtonsBox.Add(_changeProfileImageButton); - - _selectedUserBox.Add(_selectedUserImage); - _selectedUserBox.PackStart(_selectedUserInfoBox, false, false, 0); - _selectedUserBox.PackEnd(_selectedUserButtonsBox, false, false, 0); - - _mainBox.PackStart(_selectedLabel, false, false, 0); - _mainBox.PackStart(_selectedUserBox, false, true, 0); - _mainBox.PackStart(_availableUsersLabel, false, false, 0); - _mainBox.Add(_usersTreeViewBox); - _mainBox.Add(_bottomBox); - - Add(_mainBox); - - ShowAll(); - } - } -} diff --git a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs deleted file mode 100644 index 77afc5d1f..000000000 --- a/src/Ryujinx.Gtk3/UI/Windows/UserProfilesManagerWindow.cs +++ /dev/null @@ -1,326 +0,0 @@ -using Gtk; -using Ryujinx.Common.Memory; -using Ryujinx.HLE.FileSystem; -using Ryujinx.HLE.HOS.Services.Account.Acc; -using Ryujinx.UI.Common.Configuration; -using Ryujinx.UI.Widgets; -using SkiaSharp; -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Threading; -using System.Threading.Tasks; - -namespace Ryujinx.UI.Windows -{ - public partial class UserProfilesManagerWindow : Window - { - private const uint MaxProfileNameLength = 0x20; - - private readonly AccountManager _accountManager; - private readonly ContentManager _contentManager; - - private byte[] _bufferImageProfile; - private string _tempNewProfileName; - - private Gdk.RGBA _selectedColor; - - private readonly ManualResetEvent _avatarsPreloadingEvent = new(false); - - public UserProfilesManagerWindow(AccountManager accountManager, ContentManager contentManager, VirtualFileSystem virtualFileSystem) : base($"Ryujinx {Program.Version} - Manage User Profiles") - { - Icon = new Gdk.Pixbuf(Assembly.GetAssembly(typeof(ConfigurationState)), "Ryujinx.UI.Common.Resources.Logo_Ryujinx.png"); - - InitializeComponent(); - - _selectedColor.Red = 0.212; - _selectedColor.Green = 0.843; - _selectedColor.Blue = 0.718; - _selectedColor.Alpha = 1; - - _accountManager = accountManager; - _contentManager = contentManager; - - CellRendererToggle userSelectedToggle = new(); - userSelectedToggle.Toggled += UserSelectedToggle_Toggled; - - // NOTE: Uncomment following line when multiple selection of user profiles is supported. - //_usersTreeView.AppendColumn("Selected", userSelectedToggle, "active", 0); - _usersTreeView.AppendColumn("User Icon", new CellRendererPixbuf(), "pixbuf", 1); - _usersTreeView.AppendColumn("User Info", new CellRendererText(), "text", 2, "background-rgba", 3); - - _tableStore.SetSortColumnId(0, SortType.Descending); - - RefreshList(); - - if (_contentManager.GetCurrentFirmwareVersion() != null) - { - Task.Run(() => - { - AvatarWindow.PreloadAvatars(contentManager, virtualFileSystem); - _avatarsPreloadingEvent.Set(); - }); - } - } - - public void RefreshList() - { - _tableStore.Clear(); - - foreach (UserProfile userProfile in _accountManager.GetAllUsers()) - { - _tableStore.AppendValues(userProfile.AccountState == AccountState.Open, new Gdk.Pixbuf(userProfile.Image, 96, 96), $"{userProfile.Name}\n{userProfile.UserId}", Gdk.RGBA.Zero); - - if (userProfile.AccountState == AccountState.Open) - { - _selectedUserImage.Pixbuf = new Gdk.Pixbuf(userProfile.Image, 96, 96); - _selectedUserIdLabel.Text = userProfile.UserId.ToString(); - _selectedUserNameEntry.Text = userProfile.Name; - - _deleteButton.Sensitive = userProfile.UserId != AccountManager.DefaultUserId; - - _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); - _tableStore.SetValue(firstIter, 3, _selectedColor); - } - } - } - - // - // Events - // - - private void UsersTreeView_Activated(object o, RowActivatedArgs args) - { - SelectUserTreeView(); - } - - private void UserSelectedToggle_Toggled(object o, ToggledArgs args) - { - SelectUserTreeView(); - } - - private void SelectUserTreeView() - { - // Get selected item informations. - _usersTreeView.Selection.GetSelected(out TreeIter selectedIter); - - Gdk.Pixbuf userPicture = (Gdk.Pixbuf)_tableStore.GetValue(selectedIter, 1); - - string userName = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[0]; - string userId = _tableStore.GetValue(selectedIter, 2).ToString().Split("\n")[1]; - - // Unselect the first user. - _usersTreeView.Model.GetIterFirst(out TreeIter firstIter); - _tableStore.SetValue(firstIter, 0, false); - _tableStore.SetValue(firstIter, 3, Gdk.RGBA.Zero); - - // Set new informations. - _tableStore.SetValue(selectedIter, 0, true); - - _selectedUserImage.Pixbuf = userPicture; - _selectedUserNameEntry.Text = userName; - _selectedUserIdLabel.Text = userId; - _saveProfileNameButton.Sensitive = false; - - // Open the selected one. - _accountManager.OpenUser(new UserId(userId)); - - _deleteButton.Sensitive = userId != AccountManager.DefaultUserId.ToString(); - - _tableStore.SetValue(selectedIter, 3, _selectedColor); - } - - private void SelectedUserNameEntry_KeyReleaseEvent(object o, KeyReleaseEventArgs args) - { - if (_saveProfileNameButton.Sensitive == false) - { - _saveProfileNameButton.Sensitive = true; - } - } - - private void AddButton_Pressed(object sender, EventArgs e) - { - _tempNewProfileName = GtkDialog.CreateInputDialog(this, "Choose the Profile Name", "Please Enter a Profile Name", MaxProfileNameLength); - - if (_tempNewProfileName != "") - { - SelectProfileImage(true); - - if (_bufferImageProfile != null) - { - AddUser(); - } - } - } - - private void DeleteButton_Pressed(object sender, EventArgs e) - { - if (GtkDialog.CreateChoiceDialog("Delete User Profile", "Are you sure you want to delete the profile ?", "Deleting this profile will also delete all associated save data.")) - { - _accountManager.DeleteUser(GetSelectedUserId()); - - RefreshList(); - } - } - - private void EditProfileNameButton_Pressed(object sender, EventArgs e) - { - _saveProfileNameButton.Sensitive = false; - - _accountManager.SetUserName(GetSelectedUserId(), _selectedUserNameEntry.Text); - - RefreshList(); - } - - private void ProcessProfileImage(byte[] buffer) - { - using var image = SKBitmap.Decode(buffer); - - image.Resize(new SKImageInfo(256, 256), SKFilterQuality.High); - - using MemoryStream streamJpg = MemoryStreamManager.Shared.GetStream(); - - image.Encode(streamJpg, SKEncodedImageFormat.Jpeg, 80); - - _bufferImageProfile = streamJpg.ToArray(); - } - - private void ProfileImageFileChooser() - { - FileChooserNative fileChooser = new("Import Custom Profile Image", this, FileChooserAction.Open, "Import", "Cancel") - { - SelectMultiple = false, - }; - - FileFilter filter = new() - { - Name = "Custom Profile Images", - }; - filter.AddPattern("*.jpg"); - filter.AddPattern("*.jpeg"); - filter.AddPattern("*.png"); - filter.AddPattern("*.bmp"); - - fileChooser.AddFilter(filter); - - if (fileChooser.Run() == (int)ResponseType.Accept) - { - ProcessProfileImage(File.ReadAllBytes(fileChooser.Filename)); - } - - fileChooser.Dispose(); - } - - private void SelectProfileImage(bool newUser = false) - { - if (_contentManager.GetCurrentFirmwareVersion() == null) - { - ProfileImageFileChooser(); - } - else - { - Dictionary buttons = new() - { - { 0, "Import Image File" }, - { 1, "Select Firmware Avatar" }, - }; - - ResponseType responseDialog = GtkDialog.CreateCustomDialog("Profile Image Selection", - "Choose a Profile Image", - "You may import a custom profile image, or select an avatar from the system firmware.", - buttons, MessageType.Question); - - if (responseDialog == 0) - { - ProfileImageFileChooser(); - } - else if (responseDialog == (ResponseType)1) - { - AvatarWindow avatarWindow = new() - { - NewUser = newUser, - }; - - avatarWindow.DeleteEvent += AvatarWindow_DeleteEvent; - - avatarWindow.SetSizeRequest((int)(avatarWindow.DefaultWidth * Program.WindowScaleFactor), (int)(avatarWindow.DefaultHeight * Program.WindowScaleFactor)); - avatarWindow.Show(); - } - } - } - - private void ChangeProfileImageButton_Pressed(object sender, EventArgs e) - { - if (_contentManager.GetCurrentFirmwareVersion() != null) - { - _avatarsPreloadingEvent.WaitOne(); - } - - SelectProfileImage(); - - if (_bufferImageProfile != null) - { - SetUserImage(); - } - } - - private void AvatarWindow_DeleteEvent(object sender, DeleteEventArgs args) - { - _bufferImageProfile = ((AvatarWindow)sender).SelectedProfileImage; - - if (_bufferImageProfile != null) - { - if (((AvatarWindow)sender).NewUser) - { - AddUser(); - } - else - { - SetUserImage(); - } - } - } - - private void AddUser() - { - _accountManager.AddUser(_tempNewProfileName, _bufferImageProfile); - - _bufferImageProfile = null; - _tempNewProfileName = ""; - - RefreshList(); - } - - private void SetUserImage() - { - _accountManager.SetUserImage(GetSelectedUserId(), _bufferImageProfile); - - _bufferImageProfile = null; - - RefreshList(); - } - - private UserId GetSelectedUserId() - { - if (_usersTreeView.Model.GetIterFirst(out TreeIter iter)) - { - do - { - if ((bool)_tableStore.GetValue(iter, 0)) - { - break; - } - } - while (_usersTreeView.Model.IterNext(ref iter)); - } - - return new UserId(_tableStore.GetValue(iter, 2).ToString().Split("\n")[1]); - } - - private void CloseButton_Pressed(object sender, EventArgs e) - { - Close(); - } - } -}