aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.Headless.SDL2/SDL2MouseDriver.cs
blob: 8983091f57c2b9bad7464bbb9653e29df9ad5f73 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Input;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Numerics;
using System.Runtime.CompilerServices;
using static SDL2.SDL;

namespace Ryujinx.Headless.SDL2
{
    class SDL2MouseDriver : IGamepadDriver
    {
        private const int CursorHideIdleTime = 5; // seconds

        private bool _isDisposed;
        private readonly HideCursorMode _hideCursorMode;
        private bool _isHidden;
        private long _lastCursorMoveTime;

        public bool[] PressedButtons { get; }

        public Vector2 CurrentPosition { get; private set; }
        public Vector2 Scroll { get; private set; }
        public Size ClientSize;

        public SDL2MouseDriver(HideCursorMode hideCursorMode)
        {
            PressedButtons = new bool[(int)MouseButton.Count];
            _hideCursorMode = hideCursorMode;

            if (_hideCursorMode == HideCursorMode.Always)
            {
                if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE)
                {
                    Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor.");
                }

                _isHidden = true;
            }
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private static MouseButton DriverButtonToMouseButton(uint rawButton)
        {
            Debug.Assert(rawButton > 0 && rawButton <= (int)MouseButton.Count);

            return (MouseButton)(rawButton - 1);
        }

        public void UpdatePosition()
        {
            _ = SDL_GetMouseState(out int posX, out int posY);
            Vector2 position = new(posX, posY);

            if (CurrentPosition != position)
            {
                CurrentPosition = position;
                _lastCursorMoveTime = Stopwatch.GetTimestamp();
            }

            CheckIdle();
        }

        private void CheckIdle()
        {
            if (_hideCursorMode != HideCursorMode.OnIdle)
            {
                return;
            }

            long cursorMoveDelta = Stopwatch.GetTimestamp() - _lastCursorMoveTime;

            if (cursorMoveDelta >= CursorHideIdleTime * Stopwatch.Frequency)
            {
                if (!_isHidden)
                {
                    if (SDL_ShowCursor(SDL_DISABLE) != SDL_DISABLE)
                    {
                        Logger.Error?.PrintMsg(LogClass.Application, "Failed to disable the cursor.");
                    }

                    _isHidden = true;
                }
            }
            else
            {
                if (_isHidden)
                {
                    if (SDL_ShowCursor(SDL_ENABLE) != SDL_ENABLE)
                    {
                        Logger.Error?.PrintMsg(LogClass.Application, "Failed to enable the cursor.");
                    }

                    _isHidden = false;
                }
            }
        }

        public void Update(SDL_Event evnt)
        {
            switch (evnt.type)
            {
                case SDL_EventType.SDL_MOUSEBUTTONDOWN:
                case SDL_EventType.SDL_MOUSEBUTTONUP:
                    uint rawButton = evnt.button.button;

                    if (rawButton > 0 && rawButton <= (int)MouseButton.Count)
                    {
                        PressedButtons[(int)DriverButtonToMouseButton(rawButton)] = evnt.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;

                        CurrentPosition = new Vector2(evnt.button.x, evnt.button.y);
                    }

                    break;

                // NOTE: On Linux using Wayland mouse motion events won't be received at all.
                case SDL_EventType.SDL_MOUSEMOTION:
                    CurrentPosition = new Vector2(evnt.motion.x, evnt.motion.y);
                    _lastCursorMoveTime = Stopwatch.GetTimestamp();

                    break;

                case SDL_EventType.SDL_MOUSEWHEEL:
                    Scroll = new Vector2(evnt.wheel.x, evnt.wheel.y);

                    break;
            }
        }

        public void SetClientSize(int width, int height)
        {
            ClientSize = new Size(width, height);
        }

        public bool IsButtonPressed(MouseButton button)
        {
            return PressedButtons[(int)button];
        }

        public Size GetClientSize()
        {
            return ClientSize;
        }

        public string DriverName => "SDL2";

        public event Action<string> OnGamepadConnected
        {
            add { }
            remove { }
        }

        public event Action<string> OnGamepadDisconnected
        {
            add { }
            remove { }
        }

        public ReadOnlySpan<string> GamepadsIds => new[] { "0" };

        public IGamepad GetGamepad(string id)
        {
            return new SDL2Mouse(this);
        }

        public void Dispose()
        {
            if (_isDisposed)
            {
                return;
            }

            _isDisposed = true;
        }
    }
}