Avalonia UI - Part 1 (#3270)
* avalonia part 1 * remove vulkan ui backend * move ui common files to ui common project * get name for oading screen from device * rebase. * review 1 * review 1.1 * review * cleanup * addressed review * use cancellation token * review * review * rebased * cancel library loading when closing window * remove star image, use fonticon instead * delete render control frame buffer when game ends. change position of fav star * addressed @Thog review * ensure the right ui is downloaded in updates * fix crash when showing not supported dialog during controller request * add prefix to artifact names * Auto-format Avalonia project * Fix input * Fix build, simplify app disposal * remove nv stutter thread * addressed review * add missing change * maintain window size if new size is zero length * add game, handheld, docked to local * reverse scale main window * Update de_DE.json * Update de_DE.json * Update de_DE.json * Update italian json * Update it_IT.json * let render timer poll with no wait * remove unused code * more unused code * enabled tiered compilation and trimming * check if window event is not closed before signaling * fix atmospher case * locale fix * locale fix * remove explicit tiered compilation declarations * Remove ) it_IT.json * Remove ) de_DE.json * Update it_IT.json * Update pt_BR locale with latest strings * Remove ')' * add more strings to locale * update locale * remove extra slash * remove extra slash * set firmware version to 0 if key's not found * fix * revert timer changes * lock on object instead * Update it_IT.json * remove unused method * add load screen text to locale * drop swap event * Update de_DE.json * Update de_DE.json * do null check when stopping emulator * Update de_DE.json * Create tr_TR.json * Add tr_TR * Add tr_TR + Turkish * Update it_IT.json * Update Ryujinx.Ava/Input/AvaloniaMappingHelper.cs Co-authored-by: Ac_K <Acoustik666@gmail.com> * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * Apply suggestions from code review Co-authored-by: Ac_K <Acoustik666@gmail.com> * addressed review * Update Ryujinx.Ava/Ui/Backend/OpenGl/OpenGlRenderTarget.cs Co-authored-by: gdkchan <gab.dark.100@gmail.com> * use avalonia's inbuilt renderer on linux * removed whitespace * workaround for queue render crash with vsync off * drop custom backend * format files * fix not closing issue * remove warnings * rebase * update avalonia library * Reposition the Text and Button on About Page * Assign build version * Remove appveyor text Co-authored-by: gdk <gab.dark.100@gmail.com> Co-authored-by: Niwu34 <67392333+Niwu34@users.noreply.github.com> Co-authored-by: Antonio Brugnolo <36473846+AntoSkate@users.noreply.github.com> Co-authored-by: aegiff <99728970+aegiff@users.noreply.github.com> Co-authored-by: Ac_K <Acoustik666@gmail.com> Co-authored-by: MostlyWhat <78652091+MostlyWhat@users.noreply.github.com>
This commit is contained in:
parent
9ba73ffbe5
commit
deb99d2cae
161 changed files with 17179 additions and 855 deletions
205
Ryujinx.Ava/Input/AvaloniaKeyboard.cs
Normal file
205
Ryujinx.Ava/Input/AvaloniaKeyboard.cs
Normal file
|
@ -0,0 +1,205 @@
|
|||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Common.Configuration.Hid.Keyboard;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
|
||||
using ConfigKey = Ryujinx.Common.Configuration.Hid.Key;
|
||||
using Key = Ryujinx.Input.Key;
|
||||
|
||||
namespace Ryujinx.Ava.Input
|
||||
{
|
||||
public class AvaloniaKeyboard : IKeyboard
|
||||
{
|
||||
private readonly List<ButtonMappingEntry> _buttonsUserMapping;
|
||||
private readonly AvaloniaKeyboardDriver _driver;
|
||||
|
||||
private readonly object _userMappingLock = new();
|
||||
|
||||
private StandardKeyboardInputConfig _configuration;
|
||||
|
||||
private bool HasConfiguration => _configuration != null;
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
|
||||
public bool IsConnected => true;
|
||||
|
||||
public GamepadFeaturesFlag Features => GamepadFeaturesFlag.None;
|
||||
|
||||
public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name)
|
||||
{
|
||||
_driver = driver;
|
||||
Id = id;
|
||||
Name = name;
|
||||
_buttonsUserMapping = new List<ButtonMappingEntry>();
|
||||
}
|
||||
|
||||
public void Dispose() { }
|
||||
|
||||
public KeyboardStateSnapshot GetKeyboardStateSnapshot()
|
||||
{
|
||||
return IKeyboard.GetStateSnapshot(this);
|
||||
}
|
||||
|
||||
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 the 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)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _driver.IsPressed(key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetConfiguration(InputConfig configuration)
|
||||
{
|
||||
lock (_userMappingLock)
|
||||
{
|
||||
_configuration = (StandardKeyboardInputConfig)configuration;
|
||||
|
||||
_buttonsUserMapping.Clear();
|
||||
|
||||
// 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) { }
|
||||
|
||||
public void Rumble(float lowFrequency, float highFrequency, uint durationMs) { }
|
||||
|
||||
public Vector3 GetMotionData(MotionInputId inputId) => Vector3.Zero;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
Vector2 stick = new(stickX, stickY);
|
||||
|
||||
stick = Vector2.Normalize(stick);
|
||||
|
||||
return ((short)(stick.X * short.MaxValue), (short)(stick.Y * short.MaxValue));
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_driver?.ResetKeys();
|
||||
}
|
||||
|
||||
private class ButtonMappingEntry
|
||||
{
|
||||
public readonly Key From;
|
||||
public readonly GamepadButtonInputId To;
|
||||
|
||||
public ButtonMappingEntry(GamepadButtonInputId to, Key from)
|
||||
{
|
||||
To = to;
|
||||
From = from;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
113
Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs
Normal file
113
Ryujinx.Ava/Input/AvaloniaKeyboardDriver.cs
Normal file
|
@ -0,0 +1,113 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Ryujinx.Ava.Common.Locale;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using AvaKey = Avalonia.Input.Key;
|
||||
using Key = Ryujinx.Input.Key;
|
||||
using TextInputEventArgs = OpenTK.Windowing.Common.TextInputEventArgs;
|
||||
|
||||
namespace Ryujinx.Ava.Input
|
||||
{
|
||||
public class AvaloniaKeyboardDriver : IGamepadDriver
|
||||
{
|
||||
private static readonly string[] _keyboardIdentifers = new string[1] { "0" };
|
||||
private readonly Control _control;
|
||||
private readonly HashSet<AvaKey> _pressedKeys;
|
||||
|
||||
public event EventHandler<KeyEventArgs> KeyPressed;
|
||||
public event EventHandler<KeyEventArgs> KeyRelease;
|
||||
public event EventHandler<TextInputEventArgs> TextInput;
|
||||
|
||||
public string DriverName => "Avalonia";
|
||||
|
||||
public ReadOnlySpan<string> GamepadsIds => _keyboardIdentifers;
|
||||
|
||||
public AvaloniaKeyboardDriver(Control control)
|
||||
{
|
||||
_control = control;
|
||||
_pressedKeys = new HashSet<AvaKey>();
|
||||
|
||||
_control.KeyDown += OnKeyPress;
|
||||
_control.KeyUp += OnKeyRelease;
|
||||
_control.TextInput += Control_TextInput;
|
||||
}
|
||||
|
||||
private void Control_TextInput(object sender, Avalonia.Input.TextInputEventArgs e)
|
||||
{
|
||||
TextInput?.Invoke(this, new TextInputEventArgs(e.Text.First()));
|
||||
}
|
||||
|
||||
public event Action<string> OnGamepadConnected
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public event Action<string> OnGamepadDisconnected
|
||||
{
|
||||
add { }
|
||||
remove { }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
}
|
||||
|
||||
public IGamepad GetGamepad(string id)
|
||||
{
|
||||
if (!_keyboardIdentifers[0].Equals(id))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new AvaloniaKeyboard(this, _keyboardIdentifers[0], LocaleManager.Instance["AllKeyboards"]);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_control.KeyUp -= OnKeyPress;
|
||||
_control.KeyDown -= OnKeyRelease;
|
||||
}
|
||||
}
|
||||
|
||||
protected void OnKeyPress(object sender, KeyEventArgs args)
|
||||
{
|
||||
AvaKey key = args.Key;
|
||||
|
||||
_pressedKeys.Add(args.Key);
|
||||
|
||||
KeyPressed?.Invoke(this, args);
|
||||
}
|
||||
|
||||
protected void OnKeyRelease(object sender, KeyEventArgs args)
|
||||
{
|
||||
_pressedKeys.Remove(args.Key);
|
||||
|
||||
KeyRelease?.Invoke(this, args);
|
||||
}
|
||||
|
||||
internal bool IsPressed(Key key)
|
||||
{
|
||||
if (key == Key.Unbound || key == Key.Unknown)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AvaloniaMappingHelper.TryGetAvaKey(key, out var nativeKey);
|
||||
|
||||
return _pressedKeys.Contains(nativeKey);
|
||||
}
|
||||
|
||||
public void ResetKeys()
|
||||
{
|
||||
_pressedKeys.Clear();
|
||||
}
|
||||
}
|
||||
}
|
188
Ryujinx.Ava/Input/AvaloniaMappingHelper.cs
Normal file
188
Ryujinx.Ava/Input/AvaloniaMappingHelper.cs
Normal file
|
@ -0,0 +1,188 @@
|
|||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using AvaKey = Avalonia.Input.Key;
|
||||
|
||||
namespace Ryujinx.Ava.Input
|
||||
{
|
||||
public static class AvaloniaMappingHelper
|
||||
{
|
||||
private static readonly AvaKey[] _keyMapping = new AvaKey[(int)Key.Count]
|
||||
{
|
||||
// NOTE: Invalid
|
||||
AvaKey.None,
|
||||
|
||||
AvaKey.LeftShift,
|
||||
AvaKey.RightShift,
|
||||
AvaKey.LeftCtrl,
|
||||
AvaKey.RightCtrl,
|
||||
AvaKey.LeftAlt,
|
||||
AvaKey.RightAlt,
|
||||
AvaKey.LWin,
|
||||
AvaKey.RWin,
|
||||
AvaKey.Apps,
|
||||
AvaKey.F1,
|
||||
AvaKey.F2,
|
||||
AvaKey.F3,
|
||||
AvaKey.F4,
|
||||
AvaKey.F5,
|
||||
AvaKey.F6,
|
||||
AvaKey.F7,
|
||||
AvaKey.F8,
|
||||
AvaKey.F9,
|
||||
AvaKey.F10,
|
||||
AvaKey.F11,
|
||||
AvaKey.F12,
|
||||
AvaKey.F13,
|
||||
AvaKey.F14,
|
||||
AvaKey.F15,
|
||||
AvaKey.F16,
|
||||
AvaKey.F17,
|
||||
AvaKey.F18,
|
||||
AvaKey.F19,
|
||||
AvaKey.F20,
|
||||
AvaKey.F21,
|
||||
AvaKey.F22,
|
||||
AvaKey.F23,
|
||||
AvaKey.F24,
|
||||
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
AvaKey.None,
|
||||
|
||||
AvaKey.Up,
|
||||
AvaKey.Down,
|
||||
AvaKey.Left,
|
||||
AvaKey.Right,
|
||||
AvaKey.Return,
|
||||
AvaKey.Escape,
|
||||
AvaKey.Space,
|
||||
AvaKey.Tab,
|
||||
AvaKey.Back,
|
||||
AvaKey.Insert,
|
||||
AvaKey.Delete,
|
||||
AvaKey.PageUp,
|
||||
AvaKey.PageDown,
|
||||
AvaKey.Home,
|
||||
AvaKey.End,
|
||||
AvaKey.CapsLock,
|
||||
AvaKey.Scroll,
|
||||
AvaKey.Print,
|
||||
AvaKey.Pause,
|
||||
AvaKey.NumLock,
|
||||
AvaKey.Clear,
|
||||
AvaKey.NumPad0,
|
||||
AvaKey.NumPad1,
|
||||
AvaKey.NumPad2,
|
||||
AvaKey.NumPad3,
|
||||
AvaKey.NumPad4,
|
||||
AvaKey.NumPad5,
|
||||
AvaKey.NumPad6,
|
||||
AvaKey.NumPad7,
|
||||
AvaKey.NumPad8,
|
||||
AvaKey.NumPad9,
|
||||
AvaKey.Divide,
|
||||
AvaKey.Multiply,
|
||||
AvaKey.Subtract,
|
||||
AvaKey.Add,
|
||||
AvaKey.Decimal,
|
||||
AvaKey.Enter,
|
||||
AvaKey.A,
|
||||
AvaKey.B,
|
||||
AvaKey.C,
|
||||
AvaKey.D,
|
||||
AvaKey.E,
|
||||
AvaKey.F,
|
||||
AvaKey.G,
|
||||
AvaKey.H,
|
||||
AvaKey.I,
|
||||
AvaKey.J,
|
||||
AvaKey.K,
|
||||
AvaKey.L,
|
||||
AvaKey.M,
|
||||
AvaKey.N,
|
||||
AvaKey.O,
|
||||
AvaKey.P,
|
||||
AvaKey.Q,
|
||||
AvaKey.R,
|
||||
AvaKey.S,
|
||||
AvaKey.T,
|
||||
AvaKey.U,
|
||||
AvaKey.V,
|
||||
AvaKey.W,
|
||||
AvaKey.X,
|
||||
AvaKey.Y,
|
||||
AvaKey.Z,
|
||||
AvaKey.D0,
|
||||
AvaKey.D1,
|
||||
AvaKey.D2,
|
||||
AvaKey.D3,
|
||||
AvaKey.D4,
|
||||
AvaKey.D5,
|
||||
AvaKey.D6,
|
||||
AvaKey.D7,
|
||||
AvaKey.D8,
|
||||
AvaKey.D9,
|
||||
AvaKey.OemTilde,
|
||||
AvaKey.OemTilde,AvaKey.OemMinus,
|
||||
AvaKey.OemPlus,
|
||||
AvaKey.OemOpenBrackets,
|
||||
AvaKey.OemCloseBrackets,
|
||||
AvaKey.OemSemicolon,
|
||||
AvaKey.OemQuotes,
|
||||
AvaKey.OemComma,
|
||||
AvaKey.OemPeriod,
|
||||
AvaKey.OemQuestion,
|
||||
AvaKey.OemBackslash,
|
||||
|
||||
// NOTE: invalid
|
||||
AvaKey.None
|
||||
};
|
||||
|
||||
private static readonly Dictionary<AvaKey, Key> _avaKeyMapping;
|
||||
|
||||
static AvaloniaMappingHelper()
|
||||
{
|
||||
var inputKeys = Enum.GetValues(typeof(Key));
|
||||
|
||||
// Avalonia.Input.Key is not contiguous and quite large, so use a dictionary instead of an array.
|
||||
_avaKeyMapping = new Dictionary<AvaKey, Key>();
|
||||
|
||||
foreach (var key in inputKeys)
|
||||
{
|
||||
if (TryGetAvaKey((Key)key, out var index))
|
||||
{
|
||||
_avaKeyMapping[index] = (Key)key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetAvaKey(Key key, out AvaKey avaKey)
|
||||
{
|
||||
var keyExist = (int)key < _keyMapping.Length;
|
||||
if (keyExist)
|
||||
{
|
||||
avaKey = _keyMapping[(int)key];
|
||||
}
|
||||
else
|
||||
{
|
||||
avaKey = AvaKey.None;
|
||||
}
|
||||
|
||||
return keyExist;
|
||||
}
|
||||
|
||||
public static Key ToInputKey(AvaKey key)
|
||||
{
|
||||
return _avaKeyMapping.GetValueOrDefault(key, Key.Unknown);
|
||||
}
|
||||
}
|
||||
}
|
90
Ryujinx.Ava/Input/AvaloniaMouse.cs
Normal file
90
Ryujinx.Ava/Input/AvaloniaMouse.cs
Normal file
|
@ -0,0 +1,90 @@
|
|||
using Ryujinx.Common.Configuration.Hid;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace Ryujinx.Ava.Input
|
||||
{
|
||||
public class AvaloniaMouse : IMouse
|
||||
{
|
||||
private AvaloniaMouseDriver _driver;
|
||||
|
||||
public GamepadFeaturesFlag Features => throw new NotImplementedException();
|
||||
|
||||
public string Id => "0";
|
||||
|
||||
public string Name => "AvaloniaMouse";
|
||||
|
||||
public bool IsConnected => true;
|
||||
|
||||
public bool[] Buttons => _driver.PressedButtons;
|
||||
|
||||
public AvaloniaMouse(AvaloniaMouseDriver 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()
|
||||
{
|
||||
_driver = null;
|
||||
}
|
||||
}
|
||||
}
|
129
Ryujinx.Ava/Input/AvaloniaMouseDriver.cs
Normal file
129
Ryujinx.Ava/Input/AvaloniaMouseDriver.cs
Normal file
|
@ -0,0 +1,129 @@
|
|||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Threading;
|
||||
using Ryujinx.Input;
|
||||
using System;
|
||||
using System.Numerics;
|
||||
using MouseButton = Ryujinx.Input.MouseButton;
|
||||
using Size = System.Drawing.Size;
|
||||
|
||||
namespace Ryujinx.Ava.Input
|
||||
{
|
||||
public class AvaloniaMouseDriver : IGamepadDriver
|
||||
{
|
||||
private Control _widget;
|
||||
private bool _isDisposed;
|
||||
|
||||
public bool[] PressedButtons { get; }
|
||||
|
||||
public Vector2 CurrentPosition { get; private set; }
|
||||
public Vector2 Scroll { get; private set; }
|
||||
|
||||
public AvaloniaMouseDriver(Control parent)
|
||||
{
|
||||
_widget = parent;
|
||||
|
||||
_widget.PointerMoved += Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed += Parent_PointerPressEvent;
|
||||
_widget.PointerReleased += Parent_PointerReleaseEvent;
|
||||
_widget.PointerWheelChanged += Parent_ScrollEvent;
|
||||
|
||||
PressedButtons = new bool[(int)MouseButton.Count];
|
||||
}
|
||||
|
||||
private void Parent_ScrollEvent(object o, PointerWheelEventArgs args)
|
||||
{
|
||||
Scroll = new Vector2((float)args.Delta.X, (float)args.Delta.Y);
|
||||
}
|
||||
|
||||
private void Parent_PointerReleaseEvent(object o, PointerReleasedEventArgs args)
|
||||
{
|
||||
var pointerProperties = args.GetCurrentPoint(_widget).Properties;
|
||||
PressedButtons[(int)args.InitialPressMouseButton - 1] = false;
|
||||
}
|
||||
|
||||
private void Parent_PointerPressEvent(object o, PointerPressedEventArgs args)
|
||||
{
|
||||
var pointerProperties = args.GetCurrentPoint(_widget).Properties;
|
||||
|
||||
PressedButtons[(int)pointerProperties.PointerUpdateKind] = true;
|
||||
}
|
||||
|
||||
private void Parent_PointerMovedEvent(object o, PointerEventArgs args)
|
||||
{
|
||||
var position = args.GetPosition(_widget);
|
||||
|
||||
CurrentPosition = new Vector2((float)position.X, (float)position.Y);
|
||||
}
|
||||
|
||||
public void SetMousePressed(MouseButton button)
|
||||
{
|
||||
PressedButtons[(int)button] = true;
|
||||
}
|
||||
|
||||
public void SetMouseReleased(MouseButton button)
|
||||
{
|
||||
PressedButtons[(int)button] = false;
|
||||
}
|
||||
|
||||
public void SetPosition(double x, double y)
|
||||
{
|
||||
CurrentPosition = new Vector2((float)x, (float)y);
|
||||
}
|
||||
|
||||
public bool IsButtonPressed(MouseButton button)
|
||||
{
|
||||
return PressedButtons[(int)button];
|
||||
}
|
||||
|
||||
public Size GetClientSize()
|
||||
{
|
||||
Size size = new();
|
||||
|
||||
Dispatcher.UIThread.InvokeAsync(() =>
|
||||
{
|
||||
size = new Size((int)_widget.Bounds.Width, (int)_widget.Bounds.Height);
|
||||
}).Wait();
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public string DriverName => "Avalonia";
|
||||
|
||||
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 AvaloniaMouse(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_isDisposed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isDisposed = true;
|
||||
|
||||
_widget.PointerMoved -= Parent_PointerMovedEvent;
|
||||
_widget.PointerPressed -= Parent_PointerPressEvent;
|
||||
_widget.PointerReleased -= Parent_PointerReleaseEvent;
|
||||
_widget.PointerWheelChanged -= Parent_ScrollEvent;
|
||||
|
||||
_widget = null;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue