Compare commits

...

8 commits

Author SHA1 Message Date
Jean-Philippe HAUTIN
55c9b373fe
Merge 6d38a5bfc1 into 23b0b22400 2024-12-22 23:18:07 +01:00
Evan Husted
23b0b22400 UI: Ensure last played date & time are always on 2 separate lines, for consistency.
Some checks are pending
Canary release job / Create tag (push) Waiting to run
Canary release job / Release for linux-arm64 (push) Waiting to run
Canary release job / Release for linux-x64 (push) Waiting to run
Canary release job / Release for win-x64 (push) Waiting to run
Canary release job / Release MacOS universal (push) Waiting to run
2024-12-22 16:08:12 -06:00
Evan Husted
3dfbf55611 Merge remote-tracking branch 'origin/master' 2024-12-22 16:01:19 -06:00
Evan Husted
cb355f504d UI: Rearrange help menu item & merge wiki page link buttons into a "category" button. 2024-12-22 16:01:09 -06:00
Marco Carvalho
b5483d8fe0
Prefer generic overload when type is known (#430) 2024-12-22 13:23:35 -06:00
Evan Husted
8259f790d7 misc: Cleanup locale validator 2024-12-22 13:19:10 -06:00
Evan Husted
1ea345faa7 UI: Move Match PC Time to next to the time selector & change label & tooltip to clarify behavior further.
Some checks are pending
Canary release job / Create tag (push) Waiting to run
Canary release job / Release for linux-arm64 (push) Waiting to run
Canary release job / Release for linux-x64 (push) Waiting to run
Canary release job / Release for win-x64 (push) Waiting to run
Canary release job / Release MacOS universal (push) Waiting to run
2024-12-22 12:53:48 -06:00
Jean-Philippe HAUTIN
6d38a5bfc1 ensure gamepads connect automatically to firs player without controller 2024-12-19 21:07:36 +01:00
18 changed files with 182 additions and 66 deletions

View file

@ -14,20 +14,20 @@ namespace Ryujinx.BuildValidationTasks
{
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
if (path.Split(new string[] { "src" }, StringSplitOptions.None).Length == 1 )
if (path.Split(["src"], StringSplitOptions.None).Length == 1)
{
//i assume that we are in a build directory in the solution dir
path = new FileInfo(path).Directory.Parent.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
path = new FileInfo(path).Directory!.Parent!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
}
else
{
path = path.Split(new string[] { "src" }, StringSplitOptions.None)[0];
path = new FileInfo(path).Directory.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
path = path.Split(["src"], StringSplitOptions.None)[0];
path = new FileInfo(path).Directory!.GetDirectories("src")[0].GetDirectories("Ryujinx")[0].GetDirectories("Assets")[0].GetFiles("locales.json")[0].FullName;
}
string data;
using (StreamReader sr = new StreamReader(path))
using (StreamReader sr = new(path))
{
data = sr.ReadToEnd();
}
@ -38,13 +38,10 @@ namespace Ryujinx.BuildValidationTasks
{
LocalesEntry locale = json.Locales[i];
foreach (string language in json.Languages)
foreach (string langCode in json.Languages.Where(it => !locale.Translations.ContainsKey(it)))
{
if (!locale.Translations.ContainsKey(language))
{
locale.Translations.Add(language, "");
Log.LogMessage(MessageImportance.High, $"Added {{{language}}} to Locale {{{locale.ID}}}");
}
locale.Translations.Add(langCode, string.Empty);
Log.LogMessage(MessageImportance.High, $"Added '{langCode}' to Locale '{locale.ID}'");
}
locale.Translations = locale.Translations.OrderBy(pair => pair.Key).ToDictionary(pair => pair.Key, pair => pair.Value);
@ -53,7 +50,7 @@ namespace Ryujinx.BuildValidationTasks
string jsonString = JsonConvert.SerializeObject(json, Formatting.Indented);
using (StreamWriter sw = new StreamWriter(path))
using (StreamWriter sw = new(path))
{
sw.Write(jsonString);
}

View file

@ -62,7 +62,7 @@ namespace Ryujinx.Graphics.Vulkan
_api = api;
_physicalDevice = physicalDevice;
int totalFormats = Enum.GetNames(typeof(Format)).Length;
int totalFormats = Enum.GetNames<Format>().Length;
_bufferTable = new FormatFeatureFlags[totalFormats];
_optimalTable = new FormatFeatureFlags[totalFormats];

View file

@ -12,7 +12,7 @@ namespace Ryujinx.Graphics.Vulkan
static FormatTable()
{
_table = new VkFormat[Enum.GetNames(typeof(Format)).Length];
_table = new VkFormat[Enum.GetNames<Format>().Length];
_reverseMap = new Dictionary<VkFormat, Format>();
#pragma warning disable IDE0055 // Disable formatting

View file

@ -13,7 +13,7 @@ namespace Ryujinx.Graphics.Vulkan.Queries
{
_pipeline = pipeline;
int count = Enum.GetNames(typeof(CounterType)).Length;
int count = Enum.GetNames<CounterType>().Length;
_counterQueues = new CounterQueue[count];

View file

@ -23,18 +23,18 @@ namespace Ryujinx.HLE.HOS.Services
public IpcService(ServerBase server = null)
{
CmifCommands = typeof(IpcService).Assembly.GetTypes()
CmifCommands = GetType().Assembly.GetTypes()
.Where(type => type == GetType())
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
.SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandCmifAttribute))
.Select(command => (((CommandCmifAttribute)command).Id, methodInfo)))
.SelectMany(methodInfo => methodInfo.GetCustomAttributes<CommandCmifAttribute>()
.Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo);
TipcCommands = typeof(IpcService).Assembly.GetTypes()
TipcCommands = GetType().Assembly.GetTypes()
.Where(type => type == GetType())
.SelectMany(type => type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public))
.SelectMany(methodInfo => methodInfo.GetCustomAttributes(typeof(CommandTipcAttribute))
.Select(command => (((CommandTipcAttribute)command).Id, methodInfo)))
.SelectMany(methodInfo => methodInfo.GetCustomAttributes<CommandTipcAttribute>()
.Select(command => (command.Id, methodInfo)))
.ToDictionary(command => command.Id, command => command.methodInfo);
Server = server;

View file

@ -444,7 +444,7 @@ namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
private ResultCode ScanInternal(IVirtualMemoryManager memory, ushort channel, ScanFilter scanFilter, ulong bufferPosition, ulong bufferSize, out ulong counter)
{
ulong networkInfoSize = (ulong)Marshal.SizeOf(typeof(NetworkInfo));
ulong networkInfoSize = (ulong)Marshal.SizeOf<NetworkInfo>();
ulong maxGames = bufferSize / networkInfoSize;
MemoryHelper.FillWithZeros(memory, bufferPosition, (int)bufferSize);

View file

@ -94,7 +94,7 @@ namespace Ryujinx.HLE.HOS.Services.Sm
{
if (_services.TryGetValue(name, out Type type))
{
ServiceAttribute serviceAttribute = (ServiceAttribute)type.GetCustomAttributes(typeof(ServiceAttribute)).First(service => ((ServiceAttribute)service).Name == name);
ServiceAttribute serviceAttribute = type.GetCustomAttributes<ServiceAttribute>().First(service => service.Name == name);
IpcService service = GetServiceInstance(type, context, serviceAttribute.Parameter);

View file

@ -426,6 +426,8 @@ namespace Ryujinx.Headless.SDL2
return;
}
_inputManager.AddUpdaterForConfiguration(_inputConfiguration);
// Setup logging level
Logger.SetEnable(LogLevel.Debug, option.LoggingEnableDebug);
Logger.SetEnable(LogLevel.Stub, !option.LoggingDisableStub);
@ -482,7 +484,7 @@ namespace Ryujinx.Headless.SDL2
}
_inputManager.Dispose();
}
}
private static void SetupProgressHandler()
{

View file

@ -34,14 +34,14 @@ namespace Ryujinx.Horizon.Kernel.Generators
private const string TypeResult = NamespaceHorizonCommon + "." + TypeResultName;
private const string TypeExecutionContext = "IExecutionContext";
private static readonly string[] _expectedResults = new string[]
{
private static readonly string[] _expectedResults =
[
$"{TypeResultName}.Success",
$"{TypeKernelResultName}.TimedOut",
$"{TypeKernelResultName}.Cancelled",
$"{TypeKernelResultName}.PortRemoteClosed",
$"{TypeKernelResultName}.InvalidState",
};
];
private readonly struct OutParameter
{

View file

@ -1,4 +1,9 @@
using LibHac.Common;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
namespace Ryujinx.Input.HLE
{
@ -41,6 +46,76 @@ namespace Ryujinx.Input.HLE
}
}
private void removeSDPWhenExternalPadsConnected(List<Tuple<string,string>> availableDevices) {
//remove all steam virtual gamepads
availableDevices.RemoveAll(a => a.Item2 == "Steam Virtual Gamepad");
//remove Steam Deck Controller if external controllers are connected (docked game mode)
if (availableDevices.Count > 1) {
var steamDeckPad = availableDevices.FindFirst( a => a.Item2 == "Steam Deck Controller");
if (steamDeckPad.HasValue) {
availableDevices.Remove(steamDeckPad.Value);
}
}
}
private List<Tuple<string,string>> getGamepadsDescriptions() {
var result = new List<Tuple<string,string>> ();
foreach (string id in GamepadDriver.GamepadsIds) {
result.Add(Tuple.Create(id,GamepadDriver.GetGamepad(id).Name));
}
return result;
}
private void LinkDevicesToPlayers(List<InputConfig> _inputConfig) {
var _availableDevices = getGamepadsDescriptions();
removeSDPWhenExternalPadsConnected(_availableDevices);
var _playersWithNoDevices = new List<PlayerIndex>();
//Remove all used Devices in current Config and at the same time list player with missing Devices
foreach(PlayerIndex _playerId in Enum.GetValues(typeof(PlayerIndex)))
{
var _config = _inputConfig.Find(inputConfig => inputConfig.PlayerIndex == _playerId);
if (_config != null && _config.Backend != InputBackendType.WindowKeyboard)
{
//check device id of the player is in the existing/connected devices
var _connectedDevice = _availableDevices.FindFirst(d => d.Item1 == _config.Id);
if (_connectedDevice.HasValue)
{
_availableDevices.Remove(_connectedDevice.Value);
}
else
{
_playersWithNoDevices.Add(_playerId);
}
}
}
var hasChanges = _playersWithNoDevices.Count() > 0 && _availableDevices.Count() > 0;
if (hasChanges)
{
Logger.Info?.Print(LogClass.Configuration, $"Controllers configuration changed. Updating players configuration...");
for (int i = 0; i < _playersWithNoDevices.Count; i++)
{
var _playerId = _playersWithNoDevices[i];
var _config = _inputConfig.Find(inputConfig => inputConfig.PlayerIndex == _playerId);
if (_config != null && _availableDevices.Count > 0)
{
var _device = _availableDevices.First();
var deviceId = _device.Item1;
var deviceName = _device.Item2;
_config.Id = _device.Item1;
Logger.Info?.Print(LogClass.Configuration, $"Link Player {_playerId} to Device {deviceName}");
_availableDevices.Remove(_device);
}
}
Logger.Info?.Print(LogClass.Configuration, $"Updated players configuration to sync with Controllers configuration changes.");
}
}
public void AddUpdaterForConfiguration(List<InputConfig> _inputConfig) {
GamepadDriver.OnGamepadConnected += id => LinkDevicesToPlayers(_inputConfig);
GamepadDriver.OnGamepadDisconnected += id => LinkDevicesToPlayers(_inputConfig);
}
public void Dispose()
{
GC.SuppressFinalize(this);

View file

@ -38,7 +38,7 @@ namespace Ryujinx.UI.App.Common
public string TimePlayedString => ValueFormatUtils.FormatTimeSpan(TimePlayed);
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed) ?? LocalizedNever();
public string LastPlayedString => ValueFormatUtils.FormatDateTime(LastPlayed)?.Replace(" ", "\n") ?? LocalizedNever();
public string FileSizeString => ValueFormatUtils.FormatFileSize(FileSize);

View file

@ -1125,6 +1125,30 @@
"zh_TW": "檢查更新"
}
},
{
"ID": "MenuBarHelpFaqAndGuides",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "FAQ & Guides",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "MenuBarHelpFaq",
"Translations": {
@ -3771,7 +3795,7 @@
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Match PC Time",
"en_US": "Resync to PC Date & Time",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
@ -14571,7 +14595,7 @@
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Change System Time to match your PC's date & time.",
"en_US": "Resync System Time to match your PC's current date & time.\n\nThis is not an active setting, it can still fall out of sync; in which case just click this button again.",
"es_ES": "",
"fr_FR": "",
"he_IL": "",

View file

@ -97,7 +97,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{
if (IsModified)
{
_playerIdChoose = value;
return;
}
@ -105,7 +105,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
IsModified = false;
_playerId = value;
if (!Enum.IsDefined(typeof(PlayerIndex), _playerId))
if (!Enum.IsDefined<PlayerIndex>(_playerId))
{
_playerId = PlayerIndex.Player1;
@ -367,12 +367,12 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private void HandleOnGamepadDisconnected(string id)
{
Dispatcher.UIThread.Post(LoadDevices);
Dispatcher.UIThread.Post(RefreshDevicesAndCurrentPlayerConfiguration);
}
private void HandleOnGamepadConnected(string id)
{
Dispatcher.UIThread.Post(LoadDevices);
Dispatcher.UIThread.Post(RefreshDevicesAndCurrentPlayerConfiguration);
}
private string GetCurrentGamepadId()
@ -441,6 +441,21 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
return str[(str.IndexOf(Hyphen) + Offset)..];
}
public void RefreshDevicesAndCurrentPlayerConfiguration()
{
LoadDevices();
//update Device for current user based on new configuration.
var config = ConfigurationState.Instance.Hid.InputConfig.Value.Find(inputConfig => inputConfig.PlayerIndex == PlayerId);
var device = Devices.FindFirst(d => d.Id==config.Id);
if (device.HasValue) {
Device=Devices.IndexOf(device);
} else {
//0 is the None Device
Device = 0;
}
}
public void LoadDevices()
{
string GetGamepadName(IGamepad gamepad, int controllerNumber)
@ -464,17 +479,19 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
DeviceList.Clear();
Devices.Add((DeviceType.None, Disabled, LocaleManager.Instance[LocaleKeys.ControllerSettingsDeviceDisabled]));
int controllerNumber = 0;
int controllerNumber = 1;
foreach (string id in _mainWindow.InputManager.KeyboardDriver.GamepadsIds)
{
using IGamepad gamepad = _mainWindow.InputManager.KeyboardDriver.GetGamepad(id);
if (gamepad != null)
{
controllerNumber++;
Devices.Add((DeviceType.Keyboard, id, $"{GetShortGamepadName(gamepad.Name)}"));
}
}
controllerNumber = 1;
foreach (string id in _mainWindow.InputManager.GamepadDriver.GamepadsIds)
{
using IGamepad gamepad = _mainWindow.InputManager.GamepadDriver.GetGamepad(id);
@ -482,6 +499,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
if (gamepad != null)
{
string name = GetUniqueGamepadName(gamepad, ref controllerNumber);
controllerNumber++;
Devices.Add((DeviceType.Controller, id, name));
}
}

View file

@ -290,6 +290,11 @@
</MenuItem>
</MenuItem>
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelp}">
<MenuItem
Click="OpenAboutWindow"
Header="{ext:Locale MenuBarHelpAbout}"
Icon="{ext:Icon fa-solid fa-circle-info}"
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
<MenuItem
Name="UpdateMenuItem"
IsEnabled="{Binding CanUpdate}"
@ -298,30 +303,26 @@
Icon="{ext:Icon mdi-update}"
ToolTip.Tip="{ext:Locale CheckUpdatesTooltip}" />
<Separator />
<MenuItem
Click="MenuItem_OnClick"
Header="{ext:Locale MenuBarHelpFaq}"
Icon="{ext:Icon fa-github}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/FAQ-and-Troubleshooting"
ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" />
<MenuItem
Click="MenuItem_OnClick"
Header="{ext:Locale MenuBarHelpSetup}"
Icon="{ext:Icon fa-github}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/Ryujinx-Setup-&amp;-Configuration-Guide"
ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" />
<MenuItem
Click="MenuItem_OnClick"
Header="{ext:Locale MenuBarHelpMultiplayer}"
Icon="{ext:Icon fa-github}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/Multiplayer%E2%80%90(LDN%E2%80%90Local%E2%80%90Wireless)%E2%80%90Guide"
ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" />
<Separator />
<MenuItem
Click="OpenAboutWindow"
Header="{ext:Locale MenuBarHelpAbout}"
Icon="{ext:Icon fa-solid fa-circle-info}"
ToolTip.Tip="{ext:Locale OpenAboutTooltip}" />
<MenuItem VerticalAlignment="Center" Header="{ext:Locale MenuBarHelpFaqAndGuides}" Icon="{ext:Icon fa-solid fa-question}" >
<MenuItem
Click="MenuItem_OnClick"
Header="{ext:Locale MenuBarHelpFaq}"
Icon="{ext:Icon fa-github}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/FAQ-and-Troubleshooting"
ToolTip.Tip="{ext:Locale MenuBarHelpFaqTooltip}" />
<MenuItem
Click="MenuItem_OnClick"
Header="{ext:Locale MenuBarHelpSetup}"
Icon="{ext:Icon fa-github}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/Ryujinx-Setup-&amp;-Configuration-Guide"
ToolTip.Tip="{ext:Locale MenuBarHelpSetupTooltip}" />
<MenuItem
Click="MenuItem_OnClick"
Header="{ext:Locale MenuBarHelpMultiplayer}"
Icon="{ext:Icon fa-github}"
Tag="https://github.com/GreemDev/Ryujinx/wiki/Multiplayer%E2%80%90(LDN%E2%80%90Local%E2%80%90Wireless)%E2%80%90Guide"
ToolTip.Tip="{ext:Locale MenuBarHelpMultiplayerTooltip}" />
</MenuItem>
</MenuItem>
</Menu>
</DockPanel>

View file

@ -52,7 +52,7 @@ namespace Ryujinx.Ava.UI.Views.Main
private void AspectRatioStatus_OnClick(object sender, RoutedEventArgs e)
{
AspectRatio aspectRatio = ConfigurationState.Instance.Graphics.AspectRatio.Value;
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames(typeof(AspectRatio)).Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
ConfigurationState.Instance.Graphics.AspectRatio.Value = (int)aspectRatio + 1 > Enum.GetNames<AspectRatio>().Length - 1 ? AspectRatio.Fixed4x3 : aspectRatio + 1;
}
private void Refresh_OnClick(object sender, RoutedEventArgs e) => Window.LoadApplications();

View file

@ -181,15 +181,11 @@
SelectedTime="{Binding CurrentTime}"
Width="350"
ToolTip.Tip="{ext:Locale TimeTooltip}" />
</StackPanel>
<StackPanel
Margin="350,0,0,10"
Orientation="Horizontal">
<Button
Margin="10, 0, 0, 0"
VerticalAlignment="Center"
Click="MatchSystemTime_OnClick"
Background="{DynamicResource SystemAccentColor}"
Width="150"
ToolTip.Tip="{ext:Locale MatchTimeTooltip}">
<TextBlock Text="{ext:Locale SettingsTabSystemSystemTimeMatch}" />
</Button>

View file

@ -1,7 +1,7 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Ryujinx.Ava.UI.ViewModels;
using System;
using TimeZone = Ryujinx.Ava.UI.Models.TimeZone;
namespace Ryujinx.Ava.UI.Views.Settings

View file

@ -16,6 +16,7 @@ using Ryujinx.Ava.UI.Applet;
using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Common;
using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.FileSystem;
@ -103,6 +104,8 @@ namespace Ryujinx.Ava.UI.Windows
if (Program.PreviewerDetached)
{
InputManager = new InputManager(new AvaloniaKeyboardDriver(this), new SDL2GamepadDriver());
InputManager.AddUpdaterForConfiguration(ConfigurationState.Instance.Hid.InputConfig);
_ = this.GetObservable(IsActiveProperty).Subscribe(it => ViewModel.IsActive = it);
this.ScalingChanged += OnScalingChanged;