diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
index 4ca21e788..83dcc7882 100644
--- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
@@ -32,6 +32,7 @@ using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
+using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Helper;
using Ryujinx.Common.Logging;
using Ryujinx.Common.UI;
@@ -1690,11 +1691,37 @@ namespace Ryujinx.Ava.UI.ViewModels
SetMainContent(RendererHostControl);
RendererHostControl.Focus();
+
+ // Show controller overlay
+ ShowControllerOverlay();
});
public static void UpdateGameMetadata(string titleId, TimeSpan playTime)
=> ApplicationLibrary.LoadAndSaveMetaData(titleId, appMetadata => appMetadata.UpdatePostGame(playTime));
+ private void ShowControllerOverlay()
+ {
+ try
+ {
+ var inputConfigs = ConfigurationState.Instance.System.UseInputGlobalConfig.Value && Program.UseExtraConfig
+ ? ConfigurationState.InstanceExtra.Hid.InputConfig.Value
+ : ConfigurationState.Instance.Hid.InputConfig.Value;
+
+ // Only show overlay if there are actual controller configurations for players 1-4
+ if (inputConfigs?.Any(c => c.PlayerIndex <= PlayerIndex.Player4) == true)
+ {
+ var overlay = new Windows.ControllerOverlayWindow(Window);
+ overlay.ShowControllerBindings(inputConfigs);
+ overlay.Show();
+ }
+ }
+ catch (Exception ex)
+ {
+ // Log the error but don't let it crash the game launch
+ Logger.Error?.Print(LogClass.UI, $"Failed to show controller overlay: {ex.Message}");
+ }
+ }
+
public void RefreshFirmwareStatus()
{
SystemVersion version = null;
diff --git a/src/Ryujinx/UI/Windows/ControllerOverlayWindow.axaml b/src/Ryujinx/UI/Windows/ControllerOverlayWindow.axaml
new file mode 100644
index 000000000..d5c205667
--- /dev/null
+++ b/src/Ryujinx/UI/Windows/ControllerOverlayWindow.axaml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Ryujinx/UI/Windows/ControllerOverlayWindow.axaml.cs b/src/Ryujinx/UI/Windows/ControllerOverlayWindow.axaml.cs
new file mode 100644
index 000000000..e70b8239e
--- /dev/null
+++ b/src/Ryujinx/UI/Windows/ControllerOverlayWindow.axaml.cs
@@ -0,0 +1,166 @@
+using Avalonia.Controls;
+using Avalonia.Media;
+using Avalonia.Threading;
+using Ryujinx.Common.Configuration.Hid;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Ryujinx.Ava.UI.Windows
+{
+ public partial class ControllerOverlayWindow : StyleableWindow
+ {
+ private const int AutoHideDelayMs = 4000; // 4 seconds
+
+ public ControllerOverlayWindow()
+ {
+ InitializeComponent();
+
+ TransparencyLevelHint = [WindowTransparencyLevel.Transparent];
+ SystemDecorations = SystemDecorations.None;
+ ExtendClientAreaTitleBarHeightHint = 0;
+ Background = Brushes.Transparent;
+ CanResize = false;
+ ShowInTaskbar = false;
+ }
+
+ public ControllerOverlayWindow(Window owner) : this()
+ {
+ if (owner != null)
+ {
+ // Position the overlay in the top-right corner of the owner window
+ WindowStartupLocation = WindowStartupLocation.Manual;
+
+ // Set position after the window is loaded
+ Loaded += (s, e) =>
+ {
+ if (owner.WindowState != WindowState.Minimized)
+ {
+ Position = new Avalonia.PixelPoint(
+ (int)(owner.Position.X + owner.Width - Width - 20),
+ (int)(owner.Position.Y + 50)
+ );
+ }
+ };
+ }
+ }
+
+ public void ShowControllerBindings(List inputConfigs)
+ {
+ // Clear existing bindings
+ PlayerBindings.Children.Clear();
+
+ // Group controllers by player index
+ var playerBindings = new Dictionary>();
+
+ foreach (var config in inputConfigs.Where(c => c.PlayerIndex <= PlayerIndex.Player4))
+ {
+ if (!playerBindings.ContainsKey(config.PlayerIndex))
+ {
+ playerBindings[config.PlayerIndex] = new List();
+ }
+ playerBindings[config.PlayerIndex].Add(config);
+ }
+
+ // Add player bindings to UI
+ for (int i = 0; i < 4; i++)
+ {
+ var playerIndex = (PlayerIndex)i;
+ var playerPanel = new StackPanel { Orientation = Avalonia.Layout.Orientation.Horizontal, Spacing = 12 };
+
+ // Player number with colored background
+ var playerNumberBorder = new Border
+ {
+ Background = GetPlayerColor(i),
+ CornerRadius = new Avalonia.CornerRadius(12),
+ Padding = new Avalonia.Thickness(8, 4),
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center
+ };
+
+ var playerLabel = new TextBlock
+ {
+ Text = $"P{i + 1}",
+ FontWeight = FontWeight.Bold,
+ Foreground = Brushes.White,
+ FontSize = 12,
+ HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Center
+ };
+ playerNumberBorder.Child = playerLabel;
+ playerPanel.Children.Add(playerNumberBorder);
+
+ if (playerBindings.ContainsKey(playerIndex))
+ {
+ var controllers = playerBindings[playerIndex];
+ var controllerNames = controllers.Select(c => GetControllerDisplayName(c)).ToList();
+
+ var controllerText = new TextBlock
+ {
+ Text = string.Join(", ", controllerNames),
+ Foreground = Brushes.LightGreen,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ FontWeight = FontWeight.SemiBold
+ };
+ playerPanel.Children.Add(controllerText);
+ }
+ else
+ {
+ var noControllerText = new TextBlock
+ {
+ Text = "No controller assigned",
+ Foreground = Brushes.Gray,
+ VerticalAlignment = Avalonia.Layout.VerticalAlignment.Center,
+ FontStyle = FontStyle.Italic
+ };
+ playerPanel.Children.Add(noControllerText);
+ }
+
+ PlayerBindings.Children.Add(playerPanel);
+ }
+
+ // Auto-hide after delay
+ _ = Task.Run(async () =>
+ {
+ await Task.Delay(AutoHideDelayMs);
+ await Dispatcher.UIThread.InvokeAsync(() =>
+ {
+ Close();
+ });
+ });
+ }
+
+ private static IBrush GetPlayerColor(int playerIndex)
+ {
+ return playerIndex switch
+ {
+ 0 => new SolidColorBrush(Color.FromRgb(255, 92, 92)), // Red for Player 1
+ 1 => new SolidColorBrush(Color.FromRgb(54, 162, 235)), // Blue for Player 2
+ 2 => new SolidColorBrush(Color.FromRgb(255, 206, 84)), // Yellow for Player 3
+ 3 => new SolidColorBrush(Color.FromRgb(75, 192, 192)), // Green for Player 4
+ _ => new SolidColorBrush(Color.FromRgb(128, 128, 128)) // Gray fallback
+ };
+ }
+
+ private static string GetControllerDisplayName(InputConfig config)
+ {
+ if (string.IsNullOrEmpty(config.Name))
+ {
+ return config.Backend switch
+ {
+ InputBackendType.WindowKeyboard => "Keyboard",
+ InputBackendType.GamepadSDL2 => "Controller",
+ _ => "Unknown"
+ };
+ }
+
+ // Truncate long controller names
+ string name = config.Name;
+ if (name.Length > 25)
+ {
+ name = name.Substring(0, 22) + "...";
+ }
+
+ return name;
+ }
+ }
+}