bug fixes + missing feats from !125 (#149)

addresses two crashes (https://github.com/Ryubing/Issues/issues/455 and another one pointed out over on discord),
code cleanup,
various fixes and improvements :
- fix assigned devices menu player display section being too large when under 4 players were selected
- fix 'profile : ...' shifting around in the UI when enabling / disabling the checkbox
- update the Dynamic Input Swap warning message to show which device is assigned to which player
- make players that have Dynamic Input Swap disabled show up in the Device assignement menu.

Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/149
This commit is contained in:
Babib3l
2026-06-29 04:00:30 +00:00
committed by sh0inx
parent 45c66eaf66
commit 8b7d27a071
5 changed files with 69 additions and 47 deletions

View File

@@ -6206,9 +6206,9 @@
"ar_SA": "", "ar_SA": "",
"de_DE": "", "de_DE": "",
"el_GR": "", "el_GR": "",
"en_US": "You can assign devices to specific players by clicking the gear icon next to each input device.", "en_US": "All currently connected input devices have been assigned to the following Players:\n\n{0}\n\nYou can assign devices to specific players by clicking the gear icon next to each input device.",
"es_ES": "Puedes asignar dispositivos a jugadores específicos haciendo clic en el icono de engranaje junto a cada dispositivo de entrada.", "es_ES": "Todos los dispositivos de entrada conectados actualmente se han asignado a los siguientes jugadores:\n\n{0}\n\nPuedes asignar dispositivos a jugadores específicos haciendo clic en el icono de engranaje junto a cada dispositivo de entrada.",
"fr_FR": "Vous pouvez assigner des périphériques à des joueurs spécifiques en cliquant sur l'icône d'engrenage à côté de chaque périphérique d'entrée.", "fr_FR": "Tous les périphériques d'entrée actuellement connectés ont été assignés aux joueurs suivants :\n\n{0}\n\nVous pouvez assigner des périphériques à des joueurs spécifiques en cliquant sur l'icône d'engrenage à côté de chaque périphérique d'entrée.",
"he_IL": "", "he_IL": "",
"it_IT": "", "it_IT": "",
"ja_JP": "", "ja_JP": "",

View File

@@ -417,8 +417,8 @@ namespace Ryujinx.Input.HLE
bool motionWasDisabled = oldConfig?.Motion == null; bool motionWasDisabled = oldConfig?.Motion == null;
bool leftMotionMissing = _leftMotionInput == null; bool leftMotionMissing = _leftMotionInput == null;
bool isJoyconPairNeedingRightMotion = newConfig.ControllerType == ConfigControllerType.JoyconPair && _rightMotionInput == null; bool isJoyconPairNeedingRightMotion = newConfig.ControllerType == ConfigControllerType.JoyconPair && _rightMotionInput == null;
bool motionEnabledChanged = oldConfig.Motion.EnableMotion != newConfig.Motion.EnableMotion; bool motionEnabledChanged = !motionWasDisabled && oldConfig?.Motion?.EnableMotion != newConfig.Motion.EnableMotion;
bool motionBackendChanged = oldConfig.Motion.MotionBackend != newConfig.Motion.MotionBackend; bool motionBackendChanged = !motionWasDisabled && oldConfig?.Motion?.MotionBackend != newConfig.Motion.MotionBackend;
return motionWasDisabled || return motionWasDisabled ||
leftMotionMissing || leftMotionMissing ||
@@ -769,7 +769,7 @@ namespace Ryujinx.Input.HLE
{ {
foreach (string gamepadId in gamepadDriver.GamepadsIds) foreach (string gamepadId in gamepadDriver.GamepadsIds)
{ {
if (gamepadId == assignedController.Id) if (string.Equals(gamepadId, assignedController.Id, StringComparison.Ordinal))
{ {
yield return assignedController; yield return assignedController;
break; break;
@@ -784,7 +784,7 @@ namespace Ryujinx.Input.HLE
{ {
foreach (string gamepadId in gamepadDriver.GamepadsIds) foreach (string gamepadId in gamepadDriver.GamepadsIds)
{ {
if (gamepadId == config.Id) if (string.Equals(gamepadId, config.Id, StringComparison.Ordinal))
{ {
yield return new AssignedInputDevice yield return new AssignedInputDevice
{ {

View File

@@ -109,6 +109,11 @@ namespace Ryujinx.Input.HLE
private void HandleOnGamepadConnected(string id) private void HandleOnGamepadConnected(string id)
{ {
List<InputConfig> requestedInputConfig;
List<PlayerInputAssignment> playerInputAssignments;
bool enableKeyboard;
bool enableMouse;
lock (_lock) lock (_lock)
{ {
for (int i = 0; i < _controllers.Length; i++) for (int i = 0; i < _controllers.Length; i++)
@@ -119,10 +124,15 @@ namespace Ryujinx.Input.HLE
_controllers[i] = null; _controllers[i] = null;
} }
} }
requestedInputConfig = _requestedInputConfig;
playerInputAssignments = _playerInputAssignments;
enableKeyboard = _enableKeyboard;
enableMouse = _enableMouse;
} }
// Force input reload // Force input reload
ReloadConfiguration(_requestedInputConfig, _playerInputAssignments, _enableKeyboard, _enableMouse); ReloadConfiguration(requestedInputConfig, playerInputAssignments, enableKeyboard, enableMouse);
} }
private bool PlayerHasAssignedControllerId(PlayerIndex playerIndex, string id) private bool PlayerHasAssignedControllerId(PlayerIndex playerIndex, string id)

View File

@@ -207,7 +207,9 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
private async void ShowDynamicInputSwapFirstUseWarning() private async void ShowDynamicInputSwapFirstUseWarning()
{ {
string message = LocaleManager.Instance[LocaleKeys.DialogDynamicInputSwapDeviceAssignmentsHint]; string message = LocaleManager.Instance.UpdateAndGetDynamicValue(
LocaleKeys.DialogDynamicInputSwapDeviceAssignmentsHint,
BuildDynamicInputSwapFirstUseAssignmentSummary());
CheckBoxDialogResult result = await ContentDialogHelper.CreateCheckBoxDialog( CheckBoxDialogResult result = await ContentDialogHelper.CreateCheckBoxDialog(
LocaleManager.Instance[LocaleKeys.ControllerSettingsAssignedInputDevices], LocaleManager.Instance[LocaleKeys.ControllerSettingsAssignedInputDevices],
@@ -221,6 +223,15 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
} }
} }
private string BuildDynamicInputSwapFirstUseAssignmentSummary()
{
return string.Join(
Environment.NewLine,
PlayerInputDevices
.Where(device => device.HasAssignedToPlayers)
.Select(device => $"{device.Name} - {device.AssignedToPlayers}"));
}
public bool AllowDuplicateDeviceAssignment public bool AllowDuplicateDeviceAssignment
{ {
get => _allowDuplicateDeviceAssignment ?? GetSavedAllowDuplicateDeviceAssignment(); get => _allowDuplicateDeviceAssignment ?? GetSavedAllowDuplicateDeviceAssignment();
@@ -853,15 +864,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
{ {
PlayerInputAssignment normalizedOtherAssignment = GetWorkingPlayerInputAssignment(otherPlayer); PlayerInputAssignment normalizedOtherAssignment = GetWorkingPlayerInputAssignment(otherPlayer);
// Only include players who participate in dynamic input swap.
// Players with dynamic swap disabled manage their device through
// the traditional InputConfig and should not appear in the
// Assigned Devices menu for other players.
if (!normalizedOtherAssignment.EnableDynamicInputSwap)
{
continue;
}
string playerName = GetPlayerDisplayName(otherPlayer); string playerName = GetPlayerDisplayName(otherPlayer);
foreach (AssignedInputDevice device in normalizedOtherAssignment.Devices) foreach (AssignedInputDevice device in normalizedOtherAssignment.Devices)
@@ -940,11 +942,6 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
string currentPlayerName = GetPlayerDisplayName(_playerId); string currentPlayerName = GetPlayerDisplayName(_playerId);
if (!AllowDuplicateDeviceAssignment)
{
return currentPlayerName;
}
return assignedOtherPlayers != null && assignedOtherPlayers.Count > 0 return assignedOtherPlayers != null && assignedOtherPlayers.Count > 0
? $"{currentPlayerName}, {string.Join(", ", assignedOtherPlayers.OrderBy(name => ExtractPlayerNumber(name)))}" ? $"{currentPlayerName}, {string.Join(", ", assignedOtherPlayers.OrderBy(name => ExtractPlayerNumber(name)))}"
: currentPlayerName; : currentPlayerName;

View File

@@ -19,59 +19,74 @@
BorderThickness="1" BorderThickness="1"
CornerRadius="5" CornerRadius="5"
HorizontalAlignment="Stretch"> HorizontalAlignment="Stretch">
<ItemsControl <ItemsControl ItemsSource="{Binding PlayerInputDevices}">
ItemsSource="{Binding PlayerInputDevices}"> <ItemsControl.Styles>
<Style Selector="ContentPresenter">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ItemsControl.Styles>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel HorizontalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <ItemsControl.ItemTemplate>
<DataTemplate DataType="models:PlayerInputDeviceAssignmentItem"> <DataTemplate DataType="models:PlayerInputDeviceAssignmentItem">
<Border <Border
Margin="0,3" Margin="0,3"
Padding="10,8" Padding="10,8"
CornerRadius="4" CornerRadius="4"
HorizontalAlignment="Stretch"
Background="{DynamicResource ControlFillColorTertiary}" Background="{DynamicResource ControlFillColorTertiary}"
BorderBrush="{DynamicResource ThemeControlBorderColor}" BorderBrush="{DynamicResource ThemeControlBorderColor}"
BorderThickness="1" BorderThickness="1"
PointerPressed="DeviceRow_OnPointerPressed" PointerPressed="DeviceRow_OnPointerPressed"
Cursor="Hand" Cursor="Hand"
IsEnabled="{Binding !IsDisabledByOtherPlayer}"> IsEnabled="{Binding !IsDisabledByOtherPlayer}">
<Grid ColumnDefinitions="Auto,*,200"> <DockPanel
HorizontalAlignment="Stretch"
LastChildFill="True">
<CheckBox <CheckBox
Grid.Column="0" DockPanel.Dock="Left"
Margin="0,0,8,0"
Padding="0"
VerticalAlignment="Center"
ClipToBounds="False"
IsChecked="{Binding IsAssigned}" IsChecked="{Binding IsAssigned}"
Checked="AssignedDeviceCheckBox_OnCheckedChanged" Checked="AssignedDeviceCheckBox_OnCheckedChanged"
Unchecked="AssignedDeviceCheckBox_OnCheckedChanged" Unchecked="AssignedDeviceCheckBox_OnCheckedChanged" />
<TextBlock
DockPanel.Dock="Right"
Margin="10,0,0,0"
Opacity="0.6"
FontSize="12"
MaxWidth="200"
VerticalAlignment="Center" VerticalAlignment="Center"
Margin="0" HorizontalAlignment="Right"
Padding="0" /> TextWrapping="Wrap"
Text="{Binding AssignedToPlayers}"
IsVisible="{Binding HasAssignedToPlayers}" />
<StackPanel <StackPanel
Grid.Column="1" HorizontalAlignment="Left"
Margin="8,0,0,0"
VerticalAlignment="Center"> VerticalAlignment="Center">
<TextBlock Text="{Binding Name}" />
<TextBlock <TextBlock
HorizontalAlignment="Left"
Text="{Binding Name}" />
<TextBlock
HorizontalAlignment="Left"
Opacity="0.6" Opacity="0.6"
FontSize="12" FontSize="12"
MaxWidth="160"
TextTrimming="CharacterEllipsis" TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" TextWrapping="NoWrap"
MaxLines="1" MaxLines="1"
IsVisible="{Binding HasBoundProfileName}"> IsVisible="{Binding HasBoundProfileName}">
<Run Text="{ext:Locale ControllerSettingsProfile}" /> <Run Text="{ext:Locale ControllerSettingsProfile}" />
<Run Text=":" /> <Run Text=": " />
<Run Text="{Binding BoundProfileName}" /> <Run Text="{Binding BoundProfileName}" />
</TextBlock> </TextBlock>
</StackPanel> </StackPanel>
<TextBlock </DockPanel>
Grid.Column="2"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Margin="10,0,0,0"
Opacity="0.6"
FontSize="12"
Text="{Binding AssignedToPlayers}"
TextWrapping="Wrap"
MaxWidth="200"
IsVisible="{Binding HasAssignedToPlayers}" />
</Grid>
</Border> </Border>
</DataTemplate> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl.ItemTemplate>