diff options
Diffstat (limited to 'src/Ryujinx.Gtk3/Input')
| -rw-r--r-- | src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs | 205 | ||||
| -rw-r--r-- | src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs | 94 | ||||
| -rw-r--r-- | src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs | 178 | ||||
| -rw-r--r-- | src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs | 90 | ||||
| -rw-r--r-- | src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs | 108 |
5 files changed, 675 insertions, 0 deletions
diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs new file mode 100644 index 00000000..ff7a2c3b --- /dev/null +++ b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Keyboard.cs @@ -0,0 +1,205 @@ +using Ryujinx.Common.Configuration.Hid; +using Ryujinx.Common.Configuration.Hid.Keyboard; +using System; +using System.Collections.Generic; +using System.Numerics; +using ConfigKey = Ryujinx.Common.Configuration.Hid.Key; + +namespace Ryujinx.Input.GTK3 +{ + public class GTK3Keyboard : IKeyboard + { + private class ButtonMappingEntry + { + public readonly GamepadButtonInputId To; + public readonly Key From; + + public ButtonMappingEntry(GamepadButtonInputId to, Key from) + { + To = to; + From = from; + } + } + + private readonly object _userMappingLock = new(); + + private readonly GTK3KeyboardDriver _driver; + private StandardKeyboardInputConfig _configuration; + private readonly List<ButtonMappingEntry> _buttonsUserMapping; + + public GTK3Keyboard(GTK3KeyboardDriver driver, string id, string name) + { + _driver = driver; + Id = id; + Name = name; + _buttonsUserMapping = new List<ButtonMappingEntry>(); + } + + private bool HasConfiguration => _configuration != null; + + public string Id { get; } + + public string Name { get; } + + public bool IsConnected => true; + + public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None; + + public void Dispose() + { + // No operations + GC.SuppressFinalize(this); + } + + public KeyboardStateSnapshot GetKeyboardStateSnapshot() + { + return IKeyboard.GetStateSnapshot(this); + } + + private static float ConvertRawStickValue(short value) + { + const float ConvertRate = 1.0f / (short.MaxValue + 0.5f); + + return value * ConvertRate; + } + + private static (short, short) GetStickValues(ref KeyboardStateSnapshot snapshot, JoyconConfigKeyboardStick<ConfigKey> stickConfig) + { + short stickX = 0; + short stickY = 0; + + if (snapshot.IsPressed((Key)stickConfig.StickUp)) + { + stickY += 1; + } + + if (snapshot.IsPressed((Key)stickConfig.StickDown)) + { + stickY -= 1; + } + + if (snapshot.IsPressed((Key)stickConfig.StickRight)) + { + stickX += 1; + } + + if (snapshot.IsPressed((Key)stickConfig.StickLeft)) + { + stickX -= 1; + } + + OpenTK.Mathematics.Vector2 stick = new(stickX, stickY); + + stick.NormalizeFast(); + + return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue)); + } + + public GamepadStateSnapshot GetMappedStateSnapshot() + { + KeyboardStateSnapshot rawState = GetKeyboardStateSnapshot(); + GamepadStateSnapshot result = default; + + lock (_userMappingLock) + { + if (!HasConfiguration) + { + return result; + } + + foreach (ButtonMappingEntry entry in _buttonsUserMapping) + { + if (entry.From == Key.Unknown || entry.From == Key.Unbound || entry.To == GamepadButtonInputId.Unbound) + { + continue; + } + + // Do not touch state of button already pressed + if (!result.IsPressed(entry.To)) + { + result.SetPressed(entry.To, rawState.IsPressed(entry.From)); + } + } + + (short leftStickX, short leftStickY) = GetStickValues(ref rawState, _configuration.LeftJoyconStick); + (short rightStickX, short rightStickY) = GetStickValues(ref rawState, _configuration.RightJoyconStick); + + result.SetStick(StickInputId.Left, ConvertRawStickValue(leftStickX), ConvertRawStickValue(leftStickY)); + result.SetStick(StickInputId.Right, ConvertRawStickValue(rightStickX), ConvertRawStickValue(rightStickY)); + } + + return result; + } + + public GamepadStateSnapshot GetStateSnapshot() + { + throw new NotSupportedException(); + } + + public (float, float) GetStick(StickInputId inputId) + { + throw new NotSupportedException(); + } + + public bool IsPressed(GamepadButtonInputId inputId) + { + throw new NotSupportedException(); + } + + public bool IsPressed(Key key) + { + return _driver.IsPressed(key); + } + + public void SetConfiguration(InputConfig configuration) + { + lock (_userMappingLock) + { + _configuration = (StandardKeyboardInputConfig)configuration; + + _buttonsUserMapping.Clear(); + + // Then left joycon + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftStick, (Key)_configuration.LeftJoyconStick.StickButton)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadUp, (Key)_configuration.LeftJoycon.DpadUp)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadDown, (Key)_configuration.LeftJoycon.DpadDown)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadLeft, (Key)_configuration.LeftJoycon.DpadLeft)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.DpadRight, (Key)_configuration.LeftJoycon.DpadRight)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Minus, (Key)_configuration.LeftJoycon.ButtonMinus)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftShoulder, (Key)_configuration.LeftJoycon.ButtonL)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); + + // Finally right joycon + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.A, (Key)_configuration.RightJoycon.ButtonA)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.B, (Key)_configuration.RightJoycon.ButtonB)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.X, (Key)_configuration.RightJoycon.ButtonX)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Y, (Key)_configuration.RightJoycon.ButtonY)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Plus, (Key)_configuration.RightJoycon.ButtonPlus)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightShoulder, (Key)_configuration.RightJoycon.ButtonR)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); + } + } + + public void SetTriggerThreshold(float triggerThreshold) + { + // No operations + } + + public void Rumble(float lowFrequency, float highFrequency, uint durationMs) + { + // No operations + } + + public Vector3 GetMotionData(MotionInputId inputId) + { + // No operations + + return Vector3.Zero; + } + } +} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs new file mode 100644 index 00000000..e502254b --- /dev/null +++ b/src/Ryujinx.Gtk3/Input/GTK3/GTK3KeyboardDriver.cs @@ -0,0 +1,94 @@ +using Gdk; +using Gtk; +using System; +using System.Collections.Generic; +using GtkKey = Gdk.Key; + +namespace Ryujinx.Input.GTK3 +{ + public class GTK3KeyboardDriver : IGamepadDriver + { + private readonly Widget _widget; + private readonly HashSet<GtkKey> _pressedKeys; + + public GTK3KeyboardDriver(Widget widget) + { + _widget = widget; + _pressedKeys = new HashSet<GtkKey>(); + + _widget.KeyPressEvent += OnKeyPress; + _widget.KeyReleaseEvent += OnKeyRelease; + } + + public string DriverName => "GTK3"; + + private static readonly string[] _keyboardIdentifers = new string[1] { "0" }; + + public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers; + + public event Action<string> OnGamepadConnected + { + add { } + remove { } + } + + public event Action<string> OnGamepadDisconnected + { + add { } + remove { } + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _widget.KeyPressEvent -= OnKeyPress; + _widget.KeyReleaseEvent -= OnKeyRelease; + } + } + + public void Dispose() + { + GC.SuppressFinalize(this); + Dispose(true); + } + + [GLib.ConnectBefore] + protected void OnKeyPress(object sender, KeyPressEventArgs args) + { + GtkKey key = (GtkKey)Keyval.ToLower((uint)args.Event.Key); + + _pressedKeys.Add(key); + } + + [GLib.ConnectBefore] + protected void OnKeyRelease(object sender, KeyReleaseEventArgs args) + { + GtkKey key = (GtkKey)Keyval.ToLower((uint)args.Event.Key); + + _pressedKeys.Remove(key); + } + + internal bool IsPressed(Key key) + { + if (key == Key.Unbound || key == Key.Unknown) + { + return false; + } + + GtkKey nativeKey = GTK3MappingHelper.ToGtkKey(key); + + return _pressedKeys.Contains(nativeKey); + } + + public IGamepad GetGamepad(string id) + { + if (!_keyboardIdentifers[0].Equals(id)) + { + return null; + } + + return new GTK3Keyboard(this, _keyboardIdentifers[0], "All keyboards"); + } + } +} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs new file mode 100644 index 00000000..422a9603 --- /dev/null +++ b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MappingHelper.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using GtkKey = Gdk.Key; + +namespace Ryujinx.Input.GTK3 +{ + public static class GTK3MappingHelper + { + private static readonly GtkKey[] _keyMapping = new GtkKey[(int)Key.Count] + { + // NOTE: invalid + GtkKey.blank, + + GtkKey.Shift_L, + GtkKey.Shift_R, + GtkKey.Control_L, + GtkKey.Control_R, + GtkKey.Alt_L, + GtkKey.Alt_R, + GtkKey.Super_L, + GtkKey.Super_R, + GtkKey.Menu, + GtkKey.F1, + GtkKey.F2, + GtkKey.F3, + GtkKey.F4, + GtkKey.F5, + GtkKey.F6, + GtkKey.F7, + GtkKey.F8, + GtkKey.F9, + GtkKey.F10, + GtkKey.F11, + GtkKey.F12, + GtkKey.F13, + GtkKey.F14, + GtkKey.F15, + GtkKey.F16, + GtkKey.F17, + GtkKey.F18, + GtkKey.F19, + GtkKey.F20, + GtkKey.F21, + GtkKey.F22, + GtkKey.F23, + GtkKey.F24, + GtkKey.F25, + GtkKey.F26, + GtkKey.F27, + GtkKey.F28, + GtkKey.F29, + GtkKey.F30, + GtkKey.F31, + GtkKey.F32, + GtkKey.F33, + GtkKey.F34, + GtkKey.F35, + GtkKey.Up, + GtkKey.Down, + GtkKey.Left, + GtkKey.Right, + GtkKey.Return, + GtkKey.Escape, + GtkKey.space, + GtkKey.Tab, + GtkKey.BackSpace, + GtkKey.Insert, + GtkKey.Delete, + GtkKey.Page_Up, + GtkKey.Page_Down, + GtkKey.Home, + GtkKey.End, + GtkKey.Caps_Lock, + GtkKey.Scroll_Lock, + GtkKey.Print, + GtkKey.Pause, + GtkKey.Num_Lock, + GtkKey.Clear, + GtkKey.KP_0, + GtkKey.KP_1, + GtkKey.KP_2, + GtkKey.KP_3, + GtkKey.KP_4, + GtkKey.KP_5, + GtkKey.KP_6, + GtkKey.KP_7, + GtkKey.KP_8, + GtkKey.KP_9, + GtkKey.KP_Divide, + GtkKey.KP_Multiply, + GtkKey.KP_Subtract, + GtkKey.KP_Add, + GtkKey.KP_Decimal, + GtkKey.KP_Enter, + GtkKey.a, + GtkKey.b, + GtkKey.c, + GtkKey.d, + GtkKey.e, + GtkKey.f, + GtkKey.g, + GtkKey.h, + GtkKey.i, + GtkKey.j, + GtkKey.k, + GtkKey.l, + GtkKey.m, + GtkKey.n, + GtkKey.o, + GtkKey.p, + GtkKey.q, + GtkKey.r, + GtkKey.s, + GtkKey.t, + GtkKey.u, + GtkKey.v, + GtkKey.w, + GtkKey.x, + GtkKey.y, + GtkKey.z, + GtkKey.Key_0, + GtkKey.Key_1, + GtkKey.Key_2, + GtkKey.Key_3, + GtkKey.Key_4, + GtkKey.Key_5, + GtkKey.Key_6, + GtkKey.Key_7, + GtkKey.Key_8, + GtkKey.Key_9, + GtkKey.grave, + GtkKey.grave, + GtkKey.minus, + GtkKey.plus, + GtkKey.bracketleft, + GtkKey.bracketright, + GtkKey.semicolon, + GtkKey.quoteright, + GtkKey.comma, + GtkKey.period, + GtkKey.slash, + GtkKey.backslash, + + // NOTE: invalid + GtkKey.blank, + }; + + private static readonly Dictionary<GtkKey, Key> _gtkKeyMapping; + + static GTK3MappingHelper() + { + var inputKeys = Enum.GetValues<Key>().SkipLast(1); + + // GtkKey is not contiguous and quite large, so use a dictionary instead of an array. + _gtkKeyMapping = new Dictionary<GtkKey, Key>(); + + foreach (var key in inputKeys) + { + var index = ToGtkKey(key); + _gtkKeyMapping[index] = key; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static GtkKey ToGtkKey(Key key) + { + return _keyMapping[(int)key]; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Key ToInputKey(GtkKey key) + { + return _gtkKeyMapping.GetValueOrDefault(key, Key.Unknown); + } + } +} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs new file mode 100644 index 00000000..0ab817ec --- /dev/null +++ b/src/Ryujinx.Gtk3/Input/GTK3/GTK3Mouse.cs @@ -0,0 +1,90 @@ +using Ryujinx.Common.Configuration.Hid; +using System; +using System.Drawing; +using System.Numerics; + +namespace Ryujinx.Input.GTK3 +{ + public class GTK3Mouse : IMouse + { + private GTK3MouseDriver _driver; + + public GamepadFeaturesFlag Features => throw new NotImplementedException(); + + public string Id => "0"; + + public string Name => "GTKMouse"; + + public bool IsConnected => true; + + public bool[] Buttons => _driver.PressedButtons; + + public GTK3Mouse(GTK3MouseDriver driver) + { + _driver = driver; + } + + public Size ClientSize => _driver.GetClientSize(); + + public Vector2 GetPosition() + { + return _driver.CurrentPosition; + } + + public Vector2 GetScroll() + { + return _driver.Scroll; + } + + public GamepadStateSnapshot GetMappedStateSnapshot() + { + throw new NotImplementedException(); + } + + public Vector3 GetMotionData(MotionInputId inputId) + { + throw new NotImplementedException(); + } + + public GamepadStateSnapshot GetStateSnapshot() + { + throw new NotImplementedException(); + } + + public (float, float) GetStick(StickInputId inputId) + { + throw new NotImplementedException(); + } + + public bool IsButtonPressed(MouseButton button) + { + return _driver.IsButtonPressed(button); + } + + public bool IsPressed(GamepadButtonInputId inputId) + { + throw new NotImplementedException(); + } + + public void Rumble(float lowFrequency, float highFrequency, uint durationMs) + { + throw new NotImplementedException(); + } + + public void SetConfiguration(InputConfig configuration) + { + throw new NotImplementedException(); + } + + public void SetTriggerThreshold(float triggerThreshold) + { + throw new NotImplementedException(); + } + + public void Dispose() + { + GC.SuppressFinalize(this); + _driver = null; + } + } +} diff --git a/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs new file mode 100644 index 00000000..5962bcb2 --- /dev/null +++ b/src/Ryujinx.Gtk3/Input/GTK3/GTK3MouseDriver.cs @@ -0,0 +1,108 @@ +using Gdk; +using Gtk; +using System; +using System.Numerics; +using Size = System.Drawing.Size; + +namespace Ryujinx.Input.GTK3 +{ + public class GTK3MouseDriver : IGamepadDriver + { + private Widget _widget; + private bool _isDisposed; + + public bool[] PressedButtons { get; } + + public Vector2 CurrentPosition { get; private set; } + public Vector2 Scroll { get; private set; } + + public GTK3MouseDriver(Widget parent) + { + _widget = parent; + + _widget.MotionNotifyEvent += Parent_MotionNotifyEvent; + _widget.ButtonPressEvent += Parent_ButtonPressEvent; + _widget.ButtonReleaseEvent += Parent_ButtonReleaseEvent; + _widget.ScrollEvent += Parent_ScrollEvent; + + PressedButtons = new bool[(int)MouseButton.Count]; + } + + + [GLib.ConnectBefore] + private void Parent_ScrollEvent(object o, ScrollEventArgs args) + { + Scroll = new Vector2((float)args.Event.X, (float)args.Event.Y); + } + + [GLib.ConnectBefore] + private void Parent_ButtonReleaseEvent(object o, ButtonReleaseEventArgs args) + { + PressedButtons[args.Event.Button - 1] = false; + } + + [GLib.ConnectBefore] + private void Parent_ButtonPressEvent(object o, ButtonPressEventArgs args) + { + PressedButtons[args.Event.Button - 1] = true; + } + + [GLib.ConnectBefore] + private void Parent_MotionNotifyEvent(object o, MotionNotifyEventArgs args) + { + if (args.Event.Device.InputSource == InputSource.Mouse) + { + CurrentPosition = new Vector2((float)args.Event.X, (float)args.Event.Y); + } + } + + public bool IsButtonPressed(MouseButton button) + { + return PressedButtons[(int)button]; + } + + public Size GetClientSize() + { + return new Size(_widget.AllocatedWidth, _widget.AllocatedHeight); + } + + public string DriverName => "GTK3"; + + 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 GTK3Mouse(this); + } + + public void Dispose() + { + if (_isDisposed) + { + return; + } + + GC.SuppressFinalize(this); + + _isDisposed = true; + + _widget.MotionNotifyEvent -= Parent_MotionNotifyEvent; + _widget.ButtonPressEvent -= Parent_ButtonPressEvent; + _widget.ButtonReleaseEvent -= Parent_ButtonReleaseEvent; + + _widget = null; + } + } +} |
