mirror of
				https://github.com/ryujinx-mirror/ryujinx.git
				synced 2025-11-04 08:18:58 -06:00 
			
		
		
		
	Fix Cursor States On Windows (#6725)
* [Ava]: Fix Cursor States On Windows It's been sometime since the last PR #5415 was made and last time i was waiting for Ava 11 to be merged before re-writing the code and along the way forgot about the PR. Anyway this PR supersedes both #5288 and #5415, and fixes issue: #5136 * Now, the bounds for which the cursor should be detected in renderer should be accurate to any scaling / resolution, taking into account the status and the menu bar. ( This issue was partially resolved by #6450 ) * Reduced the number of times the cursor updates from per frame update to updating only when the cursor state needs to be changed. * Fixed the issue wherein you weren't able to resize the window, because of the cursor passthrough which caused the cursor to reset from the reset icon or flicker. * Fixed the issue caused by #6450 which caused the cursor to disappear over the submenus while cursor was set to always hide. * Changed the cursor state to not disappear while the game is being loaded. ( Needs Feedback ). * Removed an unused library import. * PR feedback * Fix excessive line breaks and whitespaces and other feedback * Add a check before calculating cursor idle time, such that it calculates only while the cursor mode is OnIdle. * PR Feedback * Rework the cursor state check code block Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com> * PR Feedback * A simpler version of the previous implementation. Co-Authored-By: gdkchan <5624669+gdkchan@users.noreply.github.com> * PR Feedback * PR Feedback --------- Co-authored-by: gdkchan <5624669+gdkchan@users.noreply.github.com>
This commit is contained in:
		@@ -94,6 +94,17 @@ namespace Ryujinx.Ava
 | 
			
		||||
 | 
			
		||||
        private long _lastCursorMoveTime;
 | 
			
		||||
        private bool _isCursorInRenderer = true;
 | 
			
		||||
        private bool _ignoreCursorState = false;
 | 
			
		||||
 | 
			
		||||
        private enum CursorStates
 | 
			
		||||
        {
 | 
			
		||||
            CursorIsHidden,
 | 
			
		||||
            CursorIsVisible,
 | 
			
		||||
            ForceChangeCursor
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        private CursorStates _cursorState = !ConfigurationState.Instance.Hid.EnableMouse.Value ?
 | 
			
		||||
            CursorStates.CursorIsVisible : CursorStates.CursorIsHidden;
 | 
			
		||||
 | 
			
		||||
        private bool _isStopped;
 | 
			
		||||
        private bool _isActive;
 | 
			
		||||
@@ -201,23 +212,65 @@ namespace Ryujinx.Ava
 | 
			
		||||
 | 
			
		||||
        private void TopLevel_PointerEnteredOrMoved(object sender, PointerEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            if (!_viewModel.IsActive)
 | 
			
		||||
            {
 | 
			
		||||
                _isCursorInRenderer = false;
 | 
			
		||||
                _ignoreCursorState = false;
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (sender is MainWindow window)
 | 
			
		||||
            {
 | 
			
		||||
                _lastCursorMoveTime = Stopwatch.GetTimestamp();
 | 
			
		||||
                if (ConfigurationState.Instance.HideCursor.Value == HideCursorMode.OnIdle)
 | 
			
		||||
                {
 | 
			
		||||
                    _lastCursorMoveTime = Stopwatch.GetTimestamp();
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                var point = e.GetCurrentPoint(window).Position;
 | 
			
		||||
                var bounds = RendererHost.EmbeddedWindow.Bounds;
 | 
			
		||||
                var windowYOffset = bounds.Y + window.MenuBarHeight;
 | 
			
		||||
                var windowYLimit = (int)window.Bounds.Height - window.StatusBarHeight - 1;
 | 
			
		||||
 | 
			
		||||
                if (!_viewModel.ShowMenuAndStatusBar)
 | 
			
		||||
                {
 | 
			
		||||
                    windowYOffset -= window.MenuBarHeight;
 | 
			
		||||
                    windowYLimit += window.StatusBarHeight + 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _isCursorInRenderer = point.X >= bounds.X &&
 | 
			
		||||
                                      point.X <= bounds.Width + bounds.X &&
 | 
			
		||||
                                      point.Y >= bounds.Y &&
 | 
			
		||||
                                      point.Y <= bounds.Height + bounds.Y;
 | 
			
		||||
                    Math.Ceiling(point.X) <= (int)window.Bounds.Width &&
 | 
			
		||||
                    point.Y >= windowYOffset &&
 | 
			
		||||
                    point.Y <= windowYLimit &&
 | 
			
		||||
                    !_viewModel.IsSubMenuOpen;
 | 
			
		||||
 | 
			
		||||
                _ignoreCursorState = false;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void TopLevel_PointerExited(object sender, PointerEventArgs e)
 | 
			
		||||
        {
 | 
			
		||||
            _isCursorInRenderer = false;
 | 
			
		||||
 | 
			
		||||
            if (sender is MainWindow window)
 | 
			
		||||
            {
 | 
			
		||||
                var point = e.GetCurrentPoint(window).Position;
 | 
			
		||||
                var bounds = RendererHost.EmbeddedWindow.Bounds;
 | 
			
		||||
                var windowYOffset = bounds.Y + window.MenuBarHeight;
 | 
			
		||||
                var windowYLimit = (int)window.Bounds.Height - window.StatusBarHeight - 1;
 | 
			
		||||
 | 
			
		||||
                if (!_viewModel.ShowMenuAndStatusBar)
 | 
			
		||||
                {
 | 
			
		||||
                    windowYOffset -= window.MenuBarHeight;
 | 
			
		||||
                    windowYLimit += window.StatusBarHeight + 1;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                _ignoreCursorState = (point.X == bounds.X ||
 | 
			
		||||
                    Math.Ceiling(point.X) == (int)window.Bounds.Width) &&
 | 
			
		||||
                    point.Y >= windowYOffset &&
 | 
			
		||||
                    point.Y <= windowYLimit;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _cursorState = CursorStates.ForceChangeCursor;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void UpdateScalingFilterLevel(object sender, ReactiveEventArgs<int> e)
 | 
			
		||||
@@ -245,9 +298,14 @@ namespace Ryujinx.Ava
 | 
			
		||||
 | 
			
		||||
                if (OperatingSystem.IsWindows())
 | 
			
		||||
                {
 | 
			
		||||
                    SetCursor(_defaultCursorWin);
 | 
			
		||||
                    if (_cursorState != CursorStates.CursorIsHidden && !_ignoreCursorState)
 | 
			
		||||
                    {
 | 
			
		||||
                        SetCursor(_defaultCursorWin);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            _cursorState = CursorStates.CursorIsVisible;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void HideCursor()
 | 
			
		||||
@@ -261,6 +319,8 @@ namespace Ryujinx.Ava
 | 
			
		||||
                    SetCursor(_invisibleCursorWin);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            _cursorState = CursorStates.CursorIsHidden;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void SetRendererWindowSize(Size size)
 | 
			
		||||
@@ -523,6 +583,8 @@ namespace Ryujinx.Ava
 | 
			
		||||
            {
 | 
			
		||||
                _lastCursorMoveTime = Stopwatch.GetTimestamp();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            _cursorState = CursorStates.ForceChangeCursor;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<bool> LoadGuestApplication()
 | 
			
		||||
@@ -1037,38 +1099,32 @@ namespace Ryujinx.Ava
 | 
			
		||||
 | 
			
		||||
            if (_viewModel.IsActive)
 | 
			
		||||
            {
 | 
			
		||||
                if (_isCursorInRenderer)
 | 
			
		||||
                bool isCursorVisible = true;
 | 
			
		||||
 | 
			
		||||
                if (_isCursorInRenderer && !_viewModel.ShowLoadProgress)
 | 
			
		||||
                {
 | 
			
		||||
                    if (ConfigurationState.Instance.Hid.EnableMouse)
 | 
			
		||||
                    if (ConfigurationState.Instance.Hid.EnableMouse.Value)
 | 
			
		||||
                    {
 | 
			
		||||
                        HideCursor();
 | 
			
		||||
                        isCursorVisible = ConfigurationState.Instance.HideCursor.Value == HideCursorMode.Never;
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        switch (ConfigurationState.Instance.HideCursor.Value)
 | 
			
		||||
                        {
 | 
			
		||||
                            case HideCursorMode.Never:
 | 
			
		||||
                                ShowCursor();
 | 
			
		||||
                                break;
 | 
			
		||||
                            case HideCursorMode.OnIdle:
 | 
			
		||||
                                if (Stopwatch.GetTimestamp() - _lastCursorMoveTime >= CursorHideIdleTime * Stopwatch.Frequency)
 | 
			
		||||
                                {
 | 
			
		||||
                                    HideCursor();
 | 
			
		||||
                                }
 | 
			
		||||
                                else
 | 
			
		||||
                                {
 | 
			
		||||
                                    ShowCursor();
 | 
			
		||||
                                }
 | 
			
		||||
                                break;
 | 
			
		||||
                            case HideCursorMode.Always:
 | 
			
		||||
                                HideCursor();
 | 
			
		||||
                                break;
 | 
			
		||||
                        }
 | 
			
		||||
                        isCursorVisible = ConfigurationState.Instance.HideCursor.Value == HideCursorMode.Never ||
 | 
			
		||||
                            (ConfigurationState.Instance.HideCursor.Value == HideCursorMode.OnIdle &&
 | 
			
		||||
                            Stopwatch.GetTimestamp() - _lastCursorMoveTime < CursorHideIdleTime * Stopwatch.Frequency);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
 | 
			
		||||
                if (_cursorState != (isCursorVisible ? CursorStates.CursorIsVisible : CursorStates.CursorIsHidden))
 | 
			
		||||
                {
 | 
			
		||||
                    ShowCursor();
 | 
			
		||||
                    if (isCursorVisible)
 | 
			
		||||
                    {
 | 
			
		||||
                        ShowCursor();
 | 
			
		||||
                    }
 | 
			
		||||
                    else
 | 
			
		||||
                    {
 | 
			
		||||
                        HideCursor();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                Dispatcher.UIThread.Post(() =>
 | 
			
		||||
@@ -1154,7 +1210,7 @@ namespace Ryujinx.Ava
 | 
			
		||||
            // Touchscreen.
 | 
			
		||||
            bool hasTouch = false;
 | 
			
		||||
 | 
			
		||||
            if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse)
 | 
			
		||||
            if (_viewModel.IsActive && !ConfigurationState.Instance.Hid.EnableMouse.Value)
 | 
			
		||||
            {
 | 
			
		||||
                hasTouch = TouchScreenManager.Update(true, (_inputManager.MouseDriver as AvaloniaMouseDriver).IsButtonPressed(MouseButton.Button1), ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
 | 
			
		||||
            }
 | 
			
		||||
 
 | 
			
		||||
@@ -111,8 +111,5 @@ namespace Ryujinx.Ava.UI.Helpers
 | 
			
		||||
 | 
			
		||||
        [LibraryImport("user32.dll", SetLastError = true)]
 | 
			
		||||
        public static partial IntPtr SetWindowLongPtrW(IntPtr hWnd, int nIndex, IntPtr value);
 | 
			
		||||
 | 
			
		||||
        [LibraryImport("user32.dll", SetLastError = true)]
 | 
			
		||||
        public static partial IntPtr SetWindowLongW(IntPtr hWnd, int nIndex, int value);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -157,7 +157,7 @@ namespace Ryujinx.Ava.UI.Renderer
 | 
			
		||||
                lpfnWndProc = Marshal.GetFunctionPointerForDelegate(_wndProcDelegate),
 | 
			
		||||
                style = ClassStyles.CsOwndc,
 | 
			
		||||
                lpszClassName = Marshal.StringToHGlobalUni(_className),
 | 
			
		||||
                hCursor = CreateArrowCursor(),
 | 
			
		||||
                hCursor = CreateArrowCursor()
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            RegisterClassEx(ref wndClassEx);
 | 
			
		||||
 
 | 
			
		||||
@@ -104,6 +104,7 @@ namespace Ryujinx.Ava.UI.ViewModels
 | 
			
		||||
        private double _windowHeight;
 | 
			
		||||
 | 
			
		||||
        private bool _isActive;
 | 
			
		||||
        private bool _isSubMenuOpen;
 | 
			
		||||
 | 
			
		||||
        public ApplicationData ListSelectedApplication;
 | 
			
		||||
        public ApplicationData GridSelectedApplication;
 | 
			
		||||
@@ -317,6 +318,17 @@ namespace Ryujinx.Ava.UI.ViewModels
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool IsSubMenuOpen
 | 
			
		||||
        {
 | 
			
		||||
            get => _isSubMenuOpen;
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                _isSubMenuOpen = value;
 | 
			
		||||
 | 
			
		||||
                OnPropertyChanged();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public bool ShowAll
 | 
			
		||||
        {
 | 
			
		||||
            get => _showAll;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
<UserControl
 | 
			
		||||
<UserControl
 | 
			
		||||
    xmlns="https://github.com/avaloniaui"
 | 
			
		||||
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
 | 
			
		||||
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
 | 
			
		||||
@@ -16,7 +16,8 @@
 | 
			
		||||
            Name="Menu"
 | 
			
		||||
            Height="35"
 | 
			
		||||
            Margin="0"
 | 
			
		||||
            HorizontalAlignment="Left">
 | 
			
		||||
            HorizontalAlignment="Left"
 | 
			
		||||
            IsOpen="{Binding IsSubMenuOpen, Mode=OneWayToSource}">
 | 
			
		||||
            <Menu.ItemsPanel>
 | 
			
		||||
                <ItemsPanelTemplate>
 | 
			
		||||
                    <DockPanel Margin="0" HorizontalAlignment="Stretch" />
 | 
			
		||||
 
 | 
			
		||||
@@ -56,6 +56,9 @@ namespace Ryujinx.Ava.UI.Windows
 | 
			
		||||
        public static bool ShowKeyErrorOnLoad { get; set; }
 | 
			
		||||
        public ApplicationLibrary ApplicationLibrary { get; set; }
 | 
			
		||||
 | 
			
		||||
        public readonly double StatusBarHeight;
 | 
			
		||||
        public readonly double MenuBarHeight;
 | 
			
		||||
 | 
			
		||||
        public MainWindow()
 | 
			
		||||
        {
 | 
			
		||||
            ViewModel = new MainWindowViewModel();
 | 
			
		||||
@@ -74,7 +77,9 @@ namespace Ryujinx.Ava.UI.Windows
 | 
			
		||||
            ViewModel.Title = $"Ryujinx {Program.Version}";
 | 
			
		||||
 | 
			
		||||
            // NOTE: Height of MenuBar and StatusBar is not usable here, since it would still be 0 at this point.
 | 
			
		||||
            double barHeight = MenuBar.MinHeight + StatusBarView.StatusBar.MinHeight;
 | 
			
		||||
            StatusBarHeight = StatusBarView.StatusBar.MinHeight;
 | 
			
		||||
            MenuBarHeight = MenuBar.MinHeight;
 | 
			
		||||
            double barHeight = MenuBarHeight + StatusBarHeight;
 | 
			
		||||
            Height = ((Height - barHeight) / Program.WindowScaleFactor) + barHeight;
 | 
			
		||||
            Width /= Program.WindowScaleFactor;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user